Android 自定义控件


如果是一个自定义控件则需要派生自 View 或其子类如果是一个自定义的容器则需要派生自 ViewGroup 或其子类

Canvas 画布类

凡是要画出成品的东西比如圆形、矩形、文字等都调用 Canvas 类里的函数生成

      画布背景

drawColor(int color);			//必须是8位16进制OxAARRGGBB否则无作用
drawRGB(int r, int g, int b);	//可10进制 0~255或16进制 OxOO~OxFF
drawARGB(int a, int r, int g, int b);

      点

// xy绘制点的坐标
drawPoint(float x, float y, Paint);
drawPoints(float[] pts, Paint);		//两个一组组成坐标pts数组长度必须是二的倍数例如[x0,y0, x1,y1, x2,y2 …]
//offset 在pts数组中取数值的偏移量第一个数值为pts[offset]
//count 在pts数组总共取多少个数值offset+count<=pts.length
drawPoints(float[] pts, int offset, int count, Paint); // 即切割数组从pts[offset]到pts[offset+count-1]count必须是2的倍数

      线

// startXstartY起始点坐标endXendY终点坐标
drawLine(float startX, float startY, float endX, float endY, Paint);
drawLines(float[] pts, Paint);		//四个一组组成坐标pts数组长度必须是四的倍数例如[startX0,startY0,endX0,endY0, startX1,startY1,endX1,endY1 …]
drawLines(float[] pts, int offset, int count, Paint);	// 同上count必须是四的倍数

      矩形

drawRect(float left, float top, float right, float bottom, Paint);	//可以理解为矩形左上角坐标(left,top) 和右下角坐标(right,bottom)
drawRect(Rect rect, Paint);
drawRect(RectF rectF, Paint);

// 绘制圆角矩形rx是横向ry是纵向单位px数值越大角越圆
drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, Paint);
drawRoundRect(RectF rectF, float rx, float ry, Paint);
// 构造方法一
Rect rect = new Rect(10, 10, 100, 100); 
// 构造方法二
Rect rect = new Rect(); 
rect.set(10, 10, 100, 100) ;
// 构造方法三:
Rect r = new Rect(10, 10, 100, 100);
Rect rect = new Rect(r); 
// RectF 类与上面相同只是精度不同精度为float

      椭圆

drawOval(float left, float top, float right, float bottom, Paint); //可以理解为一个矩形在矩形之中画一个椭圆
drawOval(RectF rect, Paint);

在这里插入图片描述

      圆

// cx和cy是圆的中心radius是圆的半径
drawCircle(float cx, float cy, float radius, Paint)

      弧形

// startAngle是圆弧开始角度sweepAngle是圆弧经过的角度useCenter设置圆弧是否经过中心
// 可以理解为只画出部分椭圆
drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, Paint);
drawArc(RectF, float startAngle, float sweepAngle, boolean useCenter, Paint);

在这里插入图片描述

	override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        var paint: Paint = Paint()
        paint.setColor(Color.RED)
        paint.style = Paint.Style.STROKE
        paint.strokeWidth = 10f;

        canvas?.drawArc(10f, 10f, 200f, 100f, 0f, 90f, true, paint)		//经过圆心
        canvas?.drawArc(10f, 150f, 200f, 240f, 45f, 90f, false, paint)	//不经过圆心
        
		paint.style = Paint.Style.FILL
        canvas?.drawArc(200f, 10f, 390f, 100f, 0f, 90f, true, paint)	//经过圆心
        canvas?.drawArc(200f, 150f, 390f, 240f, 45f, 90f, false, paint)	//不经过圆心
    }

      路径

drawPath(Path path, Paint)

Path 类
    1.常用方法

moveTo(float xl , float yl)		//移动画笔到画布对应的坐标
rMoveTo(float dx , float dy)	//相对于上一个画笔的最后一点移动画笔等价于上一个画笔位置的偏移量如果没有上一个画笔位置则将其视为moveTo()

lineTo(float x, float y)		//从当前画笔位置绘制一条直线到画布对应坐标x,y
rLineTo(float dx, float dy)		//与lineTo相同相对于当前画笔位置等价于当前画笔位置为(x0,y0)lineTo(x0+dx, y0+dy)

