android 自定义控件画板 PaintView

该代码实现了一个安卓手绘板控件PaintView,支持实时绘制、路径记录与清除功能。通过Touch事件捕捉手势,使用Path和Paint绘制平滑轨迹,采用贝塞尔曲线实现流畅笔触。用户可自由绘画,支持多条轨迹保存,并可通过clear()方法清空画板。

点击查看代码
package com.run.treadmill

import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Path
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View

//笔记  起点和路径
data class   Stroke(val  path:Path,val paint: Paint)

class PaintView :View {
    constructor(context: Context) : super(context){
        init(null)
    }
    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs){
        init(attrs)
    }
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
        context,
        attrs,
        defStyleAttr){
        init(attrs)
    }

    //轨迹集合
    private   val  strokeList=mutableListOf<Stroke>()
    private   val  currentPath=Path()
    private   val  drawPaint=Paint()

    //触摸临时坐标
    private   var  lastx=0f
    private   var  lasty=0f

    private fun init(attrs: AttributeSet?){
        resetNormalPaint()
    }
    fun   resetNormalPaint(){
        //1.画笔实例化
        drawPaint.apply {
            isAntiAlias=true
            style= Paint.Style.STROKE
            strokeWidth=8f
            color=Color.BLACK
            strokeCap=Paint.Cap.ROUND
        }
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),MeasureSpec.getSize(heightMeasureSpec))
    }

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        //2.画出所有的轨迹
        strokeList.forEach {
            canvas?.drawPath(it.path,it.paint)
        }
        //3.画出正在画的轨迹
        canvas?.drawPath(currentPath,drawPaint)
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
          val x=event.x
          val y=event.y

           //边界
          val inBounds=   x >=  0    &&  x<=  width   &&   y >0    &&   y    <=   height
          if (!inBounds) return true

          when(event.action){
              //手指按下开启新的笔迹
              MotionEvent.ACTION_DOWN -> {
                  currentPath.reset()
                  currentPath.moveTo(x,y)
                  lastx=x
                  lasty=y
              }
              //手指滑动,连续画线 贝塞尔曲线的控制点取两点中点
              MotionEvent.ACTION_MOVE -> {
                  currentPath.quadTo(lastx,lasty,(x+lastx)/2,(y+lasty)/2)
                  lastx=x
                  lasty=y
              }
              //手指抬起,保存当前笔迹到集合
              MotionEvent.ACTION_UP -> {
                  strokeList.add(Stroke(Path(currentPath),drawPaint))
                  currentPath.reset()
              }
          }
          invalidate()//刷新界面
//        return super.onTouchEvent(event)
        return true
    }
    //清空画板
    fun  clear(){
        strokeList.clear()
        currentPath.reset()
        invalidate()
    }
    //撤回上一笔
    fun undo(){
        if(strokeList.isNotEmpty()){
            strokeList.removeLast()
            invalidate()
        }
    }
    //画笔颜色
    fun setPaintColor(color:Int){
        drawPaint.color=color
    }
    //橡皮擦
    fun useEraser(){
       drawPaint.color= Color.WHITE
       drawPaint.strokeWidth=25f
    }
    fun usePen(){
        resetNormalPaint()
    }
}
posted @ 2026-10-21 11:22  xiao250  阅读(2)  评论(0)    收藏  举报