前面的文章提到过一个圆环交替效果的自定义View:http://my.oschina.net/august1996/blog/655682
前面的文章中我们通过使用线程去控制mProgress的值然后去从新绘制View来达到动态的效果.其实我们可以通过属性动画去设置我们自定义的动画属性,例如mProgress就是了.下面我们来说一下ObjectAnimator的原理吧.
我们通过ofXXX的方法去设置动画数字区间,然后ObjectAnimator会找到用户所设置的加速器计算得出步进值,然后是计算当前值,最后调用对应属性的set函数.回想一下我们在http://my.oschina.net/august1996/blog/658240,这篇文章说过,怎么知道哪些属性可以改变,就是找那个view的setXXX方法,去掉set就要XXX就是属性了.那下面就直接拿上次的那个代码来修改,原来是这样的
package com.example.customview; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.RectF; import android.util.AttributeSet; import android.util.TypedValue; import android.view.View; /** * Created by August on 16/4/9. */ public class CircleProgressView extends View { private int mCircleWidth; private int mCircleFirstColor; private int mCircleSecondColor; private int mSpeed; /** * 接收前面设置的属性 */ private Paint mPaint; private boolean isSecond = false; /** * 当前是否为第二圈 */ private int mProgress = 0; /** * 进度 */ public CircleProgressView(Context context) { this(context, null); } public CircleProgressView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public CircleProgressView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); TypedArray ta = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CircleProgressView, defStyleAttr, 0); int n = ta.getIndexCount(); for (int i = 0; i < n; i++) { int attr = ta.getIndex(i); switch (attr) { case R.styleable.CircleProgressView_mCircleWidth: mCircleWidth = ta.getDimensionPixelSize(attr, (int) TypedValue .applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16, getResources().getDisplayMetrics())); break; case R.styleable.CircleProgressView_mCircleFirstColor: mCircleFirstColor = ta.getColor(attr, 0); break; case R.styleable.CircleProgressView_mCircleSecondColor: mCircleSecondColor = ta.getColor(attr, 0); break; case R.styleable.CircleProgressView_mCircleSpeed: mSpeed = ta.getInteger(attr, 20); break; } } ta.recycle(); mPaint = new Paint(); new Thread() { @Override public void run() { while (true) { mProgress++; if (mProgress == 360) { mProgress = 0; isSecond = !isSecond; /** * 控制颜色的切换 */ } try { Thread.sleep(mSpeed); // 速度的设置 } catch (InterruptedException e) { e.printStackTrace(); } postInvalidate(); // 通过postInvalidate来调用onDraw方法重新绘画 } } }.start(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); int center = getWidth() / 2; // 圆心位置,因为圆的半径一致,所以圆心只需确定一次 int radius = center - mCircleWidth; // 确定圆的半径 mPaint.setStrokeWidth(mCircleWidth); // 设置画笔粗细 mPaint.setStyle(Paint.Style.STROKE); // 设置空心画笔,不使用填充 RectF oval = new RectF(center - radius, center - radius, center + radius, center + radius); /** * 设置圆的边界 */ if (isSecond) { mPaint.setColor(mCircleSecondColor); canvas.drawCircle(center, center, radius, mPaint); // 画圆 mPaint.setColor(mCircleFirstColor); canvas.drawArc(oval, 0, mProgress, false, mPaint); /** * 0是开始画的度数,参考直角坐标系 0是x的正半轴 90是y的负半轴 180是x的负半轴 270是y的正半轴 可用负数表示 */ } else { mPaint.setColor(mCircleFirstColor); canvas.drawCircle(center, center, radius, mPaint); mPaint.setColor(mCircleSecondColor); canvas.drawArc(oval, 0, mProgress, false, mPaint); } } public int getmProgress() { return mProgress; } public void setmProgress(int mProgress) { this.mProgress = mProgress; } }
我们把那个更新mProgress值得线程去掉并且增加方法
public int getmProgress() { return mProgress; } public void setmProgress(int mProgress) { this.mProgress = mProgress; } public void setProgress(int mProgress) { setmProgress(mProgress); invalidate(); }
布局文件
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical" > <com.example.customview.CircleProgressView android:id="@+id/mCircleProgressView" android:layout_width="200dp" android:layout_height="200dp" app:mCircleFirstColor="#FF0000" app:mCircleSecondColor="#00FF00" app:mCircleSpeed="1" app:mCircleWidth="30dp" /> </LinearLayout>
然后我们去调用
mCircleProgressView = (CircleProgressView) findViewById(R.id.mCircleProgressView); ObjectAnimator objectAnimator = ObjectAnimator.ofInt(mCircleProgressView, "Progress", 0, 360); objectAnimator.setRepeatCount(ObjectAnimator.INFINITE); objectAnimator.setDuration(1000); objectAnimator.start();
看,我们增加了那三个方法,然后我们就可以去使用Progress做属性动画了,其中Progress首字母不区分大小写.
动画虽然是可以动了,但是我们发现第二圈的效果没有了.因为我们压根没有改变isSecond的值,下面我们考虑下应该怎么做...
其实很简单,放置一个lastProgress保存上次的进度,因为同一圈的进度都是递增的,所以如果当lastProgress的值大于mProgress的值,则可认为mProgress已经进入了下一圈,于是我们增加变量lastProgress,修改方法:
public void setProgress(int mProgress) { if (lastProgress > mProgress) { isSecond = !isSecond; } lastProgress = mProgress; setmProgress(mProgress); invalidate(); }
我们想要的效果就已经达到了,整体代码:
package com.example.customview; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.RectF; import android.util.AttributeSet; import android.util.TypedValue; import android.view.View; /** * Created by August on 16/4/9. */ public class CircleProgressView extends View { private int mCircleWidth; private int mCircleFirstColor; private int mCircleSecondColor; private int mSpeed; /** * 接收前面设置的属性 */ private Paint mPaint; private boolean isSecond = false; /** * 当前是否为第二圈 */ private int mProgress = 0; private int lastProgress; /** * 进度 */ public CircleProgressView(Context context) { this(context, null); } public CircleProgressView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public CircleProgressView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); TypedArray ta = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CircleProgressView, defStyleAttr, 0); int n = ta.getIndexCount(); for (int i = 0; i < n; i++) { int attr = ta.getIndex(i); switch (attr) { case R.styleable.CircleProgressView_mCircleWidth: mCircleWidth = ta.getDimensionPixelSize(attr, (int) TypedValue .applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16, getResources().getDisplayMetrics())); break; case R.styleable.CircleProgressView_mCircleFirstColor: mCircleFirstColor = ta.getColor(attr, 0); break; case R.styleable.CircleProgressView_mCircleSecondColor: mCircleSecondColor = ta.getColor(attr, 0); break; case R.styleable.CircleProgressView_mCircleSpeed: mSpeed = ta.getInteger(attr, 20); break; } } ta.recycle(); mPaint = new Paint(); // new Thread() { // @Override // public void run() { // while (true) { // mProgress++; // if (mProgress == 360) { // mProgress = 0; // isSecond = !isSecond; // /** // * 控制颜色的切换 // */ // } // try { // Thread.sleep(mSpeed); //速度的设置 // } catch (InterruptedException e) { // e.printStackTrace(); // } // postInvalidate(); //通过postInvalidate来调用onDraw方法重新绘画 // } // } // }.start(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); int center = getWidth() / 2; // 圆心位置,因为圆的半径一致,所以圆心只需确定一次 int radius = center - mCircleWidth; // 确定圆的半径 mPaint.setStrokeWidth(mCircleWidth); // 设置画笔粗细 mPaint.setStyle(Paint.Style.STROKE); // 设置空心画笔,不使用填充 RectF oval = new RectF(center - radius, center - radius, center + radius, center + radius); /** * 设置圆的边界 */ if (isSecond) { mPaint.setColor(mCircleSecondColor); canvas.drawCircle(center, center, radius, mPaint); // 画圆 mPaint.setColor(mCircleFirstColor); canvas.drawArc(oval, 0, mProgress, false, mPaint); /** * 0是开始画的度数,参考直角坐标系 0是x的正半轴 90是y的负半轴 180是x的负半轴 270是y的正半轴 可用负数表示 */ } else { mPaint.setColor(mCircleFirstColor); canvas.drawCircle(center, center, radius, mPaint); mPaint.setColor(mCircleSecondColor); canvas.drawArc(oval, 0, mProgress, false, mPaint); } } public int getmProgress() { return mProgress; } public void setmProgress(int mProgress) { this.mProgress = mProgress; } public void setProgress(int mProgress) { if (lastProgress > mProgress) { isSecond = !isSecond; } lastProgress = mProgress; setmProgress(mProgress); invalidate(); } }