arcTo(RectF oval, float startAngle, float sweepAngle)	//绘制弧线参数与画弧线相同
arcTo(RectF oval, float startAngle, float sweepAngle, boolean forceMoveTo)	// forceMoveTo 是否强制地将弧的起始点作为绘制起始
arcTo(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean forceMoveTo) //与上面一个方法相同

close(); // 使用 close() 封闭子图形

//Path.Direction.CW 顺时针    Path.Direction.CCW 逆时针
addPath()		//添加另一个路径
addRect()		//添加一个矩形
addRoundRect()	//添加一个圆角矩形
addCircle()		//添加一个圆形
addOval()		//添加一个椭圆
addArc()		//添加一个弧线

quadTo(float x1, float y1, float x2, float y2)							//画二次贝塞尔曲线两个点的坐标值(x1,y1) (x2,y2)
rQuadTo(float dx1, float dy1, float dx2, float dy2)						//相对坐标
cubicTo(float x1, float y1, float x2, float y2, float x3, float y3) 	//画三次贝塞尔曲线
rCubicTo(float dx1, float dy1, float dx2, float dy2, float dx3, float dy3)	//相对坐标

在这里插入图片描述

	override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        var paint: Paint = Paint()
        paint.setColor(Color.RED)
        paint.style = Paint.Style.STROKE
        paint.strokeWidth = 10f;

        var path: Path = Path()
        path.moveTo(10f, 10f)

		// forceMoveTo = true
        path.addArc(30f, 30f, 200f, 200f, -225f, 225f);
        path.arcTo(400f, 200f, 600f, 400f, -180f, 225f, true);

		// forceMoveTo = false
        path.addArc(30f, 230f, 200f, 400f, -225f, 225f);
        path.arcTo(400f, 400f, 600f, 600f, -180f, 225f, false);
        
        canvas?.drawPath(path,paint)
    }

    2.FillType属性

setFillType(Path.FillType);

有四种类型 Path.FillType.WINDINGPath.FillType.EVEN_ODDPath.FillType.INVERSE_WINDINGPath.FillType.INVERSE_EVEN_ODD
在这里插入图片描述

	override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        paint: Paint = Paint()
        paint.setColor(Color.RED)
        paint.style = Paint.Style.FILL
        paint.strokeWidth = 1f;

		//Path.Direction.CW 顺时针    Path.Direction.CCW 逆时针 
        var path: Path = Path()
        path.addCircle(200f, 200f, 100f, Path.Direction.CW)		//方向相同
        path.addCircle(300f, 200f, 100f, Path.Direction.CW)
        path.fillType = Path.FillType.WINDING
        canvas?.drawPath(path, paint)

        var path2: Path = Path()
        path2.addCircle(200f, 460f, 100f, Path.Direction.CW)	//方向不同
        path2.addCircle(300f, 460f, 100f, Path.Direction.CCW)
        path2.fillType = Path.FillType.WINDING
        canvas?.drawPath(path2, paint)
    }

    2.op方法

op(Path path1, Path path2, Op op)
/** Op 的值有
Path.Op.DIFFERENCE 				path1 减去 path2 的区域
Path.Op.INTERSECT				留下 path2 和 path2 相交的区域
Path.Op.UNION					path1 和 path2 的并集
Path.Op.XOR						包含 path1 和 path2 但不包含相交的部分
Path.Op.REVERSE_DIFFERENCE		path2 减去 path1 的区域
**/

在这里插入图片描述

	override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        paint: Paint = Paint()
        paint.setColor(Color.RED)
        paint.style = Paint.Style.FILL
        paint.strokeWidth = 1f;

        var path: Path = Path()
        path.addCircle(200f, 200f, 100f, Path.Direction.CW)

        var path2: Path = Path()
        path2.addCircle(300f, 200f, 100f, Path.Direction.CW)

        path.op(path2, Path.Op.REVERSE_DIFFERENCE)
        canvas?.drawPath(path, paint)
//        canvas?.drawPath(path2, paint)  //注意不要在绘制path2了op方法已经将path2的图像合并到path中了
    }

      字符

