我们在onDraw里面canvas画布其实有两个坐标系并且可以新建多个画布:一个默认的坐标系是canvas坐标系,还有一个是绘图坐标系。后面的连接博客的开头就进行了详细的介绍。讲解两个坐标系的博客。画布有一个默认画布,然后我们可以自己新建画布:int layerId = canvas.saveLayer(0, 0, canvasWidth, canvasHeight, null, Canvas.ALL_SAVE_FLAG);,然后在新建的画布上进行一系列操作。比如我最后会贴的一部分代码实现一个仿支付宝刷脸的基本UI。下面我先贴一些基本代码和效果图帮助循序渐进和方便我自己以后回顾:
(1)
我们经常见到类似的数据统计比例图。可能大部分人用的是第三方的统计包。但是他的原理也是通过复杂的数学计算然后在onDraw里面进行绘制出来的。我这里贴一下我实现这个的代码。
public class View5 extends View {
private Paint mPaint;
private int width;
private int height;
public View5(Context context) {
this(context, null);
}
public View5(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public View5(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mPaint = new Paint();
mPaint.setColor(Color.GREEN);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeCap(Paint.Cap.ROUND);
// mPaint.setTextSize(60);
// mPaint.setStrokeWidth(60);//只有画圆和圆点的时候才会用这个
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
width = getWidth();
height = getHeight();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//(1)没有平移的绘图坐标系
// canvas.drawLine(0, 0, width, 0, mPaint);
// canvas.drawLine(0, 0, 0, height, mPaint);//绘制y轴
//(2)平移了的绘图坐标系,还支持旋转
// canvas.translate(200, 200);
// canvas.drawLine(0, 0, width, 0, mPaint);
// canvas.drawLine(0, 0, 0, height, mPaint);
//(3)画字
// canvas.drawText("这是没有平移的", 150, 150, mPaint);
//(4)画圆
// canvas.drawCircle(600,600,200,mPaint);//STROKE
// mPaint.setStyle(Paint.Style.FILL);
// canvas.drawCircle(600,600,200,mPaint);//FILL
// mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
// canvas.drawCircle(600,600,200,mPaint);//FILL_AND_STROKE
//(5)画线参数一和二十线的起始坐标,参数三和四是线的技术坐标
// canvas.drawLine(100, 100, width, 100, mPaint);
// canvas.drawLines(new float[]{200,200,300,200,300,300,300,400},mPaint);
//(6)画椭圆rectF的四个参数:一代表椭圆左侧的x坐标,二代表上边的y坐标,三代表右侧的x坐标,四代表底部的y坐标
// canvas.drawOval(new RectF(50, 50, 400, 700), mPaint);
//(7)画圆弧参数2和3分别是起始角度和圆弧角度,参数4代表是否显示半径连线,起始我个人觉得画圆弧的rectF是一个椭圆
//的四个参数,只不过根据起始角度和结束角把这个椭圆进行了截取
canvas.drawArc(new RectF(50, 50, 400, 400), 30, 270, true, mPaint);
// mPaint.setColor(Color.RED);
canvas.drawArc(new RectF(60, 47, 410, 397), 30, -90, true, mPaint);//往右移动距离是上移距离的三倍
//(8)画矩形
// canvas.drawRoundRect(new RectF(50,50,400,400),20,20,mPaint);//这是带圆角的矩形
// canvas.drawRect(new RectF(50,50,400,400),mPaint);//不带圆角的矩形
//(9)画圆点
// canvas.drawPoint(50,50,mPaint);
//每两个一组代表一个原点的xy坐标
// canvas.drawPoints(new float[]{100,100,200,200,300, 300, 400,400,500,500,600,600},mPaint);
//(10)画图片
// Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
// canvas.drawBitmap(bitmap, 200,300, mPaint);
}
}
上面的第7部分代码就是实现这个功能的,我这里写的只是比较简单的。实际上要经过计算然后动态赋值画出的分析图的。我上面写的十个练手小demo也可以在我上面的那个连接里面有和效果图。
(2)关于text的onDraw
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
TextPaint mPaint = new TextPaint();
mPaint.setFlags(Paint.ANTI_ALIAS_FLAG);//抗锯齿
mPaint.setTextSize(48);
int textHeigth = 48;
float translateY = textHeigth;
canvas.save();
canvas.translate(0, translateY);
mPaint.setFlags(Paint.SUBPIXEL_TEXT_FLAG);
canvas.drawText("绘制文本", 0, 0, mPaint);
canvas.restore();
canvas.save();
translateY += textHeigth * 2;
canvas.translate(0, translateY);
mPaint.setColor(Color.GREEN);
canvas.drawText("绿色文本", 0, 0, mPaint);
canvas.restore();
canvas.save();
translateY += textHeigth*2;
canvas.translate(0,translateY);
mPaint.setUnderlineText(true);
canvas.drawText("这是带下划线的text",0,0,mPaint);
canvas.restore();
}
TextView mTv = (TextView) findViewById(R.id.tv);
mTv.getPaint().setFlags(Paint.FAKE_BOLD_TEXT_FLAG);//加粗
mTv.getPaint().setFlags(Paint.ANTI_ALIAS_FLAG);//抗锯齿
mTv.getPaint().setFlags(Paint.UNDERLINE_TEXT_FLAG);//这个是设置下划线,如果跟加粗一起用,要写在加粗后面
下过如下:
(3)仿支付宝的刷脸界面:
我这里只是简约版,真实的底部应该是一个surfaceView加载相机预览界面的。
实现:我用一个帧布局进行覆盖,下面是一张图片,上面是一个自定义控件,这个自定义控件让下面的布局只能露出一个圆形的视角。重点在于这个自定义控件,代码如下:
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="300dp"
android:layout_height="300dp"
android:layout_gravity="center"
android:src="@mipmap/ic_launcher" />
<shen.da.ye.mypaintdemo.View10
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</FrameLayout>
public class View10 extends View {
public View10(Context context) {
this(context, null);
}
public View10(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public View10(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
canvas.drawARGB(0, 0, 0, 0);
//这要这句代码走了,就代表新建了一个画布
int layer = canvas.saveLayer(0, 0, canvas.getWidth(), canvas.getHeight(), null, Canvas.ALL_SAVE_FLAG);
canvas.drawARGB(255, 255, 255, 255);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));
paint.setFlags(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.FILL);
paint.setColor(getResources().getColor(R.color.testColor));
canvas.drawCircle(500, 600, 200, paint);
paint.setXfermode(null);
canvas.restoreToCount(layer);
}
}
默认的canvas的ARGB就是0,0,0,0。透明的,我上面的代码canvas.drawARGB是可以不用写的。如果你想透视底部的话,这里要么对原本的画布不进行设置RGB,要么就设置为0000,。然后新建一个画布,对这个画布进行操作,捡完画布之后对画布进行设置纯白色。然后设置xferMode(最后记得清除掉),关键是这个xfermode的设计比较复杂。我下面放一个连接,别人写的很详细。关于
PorterDuffXfermode使用及工作原理详解,最后要看懂这个porterDuffXfermo的混合规则,他最后面有一个混合规则的“公式”,我就是根据这个公式做出来的,根据公式我上面的MODE选择其实有好几种(CLEAR,
DST_OUT),只要保证我的目标原素最后变成透明就可以实现透视效果。