自定义View_绘制基础

原文:https://hencoder.com/ui-1-1/

绘制基础知识总结

基础知识一

public class UiView_1_1 extends View{

    public UiView_1_1(Context context) {
        super(context);
    }

    public UiView_1_1(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }


    public UiView_1_1(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
}

通过上述代码,就实现了一个自定义View的基础模版,但是为了实现自定义View,我们还需要相关的绘制,在这里我们要重写onDraw()方法

  @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
}

在这里传入了Canvas,可以理解成画布的意思,自定义View简单理解就是在屏幕的坐标系中去绘制图案的过程,在这里就设计到坐标系的知识,Android规定以屏幕的左上定点为原点向右为x轴的正向,向下为y轴的正向

有了画布以后,我们还需要一支画笔,Android提供了Paint类可供我们使用,所以我们需要在创建一支画笔就可以了

        Paint paint = new Paint();

        //设置画笔风格:填充,勾边,即填充又勾边
        paint.setStyle(Paint.Style.STROKE);

        //设置画笔的颜色
        paint.setColor(Color.BLACK);

        //设置画笔的宽度
        paint.setStrokeWidth(5);

        //设置抗锯齿开关
        paint.setAntiAlias(true);

基础知识二

既然有了坐标系,那么说我们就可以通过坐标系绘制图形了

下面是绘制基础图形:

        //画圆:注意这里的cx 和 cy 是圆心的坐标
        canvas.drawCircle(250,250,200,paint);

        //画方形:注意left是左边到y轴的距离,top是上边到x轴的距离,right是右边到y轴的距离,bottom是底边到x轴的距离
        canvas.drawRect(100, 100, 200, 200, paint);

        //画椭圆:api最小为16,当椭圆的上下边距和左右边距相等时,就为圆了
        canvas.drawOval(200,50,400,120,paint);

        //画线
        canvas.drawLine(20,20,80,80,paint);

        //设置端头的形状
        paint.setStrokeCap(Paint.Cap.ROUND);

        //绘制单个点
        canvas.drawPoint(50,50,paint);

        //绘制多个点
        float[] pts = {60,60,70,70,80,80};
        canvas.drawPoints(pts,paint);

        //绘制多条线,注意要为复数
        canvas.drawLines(pts,paint);

        //绘制圆角矩形,rx和ry代表圆角的横向半径和纵向半径
        canvas.drawRoundRect(300,300,500,500,50,50,paint);

        paint.setStyle(Paint.Style.FILL); // 填充模式

        //drawArc()是使用一个椭圆来描述弧形的。left, top, right, bottom 描述的是这个弧形所在的椭圆;
        //绘制弧形或扇形,startAngle:开始的角度,sweepAngle:滑过的角度,useCenter:是否连接圆心,
        canvas.drawArc(400,200,900,500,-110,100,true,paint);
        canvas.drawArc(400,200,900,500,20,140,false,paint);

        //注意滑过的角度,顺时针为正角度,逆时针为负角度
        paint.setStyle(Paint.Style.STROKE);
        canvas.drawArc(400,200,900,500,-180,60,false,paint);

        //将Resource中的图片文件转换为bitmap
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
        canvas.drawBitmap(bitmap,200,200,paint);

        //设置字体的大小
        paint.setTextSize(18);
        //绘制文字
        canvas.drawText("Hello World",400,400,paint);

基础知识三

在知识二中,我们介绍了基础图形的绘制,比如圆形,方形,椭圆形,扇形等图案,但是有一些组合图形是无法绘制出来的

如果我们要绘制比如❤️形,通过上面方式就无法满足了,所以,我们可以使用Android提供的Path类,使用方式:

        Path path = new Path();
        Paint paint = new Paint();
        //path的相关绘制,在这里我们在画布中传入path和paint即可
        canvas.drawPath(path,paint);