drawText(String text, float x, float y, Paint);
drawText(String text, int start, int end, float x, float y, Paint);		//只画出字符串text的部分text[start]~text[end]
drawText(CharSequence text, int start, int end, float x, float y, Paint);//同上
drawText(char[] text, int index, int count, float x, float y, Paint);	//同样之画出部分count不是结束位置而是取出多少个字符index+count<=text.length

// hOffset与路径起始点的水平偏移距离vOffset与路径中心点的垂直偏移量 
drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint);
drawTextOnPath(char[] text, int index, int count, Path path, float hOffset, float vOffset, Paint);

注意x的位置跟文字对齐方式有关通过设置画笔的 setTextAlign() 方法指定默认为left
           y的位置是基准线Baseline
在这里插入图片描述
通过这些来获取这些线的 y 的值

		Paint.FontMetrics fontMetrics=paint.getFontMetrics();
        // 注意获取的值都是相对于Baseline的偏移量
        fontMetrics.top			//注意是负数是相对于Baseline的偏移量
        fontMetrics.ascent
        fontMetrics.descent
        fontMetrics.bottom

在这里插入图片描述
由上图清晰得出基线相对于文字中心点的偏移量为

//偏移量
float distance = (fontMetrics.bottom - fontMetrics.top)/2 - fontMetrics.bottom;

在这里插入图片描述

	override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        paint = Paint()
        paint.setColor(Color.RED)
        paint.style = Paint.Style.FILL
        paint.strokeWidth = 1f;
        paint.textSize = 36f

        var path: Path = Path()
        path.moveTo(10f, 10f)
        path.lineTo(300f, 300f)

        canvas?.drawTextOnPath("Hello zwt!Hello zwt!", path, 0f ,0f ,paint)
        canvas?.drawTextOnPath("Hello zwt!Hello zwt!", path, 110f ,60f ,paint)

        paint.color = Color.YELLOW
        paint.style = Paint.Style.STROKE
        paint.strokeWidth = 1f;
        canvas?.drawPath(path ,paint)	//画出路径

        var path1: Path = Path()
        path1.arcTo(400f, 400f, 600f, 600f, 180f, 180f, false);

        canvas?.drawPath(path1 ,paint)	//画出路径

        paint.color = Color.RED
        paint.style = Paint.Style.FILL
        canvas?.drawTextOnPath("Hello zwt!Hello zwt!", path1, 0f ,-60f ,paint)
    }

      对画布裁剪及变形

save()		//将当前Canvas状态进行保存会有一个int类型返回值
restore()	//将Canvas还原成最近的一个save()的状态
restoreToCount(int saveCount)	//将Canvas还原成某一个特定的save()状态 saveCount为调用save()所返回的数值
//平移
translate(float dx, float dy)
//旋转
rotate(float degrees) //以(0, 0)为原点
rotate(float degrees, float px, float py) //以(px, py)为原点
//斜切
skew(float sx, float sy) //sxsy倾斜角度的tan值
//缩放
scale(float sx, float sy)
scale(float sx, float sy, float px, float py)
//裁剪
clipPath(Path path)					// DIFFERENCE
clipPath(Path path, Region.Op op) 	// 已弃用功能与Path的op方法相同
clipOutPath(Path path)				//INTERSECT

在这里插入图片描述

	override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        paint: Paint = Paint()
        paint.setColor(Color.RED)
        paint.style = Paint.Style.FILL
        paint.strokeWidth = 1f;

        /*****************平移********************/
        canvas?.drawRect(10f,10f, 500f, 300f, paint)
        canvas?.translate(100f, 100f)
        paint.setARGB(180, 255, 255, 0)	//设置为黄色透明
        canvas?.drawRect(10f,10f, 500f, 300f, paint)


        /*****************旋转********************/
