这里主要讲一下刻度的绘制。刻度绘制主要使用 Canvas 类的 save()、rotate()和restore() 方法,当然你也可以使用 translate() 方法对坐标系进行平移,方便计算。
/** * 用来保存Canvas的状态。save之后,可以调用Canvas的平移、放缩、旋转、错切、裁剪等操作。 */ public void save() /** * 旋转一定的角度绘制图像 * @param degrees 旋转角度 * @param x 旋转中心点x轴坐标 * @param y 旋转中心点y轴坐标 */ public void rotate(float degrees, float x, float y) /** * 在当前的坐标上平移(x,y)个像素单位 * 若dx <0 ,沿x轴向上平移; dx >0 沿x轴向下平移 * 若dy <0 ,沿y轴向上平移; dy >0 沿y轴向下平移 */ public void translate(float dx, float dy) /** * 用来恢复Canvas之前保存的状态。防止save后对Canvas执行的操作对后续的绘制有影响。 */ public void restore() private void drawDial(Canvas canvas) { int total = (int) (mSweepAngle / mDialIntervalDegree); canvas.save(); canvas.rotate(mStartAngle, mCenterPoint.x, mCenterPoint.y); for (int i = 0; i <= total; i++) { canvas.drawLine(mCenterPoint.x + mRadius, mCenterPoint.y, mCenterPoint.x + mRadius + mArcWidth, mCenterPoint.y, mDialPaint); canvas.rotate(mDialIntervalDegree, mCenterPoint.x, mCenterPoint.y); } canvas.restore(); }关于 Canvas 的画布操作可以参考这篇文章:安卓自定义View进阶-Canvas之画布操作
水波纹效果的进度条
水波纹效果的进度条实现需要用到贝塞尔曲线,主要难点在于 绘制区域的计算 和 波浪效果 的实现,其余的逻辑跟上述两种进度条相似。
这里使用了 Path 类,该类在 Android 2D 绘图中是非常重要的,Path 不仅能够绘制简单图形,也可以绘制这些比较复杂的图形。也可以对多个路径进行布尔操作,类似设置 Paint 的 setXfermode() ,具体使用可以参考这篇博客:安卓自定义View进阶-Path基本操作。这里就不再赘述,有机会自己也会对 Android 自定义 View 的知识进行总结,不过,感觉应该了了无期。
继续上示意图,请叫我灵魂画手~
图中黑色的圆为我们要绘制的进度条圆,黑色的曲线为初始状态的的波浪,该波浪使用贝塞尔曲线绘制,其中奇数的点为贝塞尔曲线的起始点,偶数的点为贝塞尔曲线的控制点。例如:1——>2——>3就为一条贝塞尔曲线,1 是起点,2 是控制点,3 是终点。从图中可以看到波浪在园内圆外各一个(1—>5 和 5->9),通过对波浪在 x 轴上做平移,即图中蓝色实线,来实现波浪的动态效果,所以一个波浪的完整动画效果需要有两个波浪来实现。同理,通过控制 y 轴的偏移量,即图中蓝色虚线,可以实现波浪随进度的上涨下降。
贝塞尔曲线上起始点和控制点的计算如下:
/** * 计算贝塞尔曲线上的起始点和控制点 * @param waveWidth 一个完整波浪的宽度 */ private Point[] getPoint(float waveWidth) { Point[] points = new Point[mAllPointCount]; //第1个点特殊处理,即数组的中心 points[mHalfPointCount] = new Point((int) (mCenterPoint.x - mRadius), mCenterPoint.y); //屏幕内的贝塞尔曲线点 for (int i = mHalfPointCount + 1; i < mAllPointCount; i += 4) { float width = points[mHalfPointCount].x + waveWidth * (i / 4 - mWaveNum); points[i] = new Point((int) (waveWidth / 4 + width), (int) (mCenterPoint.y - mWaveHeight)); points[i + 1] = new Point((int) (waveWidth / 2 + width), mCenterPoint.y); points[i + 2] = new Point((int) (waveWidth * 3 / 4 + width), (int) (mCenterPoint.y + mWaveHeight)); points[i + 3] = new Point((int) (waveWidth + width), mCenterPoint.y); } //屏幕外的贝塞尔曲线点 for (int i = 0; i < mHalfPointCount; i++) { int reverse = mAllPointCount - i - 1; points[i] = new Point(points[mHalfPointCount].x - points[reverse].x, points[mHalfPointCount].y * 2 - points[reverse].y); } return points; }以上,我们已经获取到绘制贝塞尔曲线所需的路径点。接下来,我们就需要来计算出绘制区域,即使用 Path 类。
紫色区域为贝塞尔曲线需要绘制的整体区域。
红色区域为上图紫色区域与圆的交集,也就是波浪要显示的区域