Code example for ObjectAnimator

Methods: cancel, isRunning

0
     * Force a stop to the chart motion. Called when the user taps during a fling. 
     */ 
    private void stopScrolling() { 
        // 
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            mAutoCenterAnimator.cancel();
        } 
 
 
        onScrollFinished(); 
    } 
 
    /** 
     * Returns the current rotation of the chart. 
     * 
     * @return The current chart rotation, in degrees. 
     */ 
    public int getChartRotation() { 
        return mChartRotation;
    } 
 
    /** 
     * Set the current rotation of the chart. Setting this value may change the current item. 
     * 
     * @param rotation The current chart rotation, in degrees. 
     */ 
    public void setChartRotation(int rotation) {
        rotation = (rotation % 360 + 360) % 360;
        mChartRotation = rotation;
        mChartView.rotateTo(rotation);
 
        // ... 
        calcCurrentItem(); 
    } 
 
    /** 
     * Add a new data item to this view. Adding an item adds a slice to the pie chart whose 
     * size is proportional to the item's value. As new items are added, the size of each 
     * existing slice is recalculated so that the proportions remain correct. 
     * 
     * @param label The label text that belongs to this item. 
     * @param value The value of this item. 
     * @param color The ARGB color of the pie chart slice associated with this item. 
     * @return The index of the newly added item. 
     */ 
    public int addItem(String label, float value, int color) {
        ChartItem item = new ChartItem();
        item.mLabel = label;
        item.mValue = value;
        item.mColor = color;
 
        // Calculate the highlight color. Saturate at 0xff to make sure that high values 
        // don't result in aliasing. 
        item.mHighlight = Color.argb(
                0xff, 
                Math.min((int) (mHighlightStrength * (float) Color.red(color)), 0xff),
                Math.min((int) (mHighlightStrength * (float) Color.green(color)), 0xff),
                Math.min((int) (mHighlightStrength * (float) Color.blue(color)), 0xff)
        ); 
        mTotal += value;
 
        mData.add(item);
 
        onDataChanged(); 
 
        return mData.size() - 1;
    } 
 
    @Override 
    public boolean onTouchEvent(MotionEvent event) {
        // Let the GestureDetector interpres this event 
        boolean result = mDetector.onTouchEvent(event);
 
        // If the GestureDetector doesn't want this event, do some custom processing. 
        // This code tries to detect when the user is done scrolling by looking for 
        // ACTION_UP events. 
        if (!result) {
            if (event.getAction() == MotionEvent.ACTION_UP) {
                // User is done scrolling, it's now safe to do things like autocenter 
                stopScrolling();; 
                result = true;
            } 
        } 
        return result;
    } 
 
    /** 
     * Do all of the recalculations needed when the data array changes. 
     */ 
    private void onDataChanged() { 
        // When the data changes, we have to recalculate all of the angles. 
        int currentAngle = 0;
        int lastItemIndex = mData.size() - 1;
        int i = 0;
        for (ChartItem item : mData) {
            item.mStartAngle = currentAngle;
            if (i == lastItemIndex) {
                item.mEndAngle = 360;
            } else { 
                item.mEndAngle = (int) ((float) currentAngle + item.mValue * 360.0f / mTotal);
            } 
            currentAngle = item.mEndAngle;
 
            // Recalculate the gradient shaders. There are three values in this 
            // gradient, even though only two are necessary, in order to work 
            // around a bug in certain versions of the graphics engine that expects 
            // at least three values if the positions array is non-null. 
            item.mShader = new SweepGradient(
                    mChartBounds.width() / 2.0f,
                    mChartBounds.height() / 2.0f,
                    new int[]{ 
                            item.mHighlight,
                            item.mHighlight,
                            item.mColor,
                            item.mColor
                    }, 
                    new float[]{ 
                            0, 
                            (float) (360 - item.mEndAngle) / 360.0f,
                            (float) (360 - item.mStartAngle) / 360.0f,
                            1.0f 
                    } 
            ); 
            i++;
        } 
 
        calcCurrentItem(); 
        onScrollFinished(); 
    } 
 
    /** 
     * Called when the user finishes a scroll action. 
     */ 
    private void onScrollFinished() { 
        if (mAutoCenterInSlice) {
            centerOnCurrentItem(); 
 
        } else { 
            // TODO : decelerate 
            mChartView.stopHardwareAcceleration();
        } 
    } 
 
    /** 
     * Animate the chart so that it centers the slice of the currently 
     * selected item. 
     */ 
    private void centerOnCurrentItem() { 
        Log.d(TAG, "Center on current item");
        ChartItem current = mData.get(getCurrentItem());
        int targetAngle = current.mStartAngle + (current.mEndAngle - current.mStartAngle) / 2;
        targetAngle -= mCurrentItemAngle;
        if (targetAngle < 90 && mChartRotation > 180) targetAngle += 360;
 
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            mAutoCenterAnimator.setIntValues(targetAngle);
            mAutoCenterAnimator.setDuration(AUTOCENTER_ANIM_DURATION).start();
        } 
 
    } 
 
    /** 
     * Calculate which pie chart slice is at the bottom, and set the current item 
     * field accordingly. 
     */ 
    private void calcCurrentItem() { 
        // 
        int pointerAngle = (mCurrentItemAngle + 360 + mChartRotation) % 360;
        // go through the slices and check 
        for (int i = 0; i < mData.size(); ++i) {
            ChartItem item = mData.get(i);
            if (item.mStartAngle <= pointerAngle && pointerAngle <= item.mEndAngle) {
                if (i != mCurrentItem) {
                    setCurrentItem(i, false);
                } 
                break; 
            } 
        } 
 
    } 
 
    private class ChartView extends View {
        // Used for SDK < 11 
        private float mRotation = 0;
        private Matrix mTransform = new Matrix(); 
        private PointF mPivot = new PointF();
        RectF mBounds;
 
        /** 
         * Constructor. 
         * @param context the context 
         */ 
        public ChartView(Context context) {
            super(context);
        } 
 
        /** 
         * Enable hardware acceleration 
         */ 
        public void startHardwareAcceleration() { 
            setLayerToHardware(this);
        } 
 
        /** 
         * Disable hardware acceleration 
         */ 
        public void stopHardwareAcceleration() { 
            setLayerToSoftware(this);
        } 
 
        @Override 
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
 
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
                mTransform.set(canvas.getMatrix());
                mTransform.preRotate(mRotation, mPivot.x, mPivot.y);
                canvas.setMatrix(mTransform);
            } 
 
            for (ChartItem item : mData) {
                mChartPaint.setShader(item.mShader);
                canvas.drawArc(mBounds,
                        360 - item.mEndAngle,
                        item.mEndAngle - item.mStartAngle,
                        true, mChartPaint);
            } 
        } 
 
 
        @Override 
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            mBounds = new RectF(0, 0, w, h);
        } 
 
        public void rotateTo(float chartRotation) {
            mRotation = chartRotation;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
                setRotation(chartRotation);
            } else { 
                invalidate();
            } 
        } 
 
        public void setPivot(float x, float y) {
            mPivot.x = x;
            mPivot.y = y;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
                setPivotX(x);
                setPivotY(y);
            } else { 
                invalidate();
            } 
        } 
    } 
 
    /** 
     * View that draws the inner circle on top of the chart 
     */ 
    private class MiddleView extends View {
 
        /** 
         * Constructor. 
         * @param context the context 
         */ 
        public MiddleView(Context context) {
            super(context);
        } 
 
        @Override 
        protected void onDraw(Canvas canvas) {
            canvas.drawCircle(mInnerCircleX, mInnerCircleY, mInnerCircleRadius, mTextPaint);
        } 
    } 
 
    /** 
     * Maintains the state for a chart data item. 
     */ 
    private class ChartItem { 
        public String mLabel;
        public float mValue;
        public int mColor;
 
        // Computed values 
        public int mStartAngle;
        public int mEndAngle;
 
        public int mHighlight;
        public Shader mShader;
    } 
 
    /** 
     * Gesture detector that extends {@link GestureDetector.SimpleOnGestureListener} to 
     * handle the gesture processing. 
     */ 
    private class GestureListener extends GestureDetector.SimpleOnGestureListener {
        @Override 
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            Log.d(TAG, "GestureListener::onScroll");
            // Set the chart rotation directly. 
            float scrollTheta = vectorToScalarScroll(
                    distanceX,
                    distanceY,
                    e2.getX() - mChartBounds.centerX(),
                    e2.getY() - mChartBounds.centerY()
            ); 
            setChartRotation(getChartRotation() - (int) scrollTheta / FLING_VELOCITY_DOWNSCALE);
            Log.d(TAG, "GestureListener::onScroll:: chart rotation set");
            return true; 
        } 
 
        @Override 
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            // ... 
 
 
            return true; 
        } 
 
        @Override 
        public boolean onDown(MotionEvent e) {
            Log.d(TAG, "GestureListener::onDown");
            // The user is interacting with the chart, turn on acceleration so the interaction is smooth 
            mChartView.startHardwareAcceleration();
            if (isAnimationRunning()) { 
                stopScrolling(); 
            } 
            return true; 
        } 
 
        @Override 
        public boolean onSingleTapConfirmed(MotionEvent e) {
            //return super.onSingleTapConfirmed(e); 
 
            // TODO 
 
            Log.d(TAG, "Single tap confirmed");
            return true; 
        } 
    } 
 
    private boolean isAnimationRunning() { 
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB && mAutoCenterAnimator.isRunning();
    } 
 
    private static float vectorToScalarScroll(float dx, float dy, float x, float y) {
        // get the length of the vector 
        float l = (float) Math.sqrt(dx * dx + dy * dy);