//        canvas?.save()      // 保存画布状态
//        canvas?.drawRect(10f,10f, 500f, 300f, paint)
//
//        canvas?.rotate(45f) // 以00点旋转45°
//        paint.setARGB(180, 255, 255, 0)
//        canvas?.drawRect(10f,10f, 500f, 300f, paint)
//
//        canvas?.restore()   //恢复画布状态
//        canvas?.rotate(90f, 250f, 150f) // 以250150点旋转90°
//        paint.setARGB(180, 255, 0, 255)
//        canvas?.drawRect(10f,10f, 500f, 300f, paint)


        /*****************斜切********************/
//        canvas?.save()      // 保存画布状态
//        canvas?.drawRect(10f,10f, 500f, 300f, paint)
//
//        canvas?.skew(1f, 0f)    // tan45° = 1即斜切45°
//        paint.setARGB(180, 255, 255, 0)
//        canvas?.drawRect(10f,10f, 500f, 300f, paint)
//
//        canvas?.restore()   //恢复画布状态
//        canvas?.skew(0f, 1f)
//        paint.setARGB(180, 255, 0, 255)
//        canvas?.drawRect(10f,10f, 500f, 300f, paint)


        /*****************缩放********************/
//        canvas?.drawRect(10f,10f, 500f, 300f, paint)
//
//        canvas?.scale(0.5f, 2f); // 宽度变为原来的一半高度变为两倍
//        paint.setARGB(180, 255, 255, 0)
//        canvas?.drawRect(10f,10f, 500f, 300f, paint)
    }

Paint 画笔类

凡是跟画笔相关的设置比如画笔的大小、粗细、颜色、透明度、字体样式等

      常用方法

setColor(int)设置画笔的颜色
setAlpha(int)设置画笔的透明度
setARGB(int a, int r, int g, int b)设置画笔的颜色a代表透明度rgb代表颜色值
setStrokeWidth(int)设置画笔的线宽
setAntiAlias(boolean)设置是否使用抗锯齿功能设置后会平滑一些但绘制速度会变慢
setDither(boolean)设定是否使用图像抖动处理设置后图像更加清晰但绘制速度会变慢
setStyle(Style)设置画笔的风格Style.FILL 实心 Style.STROKE 空心 Style.FILL_AND_STROKE 同时显示实心和空心
在这里插入图片描述

	override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        var paint: Paint = Paint()
        paint.setColor(Color.RED)
        paint.strokeWidth = 50f;

        paint.style = Paint.Style.STROKE
        canvas?.drawCircle(150f, 300f, 75f, paint);
        paint.style = Paint.Style.FILL
        canvas?.drawCircle(350f, 300f, 75f, paint);
        paint.style = Paint.Style.FILL_AND_STROKE
        canvas?.drawCircle(550f, 300f, 75f, paint);

        paint.style = Paint.Style.FILL
        canvas?.drawCircle(800f, 300f, 75f, paint)
        paint.setARGB(128, 39, 117, 172)	//约50%的透明度
        paint.style = Paint.Style.STROKE
        canvas?.drawCircle(800f, 300f, 75f, paint)
    }

      图形线条相关

setStrokeCap(Cap)设置画笔的线帽Paint.Cap.BUTT无线帽、Paint.Join.ROUND圆形、Paint.Join.BEVEL方形
setStrokeJoin(Join)设置连接Paint.Join.MITER锐角、Paint.Join.ROUND圆弧、Paint.Join.BEVEL直线
在这里插入图片描述

	override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        paint: Paint = Paint()
        paint.setColor(Color.RED)
        paint.style = Paint.Style.STROKE
        paint.strokeWidth = 50f;

        paint.setStrokeCap(Paint.Cap.BUTT);
        canvas?.drawLine(100f, 100f, 500f, 100f, paint);

        paint.setStrokeCap(Paint.Cap.ROUND);
        canvas?.drawLine(100f, 200f, 500f, 200f, paint);

        paint.setStrokeCap(Paint.Cap.SQUARE);
        canvas?.drawLine(100f, 300f, 500f, 300f, paint);

    }

在这里插入图片描述

	override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        paint: Paint = Paint()
        paint.setColor(Color.RED)
        paint.style = Paint.Style.STROKE
        paint.strokeWidth = 50f;

        var path: Path = Path()
        path.moveTo(100f,100f)
        path.rLineTo(500f,0f)
        path.rLineTo(-200f,200f)

		paint.strokeJoin = Paint.Join.MITER
