Android 自定义View (二)
一、前言
通过一个简单的旋转环对自定义view作了一个基本的认识,本文将大致讲解下实现的思路以及对该view的一些可能的改进。
二、思路
主要通过重写 view 中的 onDraw() 方法,利用 canvas 类中的 drawArc() 方法绘制圆弧,其中第一个参数 rectF 是一个浮点矩形,确定了圆弧的大小及位置 (想象一个放在矩形中的圆,截取其弧即成圆环)。其中圆弧颜色由画笔类 Paint 定义, 为简单起见,这里使用两种颜色的渐变色。值得一提的是,当圆弧两端成圆头时 ( paint.getStrokeCap() == Paint.Cap.ROUND ),其首端的圆头颜色为尾部的颜色,如图2.1.1 所示。因此此处要将起始旋转角度 + width/2。其中 width 为圆环的宽度,除以2 恰好为圆头的半径,故可以通过这种办法消除掉,如图2.1.2所示
绘制圆弧需要确定起始角度 startAngle,以及扫过的角度sweepAngle,对于 startAngle,其中3点钟方向为0°, sweepAngle一般不超过360° (超过即成一个完整的圆)。此外还需要一个boolean变量的值,来确定是否绘出圆弧对应的半径。由于我们不需要扇形,这里设为fasle。
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.rotate(angle, centerX, centerY); canvas.drawArc(rectF, paint.getStrokeCap() == Paint.Cap.ROUND ? startAngle + width/2f: startAngle, sweepAngle, false, paint); }
图2.1.1 首部圆头部分出现尾部颜色 图2.1.2 正常的圆弧
canvas.drawArc() 的第五个参数为Paint类型,这里定义了绘制圆弧所用到的画笔,如下所示
private void setPaint(){ paint = new Paint(); paint.setStyle(Paint.Style.STROKE); paint.setStrokeCap(Paint.Cap.ROUND); paint.setShader(new SweepGradient(centerX, centerY, new int[]{startColor, endColor}, new float[]{startAngle/360f*1.0f, sweepAngle/360f*1.0f})); paint.setAntiAlias(true); paint.setStrokeWidth(width); }
此处主要说说何处初始化该 paint 的原因。由于实现圆弧的渐变色用到了 SweepGradient 着色器,着色器需要传入x、y来确定扫描渐变色的中心,故需要在获取到控件中心之后再进行初始化。在该自定义圆弧控件中,是通过 onSizeChanged(int w, int h, int oldw, int oldh) 方法来获取控件视图的中点,故在 onSizeChanged() 方法中对画笔进行初始化,如下所示
@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); centerX = w / 2; centerY = h / 2; rectF = new RectF(centerX - radius, centerY - radius, centerX + radius, centerY + radius); setPaint(); }
最后,通过 ValueAnimator 类给圆弧设置一个旋转动画,通过调用视图本身的 invalidate() 方法,不断通知视图进行重绘 (不断调用onDraw()方法 ),实现了圆弧视觉上的旋转。
public void startAnim(){ final ValueAnimator animator = ValueAnimator.ofInt(0,360); animator.setDuration(duration); animator.setRepeatCount(ValueAnimator.INFINITE); animator.setRepeatMode(ValueAnimator.RESTART); animator.setInterpolator(new LinearInterpolator()); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { angle = (int) animation.getAnimatedValue(); invalidate(); } }); animator.start(); }
讲述如何添加自定义属性,在xml中声明旋转环的颜色、半径、宽度,旋转速率等,避免在代码中显式调用setXXX()方法。