一:弧形柱的画法
/**
* 弧形柱的画法,可用于饼图
*/
float outSideRadius = getWidth()/3;//外圆半径
float inSideRadius = getWidth()/4;//内圆半径
//midX和midY是View的中心位置,这里用作俩圆的圆心(midX,midY)坐标
float midX = getWidth()/2;
float midY = getHeight()/2;
canvas.drawCircle(midX, midY, outSideRadius, ringPaint);//外层圆,第一二个参数是圆心的x,y坐标,这里是View的中心,第二个参数是半径,第三个参数是Paint
canvas.drawCircle(midX, midY, inSideRadius, ringPaint);//内层圆
//圆心的x,y坐标各减半径即为包裹该圆的矩形左上角的x,y坐标,各加半径即为右下角的坐标,这样就确定了这个矩形
RectF outSideRectF = new RectF(midX-outSideRadius,midY-outSideRadius,midX + outSideRadius, midY+outSideRadius);//包裹外层圆
RectF inSideRectF = new RectF(midX-inSideRadius,midY-inSideRadius,midX+inSideRadius,midY+inSideRadius);//包裹内层圆的矩形
canvas.drawRect(outSideRectF,ringPaint);
canvas.drawRect(inSideRectF, ringPaint);
Paint arcPaint = new Paint();
arcPaint.setColor(Color.GREEN);
arcPaint.setAntiAlias(true);
//默认就是FILL,FILL的意思是显示将Paint的起点和终点直线联系后形成闭合再用指定的颜色填满后的效果,STROKE是指只显示Paint走过的线的路径
arcPaint.setStyle(Paint.Style.FILL);
//路径,可以理解为线性的路径,没有宽度概念?
Path path = new Path();
//arcTo方法只是指定弧形的路径,还没有画出来显示到canvas上
//画弧形的原理是:截取矩形所包裹的椭圆(包含圆)的一段即为弧形,
// 圆的起始角度是圆心的正右侧(0度),第二个参数是弧形开始的角度,第三个参数是弧形走过的角度,顺时针为正,逆时针为负
path.arcTo(outSideRectF, 0, 90);//外层弧形
// path.arcTo(inSideRectF, 0, 90);//内层弧形,不要这样走,这样两个path走不成弧形柱,可以用笔画一下,参考下图的图二
path.arcTo(inSideRectF, 0+90, -90);//内层弧形,第二个path应该倒着走,这样就走成了一个空的弧形柱,再用paint的颜色一填充就形成了相应颜色的实体的扇形柱,参考下图图二
// path.close();//封闭轮廓,即轮廓是闭合的,没有开口
//将path指定的弧形用画笔画到画布上
canvas.drawPath(path,arcPaint);
//一个画笔Paint画两个Path中间是不可以抬笔的,类似于汉字的连笔字
//即一个path从起点画到终点后要直接连(这个连是有痕迹的,类似连笔字,也是paint画出的内容)到下一个path的起点
弧形柱图片:
二:创建Canvas,在该画布上画圆,画矩形并在当前View上显示,ProterDuff.Mode用法
效果如下图:
代码如下:
public class TestView extends View {
private Canvas myCanvas;
private Paint ringPaint;//在当前View画空心圆用的画笔
private Paint circlePaint;
private int mRadius;
private Paint mFgroundPaint;
private RectF mRect;
public TestView(Context context) {
super(context);
initView();
}
private void initView() {
mRadius = getScreenWidth()*1/4;
ringPaint = new Paint();
circlePaint = new Paint();
mFgroundPaint = new Paint();
// ringPaint.setStyle(Paint.Style.FILL);
ringPaint.setStyle(Paint.Style.STROKE);
ringPaint.setAntiAlias(true);
// ringPaint.setARGB(255, 255 ,225, 255);
ringPaint.setColor(Color.RED);
// ringPaint.setStrokeWidth(10);
circlePaint.setStrokeWidth(mRadius);
circlePaint.setAntiAlias(true);
// circlePaint.setARGB(255, 0, 0, 0);
circlePaint.setColor(Color.BLUE);
//PorterDuff.Mode用在上面图形的Paint上,用于与下面图形的交互,两个图形用一个Paint也是相同的道理
circlePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));
mFgroundPaint.setAntiAlias(true);
// mFgroundPaint.setARGB(185, 0, 0, 0);
mFgroundPaint.setColor(Color.GREEN);
// mFgroundPaint.setStrokeWidth(10);
// mFgroundPaint.setStyle(Paint.Style.STROKE);不设置默认fill
mFgroundPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));
}
public TestView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public TestView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public TestView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initView();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// canvas.drawColor(Color.BLUE);
mRect = new RectF(0, 0, getWidth(), getHeight());
Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
myCanvas = new Canvas(bitmap);//创建Canvas要指定一个Bitmap,也可用无参的构造器之后手动指定
//在当前View的画布上画红色空心圆
canvas.drawCircle(getWidth() / 2, getHeight() / 2, getWidth() / 3, ringPaint);
myCanvas.drawRect(new RectF(getWidth() / 2, getHeight() / 2, getWidth(), getHeight()), mFgroundPaint) ;//在自己的画布上画绿色矩形
myCanvas.drawCircle(getWidth() / 2, getHeight() / 2, mRadius, circlePaint);//在自己的画布上画蓝色实心圆
//在你自己创建的画布myCanvas上画完图像后,要将myCanvas的bitmap画到当前View的canvas上才能显示出来
canvas.drawBitmap(bitmap, null, mRect, null);//第三个参数是bitmap要画到的目标矩形
//你在自己画布上画的图形添加到当前View的canvas上后会遮挡当前view的对应区域
}
private int getScreenWidth() {
WindowManager wm = (WindowManager) this.getContext().getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics m = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(m);
return m.widthPixels;
}
}
三:我想使用ProterDuff.Mode.DST_OUT实现如下图一的效果,结果实际效果却是下图二
原因是:在该例中你直接在当前View的Canvas上画图,无论你使用什么Mode,两个图的的相交部分总是黑色的,不知道是从哪里来的,要想实现Mode的正常效果,可以自己创建一个myCanvas,在该画布上画完图后再在当前View的画布上显示,如上一个例子一样。
图一
图二
错误代码:该代码运行的结果是图二,二不是我希望的图一
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float outSideRadius = getWidth()/3;//外圆半径
float inSideRadius = getWidth()/4;//内圆半径
float midX = getWidth()/2;
float midY = getHeight()/ 2;
ringPaint.reset();
ringPaint.setColor(Color.GREEN);
ringPaint.setAntiAlias(true);
canvas.drawCircle(midX, midY, outSideRadius, ringPaint);//外层圆,第一二个参数是圆心的x,y坐标,这里是View的中心,第二个参数是半径,第三个参数是Paint
ringPaint.reset();
ringPaint.setColor(Color.BLUE);
ringPaint.setAntiAlias(true);
ringPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));//取下层图片非交集部分
canvas.drawCircle(midX, midY, inSideRadius, ringPaint);//内层圆
}
四:使用saveLayer创建一个新的图层,结合ProterDuff.Mode实现上图一的效果
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//当前View的画布上使用了xfermode, color filter, or alpha时常用saveLayer方法创建一个新的图层
// 创建一个单独的图层,并放入Canvas图层栈
int saveCount = canvas.saveLayer(0, 0, getWidth(), getHeight(), null,
Canvas.ALL_SAVE_FLAG);
float outSideRadius = getWidth()/3;//外圆半径
float inSideRadius = getWidth()/4;//内圆半径
float midX = getWidth()/2;
float midY = getHeight()/ 2;
ringPaint.reset();
ringPaint.setColor(Color.GREEN);
ringPaint.setAntiAlias(true);
canvas.drawCircle(midX, midY, outSideRadius, ringPaint);
ringPaint.reset();
ringPaint.setColor(Color.BLUE);
ringPaint.setAntiAlias(true);
ringPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
canvas.drawCircle(midX, midY, inSideRadius, ringPaint);//内层圆
//将之前创建的图层从图层栈中退出,并将在该图层绘制的图片”绘制“到canvas上
canvas.restoreToCount(saveCount);
// canvas.restore();
}