本文共 5768 字,大约阅读时间需要 19 分钟。
记得这个东西原来有个同事问过我,当时正在自学自定义View和属性动画这一块,对这个功能也没有写过,所以就google了一下,发了几个类似效果的github项目给朋友,今天礼拜天难得有心情写写代码,所以想想实现一下这个自定义View的效果。
首先,我们从这个gif的效果图中就可以得知这个自定义View我们需要哪些自定义属性,内部圆环的颜色、外部圆环的颜色、圆环的宽度、字体的大小、颜色,话不多说,直接撸码。/** * Created by Administrator on 2018/5/13. * 运动圆环自定义View */public class MotionCrcle extends View { /** * 外部圆环颜色 */ private int mOuterColor = Color.BLUE; /** * 里面圆环颜色 */ private int mInnerColor = Color.RED; /** * 跑步数的文字大小 */ private int mTextSize = 30; /** * 跑步文字的颜色 */ private int mTextColor = Color.BLACK; /** * 圆环的宽度 */ private int mCrcleWidth = 45; private Paint mOuterPaint, mInnerPaint, mTextPaint; private float mMaxSteps = 0; private int mCurrentSteps = 0; public MotionCrcle(Context context) { this(context, null); } public MotionCrcle(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public MotionCrcle(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.MotionCrcle); mOuterColor = array.getColor(R.styleable.MotionCrcle_outerCrcleColor, mOuterColor); mInnerColor = array.getColor(R.styleable.MotionCrcle_innerCrcleColor, mInnerColor); mCrcleWidth = array.getInt(R.styleable.MotionCrcle_crcleWidth, mCrcleWidth); mTextColor = array.getColor(R.styleable.MotionCrcle_crcleTextColor, mTextColor); mTextSize = array.getInt(R.styleable.MotionCrcle_crcleTextSize, mTextSize); initPaint(); array.recycle(); } public synchronized void setmMaxSteps(float mMaxSteps) { this.mMaxSteps = mMaxSteps; } public synchronized void setmCurrentSteps(int mCurrentSteps) { this.mCurrentSteps = mCurrentSteps; invalidate(); } private void initPaint() { mOuterPaint = new Paint(); mOuterPaint.setAntiAlias(true); mOuterPaint.setStrokeWidth(mCrcleWidth); mOuterPaint.setStrokeCap(Paint.Cap.ROUND); mOuterPaint.setStyle(Paint.Style.STROKE); mOuterPaint.setColor(mOuterColor); mInnerPaint = new Paint(); mInnerPaint.setAntiAlias(true); mInnerPaint.setStrokeWidth(mCrcleWidth); mInnerPaint.setStrokeCap(Paint.Cap.ROUND); mInnerPaint.setStyle(Paint.Style.STROKE); mInnerPaint.setColor(mInnerColor); mTextPaint = new Paint(); mTextPaint.setAntiAlias(true); mTextPaint.setTextSize(mTextSize); mTextPaint.setColor(mTextColor); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = MeasureSpec.getSize(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); if(width != height){ /** * 因为运动圆环是一个正方形,所以我们要设置一个最小值作为View的长度 */ int min = Math.min(width, height); widthMeasureSpec = MeasureSpec.makeMeasureSpec(min, MeasureSpec.EXACTLY); heightMeasureSpec = MeasureSpec.makeMeasureSpec(min, MeasureSpec.EXACTLY); setMeasuredDimension(widthMeasureSpec, heightMeasureSpec); } } @Override protected void onDraw(Canvas canvas) { //因为圆环本身是具有宽度的,所以我们这里左边的顶点要往右mCrcleWidth个距离,右边底部的点要往左,所以要减去mCrcleWidth RectF recf = new RectF(mCrcleWidth, mCrcleWidth, getWidth() - mCrcleWidth, getHeight() - mCrcleWidth); //135是外部圆环的初始角度,270是所画的总角度数 canvas.drawArc(recf, 135, 270, false, mOuterPaint); if(mCurrentSteps == 0){ return; } //算出当前步数是最大步数的比值 float angle = mCurrentSteps / mMaxSteps; canvas.drawArc(recf, 135, 270 * angle, false, mInnerPaint); String stepText = mCurrentSteps+""; Rect bounds = new Rect(); //算出步数文字的baseLine,也就是基准线 mTextPaint.getTextBounds(stepText, 0, stepText.length(), bounds); int x = getWidth() / 2 - bounds.width() / 2; Paint.FontMetricsInt metrices = mTextPaint.getFontMetricsInt(); int diffY = (metrices.bottom - metrices.top) / 2 - metrices.bottom; int baseLine = getHeight() / 2 + diffY; canvas.drawText(stepText, x, baseLine, mTextPaint); }}
文字的绘制和画圆画弧不一样,其实仔细想想也明白,如果绘制按照左上角开始的话是不现实的,因为文字不可能是简单的顶部或底部对其,应该是重心对齐,简单说就是基准线,所以代码中基准线的算法是bottom-top再除以2减去bottom,以基准线开始绘制,top就是负数,bottom是正数。
public class MainActivity extends AppCompatActivity { private Button mBtn; private baohuo.rzj.com.myapplication.Views.MotionCrcle mCrcle; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mBtn = (Button) findViewById(R.id.btn); mCrcle = (MotionCrcle) findViewById(R.id.motion_crcle); mCrcle.setmMaxSteps(10000); final ValueAnimator animator = ObjectAnimator.ofFloat(0,4500); animator.setDuration(2000); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { float value = (float) valueAnimator.getAnimatedValue(); mCrcle.setmCurrentSteps((int) value); } }); mBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { animator.start(); } }); }}
以上是Activity的代码,非常简单,不做解释,这个值得一提的是,我在写的时候内部圆环一直没有绘制,文字在不断变化,通过debug发现,我把步数最大值和当前步数设置为int,一个小的int除以大的int的到的只有0,所以我把最大步数改为float,这样得出的值就有小数位,最后附上xml代码。
转载地址:http://ezbcl.baihongyu.com/