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

相关推荐
每次的天空39 分钟前
Android-自定义View的实战学习总结
android·学习·kotlin·音视频
恋猫de小郭1 小时前
Flutter Widget Preview 功能已合并到 master,提前在体验毛坯的预览支持
android·flutter·ios
断剑重铸之日2 小时前
Android自定义相机开发(类似OCR扫描相机)
android
随心最为安2 小时前
Android Library Maven 发布完整流程指南
android
岁月玲珑2 小时前
【使用Android Studio调试手机app时候手机老掉线问题】
android·ide·android studio
还鮟6 小时前
CTF Web的数组巧用
android
小蜜蜂嗡嗡8 小时前
Android Studio flutter项目运行、打包时间太长
android·flutter·android studio
aqi008 小时前
FFmpeg开发笔记(七十一)使用国产的QPlayer2实现双播放器观看视频
android·ffmpeg·音视频·流媒体
zhangphil9 小时前
Android理解onTrimMemory中ComponentCallbacks2的内存警戒水位线值
android
你过来啊你10 小时前
Android View的绘制原理详解
android