iT邦幫忙

0

【Android studio】【kotlin】 canvas 的儲存方法?

不知道大家有沒有玩過Gartic.io(你畫我猜)這款遊戲,我希望能做出類似的遊戲,計畫透過firebase做連線,目前已完成canvas,但卡在如何將canvas中的paths儲存至雲端,原本是打算使用Gson將path轉換成json後再上傳,但轉換的結果似乎與預期不符,轉換結果如下:
https://ithelp.ithome.com.tw/upload/images/20210806/20136380A6tmaPRERy.png
另外canvas的code如下:

private const val STROKE_WIDTH = 12f // has to be float

class CanvasView(c : Context,attrs: AttributeSet? = null) : View(c,attrs) {


    private lateinit var extraCanvas: Canvas
    private lateinit var extraBitmap: Bitmap
    private val backgroundColor = ResourcesCompat.getColor(resources, R.color.white, null)
    var drawColor = ResourcesCompat.getColor(resources, R.color.black, null)
    val paint = Paint().apply {
        color = drawColor
        // Smooths out edges of what is drawn without affecting shape.
        isAntiAlias = true
        // Dithering affects how colors with higher-precision than the device are down-sampled.
        isDither = true
        style = Paint.Style.STROKE // default: FILL
        strokeJoin = Paint.Join.ROUND // default: MITER
        strokeCap = Paint.Cap.ROUND // default: BUTT
        strokeWidth = STROKE_WIDTH // default: Hairline-width (really thin)
    }
    val paintFrame = Paint().apply {
        color = drawColor
        // Smooths out edges of what is drawn without affecting shape.
        isAntiAlias = true
        // Dithering affects how colors with higher-precision than the device are down-sampled.
        isDither = true
        style = Paint.Style.STROKE // default: FILL
        strokeJoin = Paint.Join.ROUND // default: MITER
        strokeCap = Paint.Cap.ROUND // default: BUTT
        strokeWidth = STROKE_WIDTH // default: Hairline-width (really thin)
    }
    private var path = Path()
    private var motionTouchEventX = 0f
    private var motionTouchEventY = 0f
    private var currentX = 0f
    private var currentY = 0f
    private val touchTolerance = ViewConfiguration.get(context).scaledTouchSlop
    private lateinit var frame: Rect
    //redo
    private val paths = ArrayList<Path>()
    private val undonePaths = ArrayList<Path>()
    private  val colors = ArrayList<Int>()
    private  val undoColors = ArrayList<Int>()

    override fun onSizeChanged(width: Int, height: Int, oldWidth: Int, oldHeight: Int) {
        super.onSizeChanged(width, height, oldWidth, oldHeight)
        if (::extraBitmap.isInitialized) extraBitmap.recycle()
        extraBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
        extraCanvas = Canvas(extraBitmap)
        extraCanvas.drawColor(backgroundColor)
        val inset = 20
        frame = Rect(inset, inset, width - inset, height - inset)
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        var i = 0
        val resentColor = paint.color
        for (Path in paths) {
            paint.color = colors[i]
            i ++
            canvas.drawPath(Path, paint)
        }
        paint.color = resentColor
        canvas.drawPath(path, paint)
        canvas.drawRect(frame, paintFrame)
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        motionTouchEventX = event.x
        motionTouchEventY = event.y
        when (event.action) {
            MotionEvent.ACTION_DOWN -> touchStart()
            MotionEvent.ACTION_MOVE -> touchMove()
            MotionEvent.ACTION_UP -> touchUp()
        }
        return true
    }

    private fun touchStart() {
        undonePaths.clear()
        undoColors.clear()
        path.reset()
        path.moveTo(motionTouchEventX, motionTouchEventY)
        currentX = motionTouchEventX
        currentY = motionTouchEventY
    }

    private fun touchMove() {
        val dx = Math.abs(motionTouchEventX - currentX)
        val dy = Math.abs(motionTouchEventY - currentY)
        if (dx >= touchTolerance || dy >= touchTolerance) {
            // QuadTo() adds a quadratic bezier from the last point,
            // approaching control point (x1,y1), and ending at (x2,y2).
            path.quadTo(currentX, currentY, (motionTouchEventX + currentX) / 2, (motionTouchEventY + currentY) / 2)
            currentX = motionTouchEventX
            currentY = motionTouchEventY
            // Draw the path in the extra bitmap to cache it.
            extraCanvas.drawPath(path, paint)
        }
        invalidate()
    }

    private fun touchUp() {
        // Reset the path so it doesn't get drawn again.
        //redo
        path.lineTo(currentX, currentY)
        paths.add(path)
        colors.add(paint.color)
        path = Path()
    }

    fun getPath() : ArrayList<Path>{
        return paths
    }
    //redo
    fun resetCanvasDrawing() {
        path.reset() // Avoiding saving redo from Path()
        paths.clear()
        invalidate()
    }

    fun undoCanvasDrawing() {
        if (paths.size > 0) {
            undoColors.add(colors.removeAt(paths.size - 1))
            undonePaths.add(paths.removeAt(paths.size - 1))
            invalidate()
        } else {
            Toast.makeText(context, "nothing to undo", Toast.LENGTH_SHORT).show()
        }
    }

    fun redoCanvasDrawing() {
        if (undonePaths.size > 0) {
            colors.add(undoColors.removeAt(undonePaths.size - 1))
            paths.add(undonePaths.removeAt(undonePaths.size - 1))
            invalidate()
        } else {
            Toast.makeText(context, "nothing to redo", Toast.LENGTH_SHORT).show()
        }
    }

}

我也有想過直接傳輸圖片就好,但圖片的傳輸量較大,希望還是可以避免。

請問有什麼方法可以儲存畫圖的路徑,或是Gartic.io即時傳輸筆跡是如何達成的,只要給我一點提示或方向即可,拜託大家了~

圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 個回答

1
koro_michael
iT邦新手 2 級 ‧ 2021-08-06 18:12:47

透過 socket 實時傳送資料吧,跟即時聊天或網路遊戲一樣

好的,我研究一下

我要發表回答

立即登入回答