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);
      点
// (x,y)绘制点的坐标
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的倍数
      线
// (startX,startY)起始点坐标,(endX,endY)终点坐标
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) //sx,sy倾斜角度的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) // 以(0,0)点旋转45°
//        paint.setARGB(180, 255, 255, 0)
//        canvas?.drawRect(10f,10f, 500f, 300f, paint)
//
//        canvas?.restore()   //恢复画布状态
//        canvas?.rotate(90f, 250f, 150f) // 以(250,150)点旋转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代表透明度,r,g,b代表颜色值
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)
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值