当前位置: 首页 > article >正文

Android图片涂鸦,Kotlin(1)

Android图片涂鸦,Kotlin(1)

 

import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Path
import android.graphics.PointF
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
import androidx.appcompat.widget.AppCompatImageView


class PaintView @JvmOverloads constructor(
    context: Context?,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : AppCompatImageView(context!!, attrs, defStyleAttr) {

    class DrawPath {
        var id: Long = 0L

        var path: Path? = null
        var paint: Paint? = null
        var points = ArrayList<PointF>()
    }

    private var mPaint: Paint? = null
    private var mPath: Path? = null

    private var mDownX = 0f
    private var mDownY = 0f
    private var mTempX = 0f
    private var mTempY = 0f

    //默认的画笔宽度
    private var mPaintWidth = 10

    //默认画笔的颜色
    private var mColor = Color.RED

    private val mDrawPathList: ArrayList<DrawPath>?
    private val mSavePathList: ArrayList<DrawPath>?

    companion object {
        var WIDTH_INCREMENT = 15
        const val TAG = "PaintView"
    }

    init {
        mDrawPathList = ArrayList()
        mSavePathList = ArrayList()
        initPaint()
    }

    private fun initPaint() {
        mPaint = Paint()

        mPaint?.color = mColor
        mPaint?.isAntiAlias = true
        mPaint?.strokeWidth = mPaintWidth.toFloat()
        mPaint?.style = Paint.Style.STROKE
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)

        if (!mDrawPathList.isNullOrEmpty()) {
            for (drawPath in mDrawPathList) {
                if (drawPath.path != null) {
                    canvas.drawPath(drawPath.path!!, drawPath.paint!!)
                }

                drawPath.points.forEach {
                    Log.d(TAG, "onDraw ${drawPath.id} ${it.x} ${it.y}")
                }
            }
        }
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                Log.d(TAG, "ACTION_DOWN ${event.x} ${event.y}")

                mDownX = event.x
                mDownY = event.y

                //每次手指点下去作为一条新路径。
                mPath = Path()
                mPath?.moveTo(mDownX, mDownY)

                val drawPath = DrawPath()
                drawPath.id = System.currentTimeMillis()
                drawPath.paint = mPaint
                drawPath.path = mPath
                mDrawPathList?.add(drawPath)

                //新一轮绘制开始,保存点。
                drawPath.points.add(PointF(event.x, event.y))

                invalidate()
                mTempX = mDownX
                mTempY = mDownY
            }

            MotionEvent.ACTION_MOVE -> {
                //Log.d(TAG, "ACTION_MOVE ${event.x} ${event.y}")

                val moveX = event.x
                val moveY = event.y

                mPath?.quadTo(mTempX, mTempY, moveX, moveY)

                mDrawPathList?.filter {
                    it.path == mPath
                }?.forEach {
                    it.points.add(PointF(event.x, event.y))
                }

                invalidate()
                mTempX = moveX
                mTempY = moveY
            }

            //每次手指抬起重置画笔,不然画笔会保存之前设置的属性会引起bug。
            MotionEvent.ACTION_UP -> {
                Log.d(TAG, "ACTION_UP ${event.x} ${event.y}")
                initPaint()
            }
        }

        return true
    }

    /**
     * 撤销。
     */
    fun undo() {
        if (mDrawPathList != null && mDrawPathList.size >= 1) {
            mSavePathList?.add(mDrawPathList[mDrawPathList.size - 1])
            mDrawPathList.removeAt(mDrawPathList.size - 1)

            invalidate()
        }
    }

    /**
     * 反撤销,重新生效。
     */
    fun reundo() {
        if (!mSavePathList.isNullOrEmpty()) {
            mDrawPathList?.add(mSavePathList[mSavePathList.size - 1])
            mSavePathList.removeAt(mSavePathList.size - 1)

            invalidate()
        }
    }

    /**
     * 画笔颜色。
     *
     * @param color
     */
    fun setPaintColor(color: Int) {
        this.mColor = color
        mPaint?.color = this.mColor
    }

    /**
     * 画笔的宽度。
     */
    fun setPaintWidth(size: Int) {
        mPaintWidth += size
        mPaint?.strokeWidth = mPaintWidth.toFloat()
    }

    /**
     * 放大,改变画笔的宽度。线条变粗。
     */
    fun enlargePaintWidth() {
        mPaintWidth += WIDTH_INCREMENT
        mPaint?.strokeWidth = mPaintWidth.toFloat()
    }

    fun getDrawPath(): ArrayList<DrawPath>? {
        return mDrawPathList
    }
}

 

 

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.pkg1115.PaintView
        android:id="@+id/paint_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="fitCenter"
        android:src="@mipmap/p1"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <RelativeLayout
        android:id="@+id/rl_left"
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <Button
            android:id="@+id/btn_undo"
            android:layout_width="match_parent"
            android:layout_height="60dp"
            android:text="撤销" />

        <Button
            android:id="@+id/btn_red"
            android:layout_width="match_parent"
            android:layout_height="60dp"
            android:layout_below="@id/btn_undo"
            android:text="红色" />

        <Button
            android:id="@+id/btn_green"
            android:layout_width="match_parent"
            android:layout_height="60dp"
            android:layout_below="@id/btn_red"
            android:text="绿色" />

        <Button
            android:id="@+id/btn_enlarge"
            android:layout_width="match_parent"
            android:layout_height="60dp"
            android:layout_below="@id/btn_green"
            android:text="放大" />
    </RelativeLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

 

 

 

 

 

