12

I've made a custom pie chart view that I want to animate starting when the pie chart is visible. Currently what I have is the pie chart animating but by the time you can actually see it on the screen the animation is half over. This is what I have:

public class SinglePieChart extends SurfaceView implements SurfaceHolder.Callback {
    // Chart setting variables
    private int emptyCircleCol, strokeColor, number, total;

    // Paint for drawing custom view
    private Paint circlePaint;

    private RectF rect;

    private Context context;
    private AnimThread animThread;
    private SurfaceHolder holder;

    // animation variables
    private float speed;
    private float current = 0.0f;
    private boolean percentsCalculated = false;
    private float degree;
    private int viewWidth, viewHeight;

    public SinglePieChart(Context ctx, AttributeSet attrs) {
        super(ctx, attrs);

        context = ctx;

        // Paint object for drawing in doDraw
        circlePaint = new Paint();
        circlePaint.setStyle(Style.STROKE);
        circlePaint.setStrokeWidth(3);
        circlePaint.setAntiAlias(true);
        circlePaint.setDither(true);


        rect = new RectF();

        //get the attributes specified in attrs.xml using the name we included
        TypedArray a = context.getTheme().obtainStyledAttributes(attrs,
            R.styleable.DashboardChartSmall, 0, 0);

        try {
            //get the colors specified using the names in attrs.xml
            emptyCircleCol = a.getColor(R.styleable.DashboardChartSmall_smCircleColor, 0xFF65676E); // light gray is default
            strokeColor    = a.getColor(R.styleable.DashboardChartSmall_smColor, 0xFF39B54A); // green is default

            // Default number values
            total  = a.getInteger(R.styleable.DashboardChartSmall_smTotal,  1);
            number = a.getInteger(R.styleable.DashboardChartSmall_smNumber, 0);
        } finally {
            a.recycle();
        }

        this.setZOrderOnTop(true);

        holder = getHolder();
        holder.setFormat(PixelFormat.TRANSPARENT);

        holder.addCallback(this);
    }

    protected void calculateValues() {
        degree = 360 * number / total;
        percentsCalculated = true;
        speed = 10 * number / total;
        viewWidth  = this.getMeasuredWidth();
        viewHeight = this.getMeasuredHeight();

        float top, left, bottom, right;

        if (viewWidth < viewHeight) {
            left = 4;
            right = viewWidth - 4;
            top = ((viewHeight - viewWidth) / 2) + 4;
            bottom = viewHeight - top;
        } else {
            top = 4;
            bottom = viewHeight - 4;
            left = ((viewWidth - viewHeight) / 2) + 4;
            right = viewWidth - left;
        }

        rect.set(left, top, right, bottom);
    }

    protected void doDraw(Canvas canvas) {
        if (total == 0) {
            // Number values are not ready 
            animThread.setRunning(false);
            return;
        }

        if (!percentsCalculated) {
            calculateValues();
        }

        // set the paint color using the circle color specified
        float last = current;
        float start = -90;

        circlePaint.setColor(strokeColor);
        canvas.drawArc(rect, start, (last > degree) ? degree : last, false, circlePaint);
        start += (last > number) ? number : last;
        last = (last < number) ? 0 : last - number;

        circlePaint.setColor(emptyCircleCol);

        if (current > 360) {
            current = 360;
        }

        canvas.drawArc(rect, start, 360 - current, false, circlePaint);     

        current += speed;

        if (last > 0 || number == 0) {
            // we're done
            animThread.setRunning(false);
        }
    }

    public void setNumbers(int num, int tot) {
        number = num;
        total  = tot;

        invalidate();
        requestLayout();
    }


    public void setColor(int col) {
        strokeColor = col;
    }

    public void redraw() {
        calculateValues();
        animThread.setRunning(true);

        invalidate();
        requestLayout();
    }

    @Override
    public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
    }

    @Override
    public void surfaceCreated(SurfaceHolder arg0) {
        animThread = new AnimThread(holder, context, this);
        animThread.setRunning(true);
        animThread.start();
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder arg0) {
        animThread.setRunning(false);

        boolean retry = true;

        while(retry) {
            try {
                animThread.join();
                retry = false;
            } catch(Exception e) {
                Log.v("Exception Occured", e.getMessage());
            }
        }
    }

    public class AnimThread extends Thread {
        boolean mRun;
        Canvas mcanvas;
        SurfaceHolder surfaceHolder;
        Context context;
        SinglePieChart msurfacePanel;

        public AnimThread(SurfaceHolder sholder, Context ctx, SinglePieChart spanel) {
            surfaceHolder = sholder;
            context = ctx;
            mRun = false;
            msurfacePanel = spanel;
        }

        void setRunning(boolean bRun) {
            mRun = bRun;
        }

        @Override
        public void run() {
            super.run();
            while (mRun) {
                mcanvas = surfaceHolder.lockCanvas();
                if (mcanvas != null) {
                    msurfacePanel.doDraw(mcanvas);
                    surfaceHolder.unlockCanvasAndPost(mcanvas);
                }
            }
        }
    }
}

Also if you see any programming errors, memory leaks, poor performing code, please let me know. I'm new to Android.

Here is the layout that uses the SinglePieChart class:

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1">
        <com.davidscoville.vokab.views.elements.SinglePieChart
            android:id="@+id/smallPieChart"
            android:layout_width="match_parent" 
            android:layout_height="match_parent" />

        <TextView
            android:id="@+id/dashSmNumber"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:layout_centerVertical="true"
            android:textSize="25sp"
            android:textColor="#FFFFFF" />

    </RelativeLayout>

    <TextView
        android:id="@+id/dashSmLabel"
        android:layout_width="match_parent"
        android:layout_height="20dp"
        android:textSize="14sp"
        android:gravity="center"
        android:textColor="#FFFFFF" />
</merge>
4

2 回答 2

5

好吧,我要使用我的饼图,它不会自动设置动画,它会有一个新功能,一旦准备好,Activity 将触发开始动画。我希望有一个更简单的方法...

于 2013-05-26T17:24:25.563 回答
1

或者,您可以使用动画框架(如果您想支持较旧的 API,则可以使用九个旧机器人)。这将允许您为视图上的属性设置动画,在您的情况下是 start 和 current 变量。

我将其设置为在onAttachedToWindow期间发生。

请注意,如果您在此饼图中没有做很多其他事情,则表面视图可能会超出您的需求。

于 2013-05-31T12:49:19.157 回答