Path 方法第一类:直接描述路径
        paint.setStyle(Paint.Style.FILL);
        //使用Path对象绘制圆,其实和drawCircle一样,但是这个一般可以用来组合
        //顺时针 (CW clockwise) 和逆时针 (CCW counter-clockwise)
        path.addCircle(300,600,200,Path.Direction.CW);
        canvas.drawPath(path,paint);

        //绘制线:lineTo表示从(00)-> (100,0) rLineTo表示相对于(100,0)->(0,200)
        paint.setStyle(Paint.Style.STROKE);
        path.lineTo(100,0);
        path.rLineTo(0,200);
        canvas.drawPath(path,paint);


        //绘制弧形:forceMoveTo 参数的意思是,绘制是要「抬一下笔移动过去」,还是「直接拖着笔过去」,区别在于是否留下移动的痕迹
        paint.setStyle(Paint.Style.STROKE);
        path.lineTo(100, 100);
        //path.addArc() 只是一个直接使用了 forceMoveTo = true 的简化版 arcTo()
        path.arcTo(100, 100, 300, 300, -90, 90, true);
        canvas.drawPath(path,paint);

        //绘制封闭的图形
        //不是所有的子图形都需要使用 close() 来封闭
        // 当需要填充图形时(即 Paint.Style 为  FILL 或 FILL_AND_STROKE),Path 会自动封闭子图形
        path.moveTo(100, 100);
        path.lineTo(200, 100);
        path.lineTo(150, 150);
        path.close(); // 使用 close() 封闭子图形。等价于 path.lineTo(100, 100)
        canvas.drawPath(path,paint);
Path 方法第二类:辅助的设置或计算

关于这一类主要是围绕着:setFillType(FillType fillType)去研究

//方法中填入不同的 FillType 值,就会有不同的填充效果
Path.setFillType(Path.FillType ft) 设置填充方式

//=> FillType 的取值有四个
//-> EVEN_ODD
//-> WINDING (默认值)
// 其中后面的两个带有 INVERSE_ 前缀的,只是前两个的反色版本
//-> INVERSE_EVEN_ODD
//-> INVERSE_WINDING

EG:
        Path path1 = new Path();
        paint.setStyle(Paint.Style.FILL);
        // 顺时针 (CW clockwise) 和逆时针 (CCW counter-clockwise)
        path1.addCircle(400,800,200, Path.Direction.CW); //顺时针
        path1.addCircle(600,800,200, Path.Direction.CCW); //逆时针
        path1.setFillType(Path.FillType.WINDING);
        canvas.drawPath(path1,paint);


EVEN_ODD 和 WINDING 的原理
EVEN_ODD => even-odd rule (奇偶原则)

对于平面中的任意一点,向任意方向射出一条射线,这条射线和图形相交的次数(相交才算,相切不算哦)
如果是奇数,则这个点被认为在图形内部 => 要被涂色的区域
如果是偶数,则这个点被认为在图形外部=> 不被涂色的区域
射线穿过图形中的一条线,内外状态就发生一次切换,EVEN_ODD =>「交叉填充」的模式

这里写图片描述

WINDING => non-zero winding rule (非零环绕数原则)

它需要你图形中的所有线条都是有绘制方向的

这里写图片描述

同样是从平面中的点向任意方向射出一条射线,但计算规则不一样:
- 以0 为初始值,对于射线图形所有交点
- 遇到每个顺时针的交点把结果加 1
- 遇到每个逆时针的交点把结果减 1
- 最终把所有的交点算上
- 结果如果不是 0,则认为这个点在图形内部=> 是要被涂色的区域
- 如果是 0,则认为这个点在图形外部=> 是不被涂色的区域

这里写图片描述

  • 图形的方向:对于添加子图形类方法(如 Path.addCircle() Path.addRect())的方向,由方法的 dir 参数来控制
  • 而对于画线类的方法(如 Path.lineTo() Path.arcTo())就更简单了,线的方向就是图形的方向。

所以,完整版的 EVEN_ODD 和 WINDING 的效果应该是这样的:

这里写图片描述


你可能感兴趣的:(自定义View)