title: Android Oreo animation rendering issue
body: <p>Android app has a custom view(screen overlay) with animation </p>
  
  <p>animation rendering is good on devices below android 7.1.1  but the animation is not rending properly on android oreo 8.0 and 8.1 </p>
  
  <p><strong>Screen record of 7.1.1</strong> </p>
  
  <p><a href="https://i.stack.imgur.com/6FuSw.gif" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/6FuSw.gif" alt="enter image description here"></a></p>
  
  <p><strong>Screen record of 8.0</strong></p>
  
  <p><a href="https://i.stack.imgur.com/ur4Xh.gif" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/ur4Xh.gif" alt="enter image description here"></a></p>
  
  <p><strong>My Circle Layout code</strong></p>
  
  <pre><code>    public class CircleLayout extends ViewGroup {
      private final Context context;
  
      public enum FirstChildPosition {
          EAST(0), SOUTH(125), WEST(180), NORTH(270);
  
          private int angle;
  
          FirstChildPosition(int angle) {
              this.angle = angle;
          }
  
          public int getAngle() {
              return angle;
          }
  
      }
  
     // public MediaPlayer mp;
      // Event listeners
      private OnItemClickListener onItemClickListener = null;
      private OnItemSelectedListener onItemSelectedListener = null;
      private OnCenterClickListener onCenterClickListener = null;
      private OnRotationFinishedListener onRotationFinishedListener = null;
  
      // Sizes of the ViewGroup
      private int circleWidth, circleHeight;
      private float radius = -1;
  
      // Child sizes
      private int maxChildWidth = 0;
      private int maxChildHeight = 0;
  
      // Touch detection
      private GestureDetector gestureDetector;
      // Detecting inverse rotations
      private boolean[] quadrantTouched;
  
      // Settings of the ViewGroup
      private int speed = 25;
      private float angle = 90;
      private FirstChildPosition firstChildPosition = FirstChildPosition.SOUTH;
      private boolean isRotating = true;
  
      // Touch helpers
      private double touchStartAngle;
      private boolean didMove = false;
  
      // Tapped and selected child
      private View selectedView = null;
  
      // Rotation animator
      private ObjectAnimator animator;
  
      public CircleLayout(Context context) {
          this(context, null);
      }
  
      public CircleLayout(Context context, AttributeSet attrs) {
          this(context, attrs, 0);
      }
  
      public CircleLayout(Context context, AttributeSet attrs, int defStyle) {
          super(context, attrs, defStyle);
          init(attrs);
          this.context=context;
         //  mp= MediaPlayer.create(context, R.raw.rotation_sound);
      }
  
      /**
       * Initializes the ViewGroup and modifies it's default behavior by the
       * passed attributes
       *
       * @param attrs the attributes used to modify default settings
       */
      protected void init(AttributeSet attrs) {
          gestureDetector = new GestureDetector(getContext(),
                  new MyGestureListener());
          quadrantTouched = new boolean[]{false, false, false, false, false};
  
          if (attrs != null) {
              TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.CircleLayout);
  
              speed = a.getInt(R.styleable.CircleLayout_speed, speed);
              radius = a.getDimension(R.styleable.CircleLayout_radius, radius);
              isRotating = a.getBoolean(R.styleable.CircleLayout_isRotating, isRotating);
  
              // The angle where the first menu item will be drawn
              angle = a.getInt(R.styleable.CircleLayout_firstChildPosition, (int) angle);
              for (FirstChildPosition pos : FirstChildPosition.values()) {
                  if (pos.getAngle() == angle) {
                      firstChildPosition = pos;
                      break;
                  }
              }
  
              a.recycle();
  
              // Needed for the ViewGroup to be drawn
              setWillNotDraw(false);
          }
      }
  
      public float getAngle() {
          return angle;
      }
  
      public void setAngle(float angle) {
          this.angle = angle % 360;
          setChildAngles();
      }
  
      public int getSpeed() {
          return speed;
      }
  
      public void setSpeed(int speed) {
          if (speed &lt;= 0) {
              throw new InvalidParameterException("Speed must be a positive integer number");
          }
          this.speed = speed;
      }
  
      public float getRadius() {
          return radius;
      }
  
      public void setRadius(float radius) {
          if(radius &lt; 0){
              throw new InvalidParameterException("Radius cannot be negative");
          }
          this.radius = radius;
          setChildAngles();
      }
  
      public boolean isRotating() {
          return isRotating;
      }
  
      public void setRotating(boolean isRotating) {
          this.isRotating = isRotating;
      }
  
      public FirstChildPosition getFirstChildPosition() {
          return firstChildPosition;
      }
  
      public void setFirstChildPosition(FirstChildPosition firstChildPosition) {
          this.firstChildPosition = firstChildPosition;
          if (selectedView != null) {
              if (isRotating) {
                  rotateViewToCenter(selectedView);
              } else {
                  setAngle(firstChildPosition.getAngle());
              }
          }
      }
  
      /**
       * Returns the currently selected menu
       *
       * @return the view which is currently the closest to the first item
       * position
       */
      public View getSelectedItem() {
          if (selectedView == null) {
              selectedView = getChildAt(0);
          }
          return selectedView;
      }
  
      @Override
      public void removeView(View view) {
          super.removeView(view);
          updateAngle();
      }
  
      @Override
      public void removeViewAt(int index) {
          super.removeViewAt(index);
          updateAngle();
      }
  
      @Override
      public void removeViews(int start, int count) {
          super.removeViews(start, count);
          updateAngle();
      }
  
      @Override
      public void addView(View child, int index, LayoutParams params) {
          super.addView(child, index, params);
          updateAngle();
      }
  
      private void updateAngle() {
          // Update the position of the views, so we know which is the selected
          setChildAngles();
          rotateViewToCenter(selectedView);
      }
  
      @Override
      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
          // Measure child views first
          maxChildWidth = 0;
          maxChildHeight = 0;
  
          int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
                  MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.AT_MOST);
          int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
                  MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.AT_MOST);
  
          final int childCount = getChildCount();
          for (int i = 0; i &lt; childCount; i++) {
              final View child = getChildAt(i);
              if (child.getVisibility() == GONE) {
                  continue;
              }
  
              measureChild(child, childWidthMeasureSpec, childHeightMeasureSpec);
  
              maxChildWidth = Math.max(maxChildWidth, child.getMeasuredWidth());
              maxChildHeight = Math.max(maxChildHeight, child.getMeasuredHeight());
          }
  
          // Then decide what size we want to be
          int widthMode = MeasureSpec.getMode(widthMeasureSpec);
          int widthSize = MeasureSpec.getSize(widthMeasureSpec);
          int heightMode = MeasureSpec.getMode(heightMeasureSpec);
          int heightSize = MeasureSpec.getSize(heightMeasureSpec);
  
          int width;
          int height;
  
          //Measure Width
          if (widthMode == MeasureSpec.EXACTLY) {
              //Must be this size
              width = widthSize;
          } else if (widthMode == MeasureSpec.AT_MOST) {
              //Can't be bigger than...
              width = Math.min(widthSize, heightSize);
          } else {
              //Be whatever you want
              width = maxChildWidth * 3;
          }
  
          //Measure Height
          if (heightMode == MeasureSpec.EXACTLY) {
              //Must be this size
              height = heightSize;
          } else if (heightMode == MeasureSpec.AT_MOST) {
              //Can't be bigger than...
              height = Math.min(heightSize, widthSize);
          } else {
              //Be whatever you want
              height = maxChildHeight * 3;
          }
  
          setMeasuredDimension(resolveSize(width, widthMeasureSpec),
                  resolveSize(height, heightMeasureSpec));
      }
  
      @Override
      protected void onLayout(boolean changed, int l, int t, int r, int b) {
          int layoutWidth = r - l;
          int layoutHeight = b - t;
  
          if(radius &lt; 0) {
              radius = (layoutWidth &lt;= layoutHeight) ? layoutWidth / 3
                      : layoutHeight / 3;
          }
  
          circleHeight = getHeight();
          circleWidth = getWidth();
          setChildAngles();
      }
  
      /**
       * Rotates the given view to the firstChildPosition
       *
       * @param view the view to be rotated
       */
      public void rotateViewToCenter(View view) {
          if (isRotating) {
              float viewAngle = view.getTag() != null ? (Float) view.getTag() : 0;
              float destAngle = firstChildPosition.getAngle() - viewAngle;
  
              if (destAngle &lt; 0) {
                  destAngle += 360;
              }
  
              if (destAngle &gt; 180) {
                  destAngle = -1 * (360 - destAngle);
              }
  
              animateTo(angle + destAngle, 7500L / speed);
          }
      }
  
      private void rotateButtons(float degrees) {
          angle += degrees;
          setChildAngles();
      }
  
      private void setChildAngles() {
          int left, top, childWidth, childHeight, childCount = getChildCount();
          float angleDelay = 360.0f / childCount;
          float halfAngle = angleDelay / 2;
          float localAngle = angle;
  
          for (int i = 0; i &lt; childCount; i++) {
              final View child = getChildAt(i);
              if (child.getVisibility() == GONE) {
                  continue;
              }
  
              if (localAngle &gt; 360) {
                  localAngle -= 360;
              } else if (localAngle &lt; 0) {
                  localAngle += 360;
              }
  
              childWidth = child.getMeasuredWidth();
              childHeight = child.getMeasuredHeight();
              left = Math
                      .round((float) (((circleWidth / 2.0) - childWidth / 2.0) + radius
                              * Math.cos(Math.toRadians(localAngle))));
              top = Math
                      .round((float) (((circleHeight / 2.0) - childHeight / 2.0) + radius
                              * Math.sin(Math.toRadians(localAngle))));
  
              child.setTag(localAngle);
  
              float distance = Math.abs(localAngle - firstChildPosition.getAngle());
              boolean isFirstItem = distance &lt;= halfAngle || distance &gt;= (360 - halfAngle);
              if (isFirstItem &amp;&amp; selectedView != child) {
                  selectedView = child;
                  if (onItemSelectedListener != null &amp;&amp; isRotating) {
                      onItemSelectedListener.onItemSelected(child);
                  }
              }
  
              child.layout(left, top, left + childWidth, top + childHeight);
              localAngle += angleDelay;
          }
      }
  
      private void animateTo(float endDegree, long duration) {
          if (animator != null &amp;&amp; animator.isRunning() || Math.abs(angle - endDegree) &lt; 1) {
              return;
          }
  
          animator = ObjectAnimator.ofFloat(CircleLayout.this, "angle", angle, endDegree);
          animator.setDuration(duration);
          animator.setInterpolator(new DecelerateInterpolator());
          animator.addListener(new Animator.AnimatorListener() {
              private boolean wasCanceled = false;
  
              @Override
              public void onAnimationStart(Animator animation) {
              }
  
              @Override
              public void onAnimationRepeat(Animator animation) {
              }
  
              @Override
              public void onAnimationEnd(Animator animation) {
                  if (wasCanceled) {
                      return;
                  }
  
                  if (onRotationFinishedListener != null) {
                      View view = getSelectedItem();
                      onRotationFinishedListener.onRotationFinished(view);
                  }
              }
  
              @Override
              public void onAnimationCancel(Animator animation) {
                  wasCanceled = true;
              }
          });
          animator.start();
      }
      private void stopAnimation() {
          if (animator != null &amp;&amp; animator.isRunning()) {
              animator.cancel();
              animator = null;
          }
        /*  if (!isRotating) {
              if (mp.isPlaying()) {
                  mp.stop();
                  mp.release();
              }
          }*/
      }
  
      /**
       * @return The angle of the unit circle with the image views center
       */
      private double getPositionAngle(double xTouch, double yTouch) {
          double x = xTouch - (circleWidth / 2d);
          double y = circleHeight - yTouch - (circleHeight / 2d);
  
          switch (getPositionQuadrant(x, y)) {
              case 1:
                  return Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI;
              case 2:
              case 3:
                  return 180 - (Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI);
              case 4:
                  return 360 + Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI;
              default:
                  // ignore, does not happen
                  return 0;
          }
      }
  
      /**
       * @return The quadrant of the position
       */
      private static int getPositionQuadrant(double x, double y) {
          if (x &gt;= 0) {
              return y &gt;= 0 ? 1 : 4;
          } else {
              return y &gt;= 0 ? 2 : 3;
          }
      }
  
      @Override
      public boolean onTouchEvent(MotionEvent event) {
          if (isEnabled()) {
              gestureDetector.onTouchEvent(event);
              if (isRotating) {
             /*     if (!mp.isPlaying())
                  {
                      mp.start();
                  } */
                  switch (event.getAction()) {
                      case MotionEvent.ACTION_DOWN:
                          // reset the touched quadrants
                          for (int i = 0; i &lt; quadrantTouched.length; i++) {
                              quadrantTouched[i] = false;
                          }
                          stopAnimation();
                          touchStartAngle = getPositionAngle(event.getX(),
                                  event.getY());
  
                          didMove = false;
                          break;
                      case MotionEvent.ACTION_MOVE:
                          double currentAngle = getPositionAngle(event.getX(),
                                  event.getY());
                          rotateButtons((float) (touchStartAngle - currentAngle));
                          touchStartAngle = currentAngle;
                          didMove = true;
                          break;
                      case MotionEvent.ACTION_UP:
                          if (didMove) {
                              rotateViewToCenter(selectedView);
                          }
                          break;
                      default:
                          break;
                  }
              }
              // set the touched quadrant to true
              quadrantTouched[getPositionQuadrant(event.getX()
                      - (circleWidth / 2), circleHeight - event.getY()
                      - (circleHeight / 2))] = true;
              return true;
          }
          return false;
      }
  
      private class MyGestureListener extends SimpleOnGestureListener {
  
          @Override
          public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
              if (!isRotating) {
            /*      if (mp.isPlaying())
                  {
                      mp.stop();
                      mp.release();
                  } */
                  return false;
              }
              // get the quadrant of the start and the end of the fling
              int q1 = getPositionQuadrant(e1.getX() - (circleWidth / 2),
                      circleHeight - e1.getY() - (circleHeight / 2));
              int q2 = getPositionQuadrant(e2.getX() - (circleWidth / 2),
                      circleHeight - e2.getY() - (circleHeight / 2));
  
              if ((q1 == 2 &amp;&amp; q2 == 2 &amp;&amp; Math.abs(velocityX) &lt; Math
                      .abs(velocityY))
                      || (q1 == 3 &amp;&amp; q2 == 3)
                      || (q1 == 1 &amp;&amp; q2 == 3)
                      || (q1 == 4 &amp;&amp; q2 == 4 &amp;&amp; Math.abs(velocityX) &gt; Math
                      .abs(velocityY))
                      || ((q1 == 2 &amp;&amp; q2 == 3) || (q1 == 3 &amp;&amp; q2 == 2))
                      || ((q1 == 3 &amp;&amp; q2 == 4) || (q1 == 4 &amp;&amp; q2 == 3))
                      || (q1 == 2 &amp;&amp; q2 == 4 &amp;&amp; quadrantTouched[3])
                      || (q1 == 4 &amp;&amp; q2 == 2 &amp;&amp; quadrantTouched[3])) {
                  // the inverted rotations
                  animateTo(
                          getCenteredAngle(angle - (velocityX + velocityY) / 25),
                          25000L / speed);
              } else {
                  // the normal rotation
                  animateTo(
                          getCenteredAngle(angle + (velocityX + velocityY) / 25),
                          25000L / speed);
              }
  
              return true;
          }
  
          private float getCenteredAngle(float angle) {
              if (getChildCount() == 0) {
                  // Prevent divide by zero
                  return angle;
              }
  
              float angleDelay = 360 / getChildCount();
              float localAngle = angle % 360;
  
              if (localAngle &lt; 0) {
                  localAngle = 360 + localAngle;
              }
  
              for (float i = firstChildPosition.getAngle(); i &lt; firstChildPosition.getAngle() + 360; i += angleDelay) {
                  float locI = i % 360;
                  float diff = localAngle - locI;
                  if (Math.abs(diff) &lt; angleDelay) {
                      angle -= diff;
                      break;
                  }
              }
  
              return angle;
          }
  
          @Override
          public boolean onSingleTapUp(MotionEvent e) {
              View tappedView = null;
              int tappedViewsPosition = pointToChildPosition(e.getX(), e.getY());
              if (tappedViewsPosition &gt;= 0) {
                  tappedView = getChildAt(tappedViewsPosition);
                  tappedView.setPressed(true);
              } else {
                  // Determine if it was a center click
                  float centerX = circleWidth / 2F;
                  float centerY = circleHeight / 2F;
  
                  if (onCenterClickListener != null
                          &amp;&amp; e.getX() &lt; centerX + radius - (maxChildWidth / 2)
                          &amp;&amp; e.getX() &gt; centerX - radius + (maxChildWidth / 2)
                          &amp;&amp; e.getY() &lt; centerY + radius - (maxChildHeight / 2)
                          &amp;&amp; e.getY() &gt; centerY - radius + (maxChildHeight / 2)) {
                      onCenterClickListener.onCenterClick();
                      return true;
                  }
              }
  
              if (tappedView != null) {
  //                if (selectedView == tappedView) {
                      if (onItemClickListener != null) {
                          onItemClickListener.onItemClick(tappedView);
                      }
  //                } else {
  //                    rotateViewToCenter(tappedView);
  //                    if (!isRotating) {
  //                        if (onItemSelectedListener != null) {
  //                            onItemSelectedListener.onItemSelected(tappedView);
  //                        }
  //
  //                        if (onItemClickListener != null) {
  //                            onItemClickListener.onItemClick(tappedView);
  //                        }
  //                    }
  //                }
                  return true;
              }
              return super.onSingleTapUp(e);
          }
  
          private int pointToChildPosition(float x, float y) {
              for (int i = 0; i &lt; getChildCount(); i++) {
                  View view = getChildAt(i);
                  if (view.getLeft() &lt; x &amp;&amp; view.getRight() &gt; x
                          &amp; view.getTop() &lt; y &amp;&amp; view.getBottom() &gt; y) {
                      return i;
                  }
              }
              return -1;
          }
      }
  
      public interface OnItemClickListener {
          void onItemClick(View view);
      }
  
      public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
          this.onItemClickListener = onItemClickListener;
      }
  
      public interface OnItemSelectedListener {
          void onItemSelected(View view);
      }
  
      public void setOnItemSelectedListener(
              OnItemSelectedListener onItemSelectedListener) {
          this.onItemSelectedListener = onItemSelectedListener;
      }
  
      public interface OnCenterClickListener {
          void onCenterClick();
      }
  
      public void setOnCenterClickListener(
              OnCenterClickListener onCenterClickListener) {
          this.onCenterClickListener = onCenterClickListener;
      }
  
      public interface OnRotationFinishedListener {
          void onRotationFinished(View view);
  
      }
  
      public void setOnRotationFinishedListener(
              OnRotationFinishedListener onRotationFinishedListener) {
          this.onRotationFinishedListener = onRotationFinishedListener;
      }
  }
  </code></pre>