日々是好日~every day is a good day~

日常の中の非日常の備忘録

【Android Studio】手書きメモアプリ 3回目

今日は手書きメモアプリの描画するクラスCustomSurfaceViewを作成します


CustomSurfaceView.ktを編集していきます

import android.content.Context
import android.graphics.Bitmap
import android.graphics.Bitmap.CompressFormat
import android.graphics.Bitmap.createBitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Path
import android.graphics.PixelFormat
import android.graphics.Point
import android.graphics.PorterDuff
import android.view.MotionEvent
import android.view.SurfaceHolder
import android.view.SurfaceView
import android.view.WindowManager
import android.widget.Toast
import java.io.File
import java.io.FileNotFoundException
import java.io.FileOutputStream
import java.io.IOException
import java.util.Calendar

class CustomSurfaceView: SurfaceView, SurfaceHolder.Callback {

    private var surfaceHolder: SurfaceHolder? = null
    private var paint: Paint? = null
    private var path: Path? = null
    var color: Int = 0
    val strokewidth:Float = 10f
    var prevBitmap: Bitmap? = null     //保存用ビットマップ
    private var prevCanvas: Canvas? = null     //保存用キャンバス
    private var canvas: Canvas? = null     //表示用キャンバス
    private  val pathList = ArrayList<Path>()    //描画順のパスリスト
    private  val colorList = ArrayList<Int>()    //描画順のカラーリスト
    private val repathList = ArrayDeque<Path>()  //redo用パスリスト
    private  val recolorList = ArrayList<Int>()  //redo用カラーリスト

    private var width: Int = 0
    private var height: Int = 0

    constructor(context: Context, surfaceView: SurfaceView) : super(context) {
        surfaceHolder = surfaceView.holder

        // ディスプレイの高さ、幅を取得
        val size = Point().also {
            (context.getSystemService(Context.WINDOW_SERVICE) as WindowManager).defaultDisplay.apply {
                getSize(
                    it
                )
            }
        }

        // surfaceViewのサイズ
        width = size.x
        height = size.y

        // 背景を透過させ、一番上に表示
        surfaceHolder!!.setFormat(PixelFormat.TRANSPARENT)
        surfaceView.setZOrderOnTop(true)

        // コールバック
        surfaceHolder!!.addCallback(this)

        // ペイントの設定
        paint = Paint()
        color = Color.BLACK
        paint!!.color = color
        paint!!.strokeWidth = strokewidth
        paint!!.style = Paint.Style.STROKE
        paint!!.strokeCap = Paint.Cap.ROUND
        paint!!.isAntiAlias = true
    }

    // pathクラスの情報と線の色を保存する
    data class pathCplor(
        var path: Path,
        var color: Int
    )

    override fun surfaceCreated(p0: SurfaceHolder) {
        Initialize()   // ビットマップ,キャンバス初期化
    }

    override fun surfaceChanged(p0: SurfaceHolder, format: Int, width: Int, height: Int) {

    }

    override fun surfaceDestroyed(p0: SurfaceHolder) {
        prevBitmap!!.recycle()   // ビットマップをリサイクル
    }

    // ビットマップ,キャンバス初期化
    private fun Initialize() {
        if(prevBitmap == null){
            prevBitmap = createBitmap(width, height, Bitmap.Config.ARGB_8888)
        }
        if (prevCanvas == null) {
            prevCanvas = Canvas(prevBitmap!!)
        }
        prevCanvas!!.drawColor(0, PorterDuff.Mode.CLEAR)
    }

    private fun Draw(pathCplor: pathCplor) {
        canvas = Canvas()
        // ロックしてキャンバスを取得
        canvas = surfaceHolder!!.lockCanvas()
        // キャンバスのクリア
        canvas!!.drawColor(0, PorterDuff.Mode.CLEAR)
        // 前回のビットマップをキャンバスに描画
        canvas!!.drawBitmap(prevBitmap!!, 0F, 0F, null)
        // pathを描画
        paint!!.color = pathCplor.color
        paint!!.strokeWidth = strokewidth
        canvas!!.drawPath(pathCplor.path, paint!!)
        // ロックを解除
        surfaceHolder!!.unlockCanvasAndPost(canvas)
    }

    // 画面をタッチしたとき
    fun onTouch(event: MotionEvent) : Boolean{
        when (event.action) {
            MotionEvent.ACTION_DOWN -> TouchDown(event.x, event.y)
            MotionEvent.ACTION_MOVE -> TouchMove(event.x, event.y)
            MotionEvent.ACTION_UP -> TouchUp(event.x, event.y)
        }
        return true
    }

    // ACTION_DOWN 時の処理
    private fun TouchDown(x: Float, y: Float) {
        path = Path()
        path!!.moveTo(x, y)
    }

    // ACTION_MOVE 時の処理
    private fun TouchMove(x: Float, y: Float) {
        // 線を書く
        path!!.lineTo(x, y)
        Draw(pathCplor(path!!,paint!!.color))
    }

    // ACTION_UP 時の処理
    private fun TouchUp(x: Float, y: Float) {
        // 線を書く
        path!!.lineTo(x, y)
        Draw(pathCplor(path!!,paint!!.color))
        // 保存用キャンバスを描画
        prevCanvas!!.drawPath(path!!, paint!!)
        // Listにパス,色を追加しredoスタックを空にする
        pathList.add(path!!)
        colorList.add(paint!!.color)
        repathList.clear()
        recolorList.clear()
    }

    // カラーチェンジメソッド
    fun ChangeColor(colorSelected: String) {
      
    }

    // clearメソッド(初期化とキャンバスクリア)
    fun Clear() {

    }

    // undoメソッド
    fun Undo() {

    }

    // redoメソッド
    fun Redo() {

    }

    // saveメソッド
    fun Save(){

    }
}

これでとりあえず手書きのラインを描画することはできるようになりました

明日は各ボタンをタップした時の処理を編集して仕上げたいと思います