先看下要实现的效果:
可以控制显示的进度,上图只是简单的开了线程控制进度,下面看具体实现。
addArc:绘制弧形
需要注意的是圆心位置的把握,以及圆弧是从哪里开始的,自己动手玩玩就知道具体的位置。
Canvas c = new Canvas(boundBitmap);
c.translate(50,50);//移动画板
c.drawColor(Color.WHITE);
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);p.setColor(0xFF66AAFF);p.setStyle(Paint.Style.STROKE);//设置为画边框
Path path = new Path();
RectF rectF = new RectF(0,0,100,100);//圆弧的圆心在对角线中间,即: 50,50
path.addArc(rectF,0,90);//开始角度和扫过的角度
path.close();
c.drawPath(path,p);
图中标识了圆心位置:rectF 的对角线为圆心的位置,而开始是从X正轴开始的,顺时针方向。
RectF rectF = new RectF(0,0,100,100);//圆弧的圆心在对角线中间,即: 50,50
path.addArc(rectF,0,90);//开始角度和扫过的角度
绘制一个椭圆:
private void makeBound() {
boundBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(boundBitmap);
c.translate(50,50);
c.drawColor(Color.WHITE);
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
p.setColor(0xFF66AAFF);
p.setStyle(Paint.Style.STROKE);//设置为画边框
Path path = new Path();
RectF rectF = new RectF(0,0,100,100);//圆弧的圆心在对角线中间,即:50,50
path.addArc(rectF,90,180);
path.lineTo(200,0);
RectF rectF2 = new RectF(150,0,250,100);//圆弧的圆心在对角线中间,即:200,50
path.addArc(rectF2,270,180);
path.moveTo(200,100);
path.lineTo(50,100);
path.close();
c.drawPath(path,p);
}
下面绘制一个进度的椭圆(进度条效果)
用到的知识点:
- path类的op(Path path, Path.Op op):对两个path之间的各种组合
- xfermode:对两个bitmap的各种组合
在之前一篇中有介绍xfermode:http://www.jianshu.com/p/2eca76145aae
先来看下效果:
public class LevelView extends View {
private int width;
private int height;
private Paint mPaint;
private Bitmap rectBitmap;
private Bitmap boundBitmap;
public LevelView(Context context) {
this(context, null);
}
public LevelView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public LevelView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(Color.parseColor("#ff0000"));
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int viewWidth = this.getPaddingLeft() + this.getPaddingRight();
int viewHeight = this.getPaddingTop() + this.getPaddingBottom();
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
//Measure Width
if (widthMode == MeasureSpec.EXACTLY) {
//Must be this size
width = widthSize;
} else if (widthMode == MeasureSpec.AT_MOST) {
//Can't be bigger than...
width = Math.min(viewWidth, widthSize);
} else {
//Be whatever you want
width = viewWidth;
}
//Measure Height
if (heightMode == MeasureSpec.EXACTLY || widthMode == MeasureSpec.EXACTLY) {
//Must be this size
height = heightSize;
} else if (heightMode == MeasureSpec.AT_MOST) {
//Can't be bigger than...
height = Math.min(viewHeight, heightSize);
} else {
//Be whatever you want
height = viewHeight;
}
setMeasuredDimension(width, height);
makeRect();
makeBound();
change();
}
private int pathBWidth = 100;
@TargetApi(Build.VERSION_CODES.KITKAT)
private void makeBound() {
boundBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(boundBitmap);
c.drawColor(Color.TRANSPARENT);
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
p.setColor(0xFF66AAFF);
p.setStyle(Paint.Style.STROKE);//设置为画边框
Path pathA = new Path();
RectF rectA1 = new RectF(0, 0, 100, 100);//圆弧的圆心在对角线中间,即:50,50
pathA.addArc(rectA1, 90, 180);
pathA.lineTo(200, 0);
RectF rectA2 = new RectF(150, 0, 250, 100);//圆弧的圆心在对角线中间,即:200,50
pathA.addArc(rectA2, 270, 180);
pathA.lineTo(50, 100);
c.drawPath(pathA, p);
}
@TargetApi(Build.VERSION_CODES.KITKAT)
private void makeRect() {
rectBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(rectBitmap);
c.drawColor(Color.TRANSPARENT);
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
p.setColor(0xFF66AAFF);
p.setStyle(Paint.Style.FILL);//设置为画边框
Path pathA = new Path();
RectF rectA1 = new RectF(0, 0, 100, 100);//圆弧的圆心在对角线中间,即:50,50
pathA.addArc(rectA1, 90, 180);
pathA.lineTo(200, 0);
RectF rectA2 = new RectF(150, 0, 250, 100);//圆弧的圆心在对角线中间,即:200,50
pathA.addArc(rectA2, 270, 180);
pathA.lineTo(50, 100);
Path pathB = new Path();
RectF rectB = new RectF(0, 0, pathBWidth, 100);
pathB.addRect(rectB, Path.Direction.CCW);
pathB.close();
pathA.op(pathB, Path.Op.INTERSECT);
c.drawPath(pathA, p);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.TRANSPARENT);
canvas.drawBitmap(boundBitmap, 0, 0, mPaint);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));
canvas.drawBitmap(rectBitmap, 0, 0, mPaint);
mPaint.setXfermode(null);
}
private void change() {
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
SystemClock.sleep(10);
pathBWidth += 1;
if (pathBWidth > 250) {
pathBWidth = 0;
}
postInvalidate();
makeRect();
}
}
}).start();
}
}
总结 Path.Op的组合:
Path.Op.DIFFERENCE 减去path1中path1与path2都存在的部分;
path1 = (path1 - path1 ∩ path2)
Path.Op.INTERSECT 保留path1与path2共同的部分;
path1 = path1 ∩ path2
Path.Op.UNION 取path1与path2的并集;
path1 = path1 ∪ path2
Path.Op.REVERSE_DIFFERENCE 与DIFFERENCE刚好相反;
path1 = path2 - (path1 ∩ path2)
Path.Op.XOR 与INTERSECT刚好相反;
path1 = (path1 ∪ path2) - (path1 ∩ path2)
最后加上xml中的布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:background="#afaeae"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="xiaocai.test03.MainActivity">
<xiaocai.test03.LevelView
android:layout_marginTop="20dp"
android:layout_marginLeft="20dp"
android:layout_width="500dp"
android:layout_height="500dp"
android:layout_centerInParent="true" />
</LinearLayout>