Android图形图像处理:添加涂鸦文字_android 在图片上涂鸦-CSDN博客文章浏览阅读2k次。先看运行效果: 关键的PaintView:package com.zhangphil;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graph..._android 在图片上涂鸦https://blog.csdn.net/zhangphil/article/details/87810653

 


http://www.kler.cn/news/133950.html

相关文章:

  • k8s_base
  • 物联网AI MicroPython学习之语法 I2C总线
  • 基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖(五)
  • 随机过程-张灏
  • java springboot 在测试类中声明临时Bean对象
  • 15. Spring源码篇之获取方法参数名
  • Qt按钮大全续集(QCommandLinkButton和QDialogButtonBox )
  • 【小黑送书—第六期】>>AI时代,程序员如何应对挑战——《AI时代系列书籍》
  • Ubuntu 20.04 LTS设置系统虚拟内存大小
  • 4 redis的HyperLogLog入门原理
  • Kubernetes(k8s)进阶
  • 大数据-之LibrA数据库系统告警处理(ALM-12055 证书文件即将过期)
  • 2023年第九届数维杯国际大学生数学建模挑战赛
  • 2023亚太地区数学建模竞赛A题B题C题思路+模型+代码
  • 【Go入门】 Go如何使得Web工作
  • CAPL实现CRC8的几种方式
  • 国产化项目改造:使用达梦数据库和东方通组件部署,前后端分离框架
  • C语言结构体
  • [webservice] springboot整合cxf
  • PMCW体制雷达系列文章(4) – PMCW雷达之抗干扰
  • FPGA设计时序约束八、others类约束之Set_Case_Analysis
  • 2-(脏读,不可重复读,幻读 ,mysql5.7以后默认隔离级别)、( 什么是qps,tps,并发量,pv,uv)、(什么是接口幂等性问题,如何解决?)
  • 记录一个困难
  • Nginx 核心配置文 nginx.conf介绍
  • 一文讲明 Spring 的使用 【全网超详细教程】
  • python-opencv 培训课程笔记(2)
  • MatrixOne 支持多样化生态工具
  • 线性变换功能块S_RTI工程上的主要应用
  • 【2021集创赛】基于arm Cortex-M3处理器与深度学习加速器的实时人脸口罩检测 SoC
  • DHCP配置命令