Android图片涂鸦,Kotlin(1)

Android图片涂鸦,Kotlin(1)

Kotlin 复制代码
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 复制代码
<?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

相关推荐
REDcker1 小时前
Android WebView 版本升级方案详解
android·音视频·实时音视频·webview·js·编解码
麦兜*1 小时前
【springboot】图文详解Spring Boot自动配置原理:为什么@SpringBootApplication是核心?
android·java·spring boot·spring·spring cloud·tomcat
le1616161 小时前
Android 依赖种类及区别:远程仓库依赖、打包依赖、模块依赖、本地仓库依赖
android
lxysbly1 小时前
psp模拟器安卓版带金手指
android
云上凯歌2 小时前
02 Spring Boot企业级配置详解
android·spring boot·后端
hqiangtai2 小时前
Android 高级专家技术能力图谱
android·职场和发展
aqi003 小时前
FFmpeg开发笔记(九十七)国产的开源视频剪辑工具AndroidVideoEditor
android·ffmpeg·音视频·直播·流媒体
stevenzqzq3 小时前
Android Koin 注入入门教程
android·kotlin
zFox3 小时前
三、Kotlin协程+异步加载+Loading状态
kotlin·android jetpack·协程
炼金术3 小时前
SkyPlayer v1.1.0 - 在线视频播放功能更新
android·ffmpeg