//		paint.strokeJoin = Paint.Join.ROUND
//      paint.strokeJoin = Paint.Join.BEVEL
        canvas?.drawPath(path, paint)
    }

      字符相关

setTextSize(int)设置字体大小
setFakeBoldText(boolean)设置文本仿粗体
setUnderlineText(boolean)设置文字的下划线
setTextSkewX(float)设置斜体字值为负右倾值为正左倾
setStrikeThruText(boolean)设置文本删除线
setTextScaleX(float)文本沿X轴水平缩放默认值为1
setLetterSpacing(float)设置行的间距
setShadowLayer(float radius, float dx, float dy, int shadowColor)设置阴影效果radius为阴影角度dx和dy为阴影在x轴和y轴上的距离color为阴影的颜色
setTypeface(Typeface)设置文本字体样式
setTextAlign(Paint.Align)设置字体方向对其方式Paint.Align.LEFTPaint.Align.RIGHTPaint.Align.CENTER

       Path设置样式

setPathEffect(PathEffect)设置Path样式
      CornerPathEffect(float radius)圆滑radius转折角度越大越圆滑
      DashPathEffect(float intervals[], float phase)虚线intervals[]指定虚实线长度phase指定偏移量
      PathDashPathEffect(Path shape, float advance, float phase, Style style)自定义效果shape定义路径填充的样式advance是每个图形之间的间距phase指定偏移量style分为ROTATE、MORPH和TRANSLATE
      DiscretePathEffect(float segmentLength, float deviation)打散效果segmentLength指定最大的段长deviation则为绘制时的偏离量
在这里插入图片描述

	override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        paint: Paint = Paint()
        paint.setColor(Color.RED)
        paint.style = Paint.Style.STROKE
        paint.strokeWidth = 3f;

        var mPhase: Float = 5f //偏移量

        var path: Path = Path()
        path.moveTo(100f,100f)
        path.rLineTo(100f,100f)
        path.rLineTo(200f,-50f)
        path.rLineTo(50f,20f)
        path.rLineTo(100f,-60f)
        path.rLineTo(100f,80f)

        paint.pathEffect = null
        canvas?.drawPath(path, paint)

        var path1: Path = Path()
        path1.moveTo(100f,200f)
        path1.rLineTo(100f,100f)
        path1.rLineTo(200f,-50f)
        path1.rLineTo(50f,20f)
        path1.rLineTo(100f,-60f)
        path1.rLineTo(100f,80f)
        paint.pathEffect = CornerPathEffect(25f)  //圆滑
        canvas?.drawPath(path1, paint)

        var path2: Path = Path()
        path2.moveTo(100f,300f)
        path2.rLineTo(100f,100f)
        path2.rLineTo(200f,-50f)
        path2.rLineTo(50f,20f)
        path2.rLineTo(100f,-60f)
        path2.rLineTo(100f,80f)
        paint.pathEffect = DashPathEffect(floatArrayOf(20f, 5f, 10f, 5f), mPhase)  //圆滑
        canvas?.drawPath(path2, paint)

        var path3: Path = Path()
        path3.moveTo(100f,400f)
        path3.rLineTo(100f,100f)
        path3.rLineTo(200f,-50f)
        path3.rLineTo(50f,20f)
        path3.rLineTo(100f,-60f)
        path3.rLineTo(100f,80f)
        var shape: Path = Path()
        shape.addCircle(0f, 0f, 3f, Path.Direction.CW);
        paint.pathEffect = PathDashPathEffect(shape, 12f, mPhase, PathDashPathEffect.Style.ROTATE)
        canvas?.drawPath(path3, paint)

        var path4: Path = Path()
        path4.moveTo(100f,500f)
        path4.rLineTo(100f,100f)
        path4.rLineTo(200f,-50f)
        path4.rLineTo(50f,20f)
        path4.rLineTo(100f,-60f)
        path4.rLineTo(100f,80f)
        paint.pathEffect = DiscretePathEffect(5.0f, mPhase)
        canvas?.drawPath(path4, paint)
    }
阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: android