android 曲面已发布:使用画布绘图时

waxmsbnn  于 2023-01-11  发布在  Android
关注(0)|答案(1)|浏览(115)

当我们点击屏幕时,我的程序会画一个椭圆,然后+会根据手指的坐标改变椭圆的坐标,当我们松开手指时,椭圆就会消失。
现在当我点击屏幕时,我得到了我的椭圆形,+当我移动手指时,椭圆形总是在它旁边,但当我松开手指时,我得到了一个错误:
E/曲面:自由所有缓冲区:1个缓冲区在出队时被释放!
E/安卓运行时:致命异常:螺纹-2
进程:com.example.lab10,PID:24198
java.lang.IllegalStateException: Surface has already been released. at android.view.Surface.checkNotReleasedLocked(Surface.java:774) at android.view.Surface.unlockCanvasAndPost(Surface.java:473) at android.view.SurfaceView$1.unlockCanvasAndPost(SurfaceView.java:1629) at com.example.lab10.TaskSix$OvalsView.surfaceCreated$lambda$0(TaskSix.kt:136) at com.example.lab10.TaskSix$OvalsView.$r8$lambda$fxYnyciEdLkjDqbt7r8jlbaJ-60(Unknown Source:0) at com.example.lab10.TaskSix$OvalsView$$ExternalSyntheticLambda0.run(Unknown Source:2) at java.lang.Thread.run(Thread.java:1012)

package com.example.lab10
    
    import android.annotation.SuppressLint
    import androidx.appcompat.app.AppCompatActivity
    import android.os.Bundle
    
    
    import android.content.Context
    import android.graphics.Canvas
    import android.graphics.Color
    import android.graphics.Paint
    import android.util.DisplayMetrics
    import android.util.Log
    import android.view.MotionEvent
    import android.view.SurfaceHolder
    import android.view.SurfaceView
    import android.view.View
    import android.view.WindowManager
    import androidx.constraintlayout.widget.ConstraintLayout
    
    class TaskSix : AppCompatActivity() {
        @SuppressLint("ClickableViewAccessibility")
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_task_six)
    
            title = "Task#6"
    
            val layout = findViewById<ConstraintLayout>(R.id.layout)
            val ovalsView =  OvalsView(this)
    
            layout.setOnTouchListener(View.OnTouchListener { view, motionEvent ->
                when (motionEvent.getActionMasked()) {
                    MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN -> {
                        Log.d("qqq", "down")
                        val ID: Int = motionEvent.getPointerId(motionEvent.getActionIndex())
                        Log.d("qqq", "element with id: $ID")
    
    
                        layout.addView(ovalsView)
    
                    }
                    MotionEvent.ACTION_MOVE -> {
                        Log.d("qqq", "move")
                        var idx = 0
                        while (idx < motionEvent.pointerCount) {
                            val ID: Int = motionEvent.getPointerId(idx) // pobranie unikalnego id dla każdego dotyku
                            idx++
    
                            ovalsView._top = motionEvent.y
                            ovalsView._left = motionEvent.x
                        }
                    }
                    MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP -> {
                        Log.d("qqq", "up")
    
                        layout.removeView(ovalsView)
    
                    }
                    else -> Log.d("qqq", "unhandled")
                }
                return@OnTouchListener true
            })
    
        }
    
        private class OvalsView(context: Context) : SurfaceView(context), SurfaceHolder.Callback {
            private val mSurfaceHolder: SurfaceHolder
            private val mPainter = Paint()
            private var mDrawingThread: Thread? = null
            private val mDisplay = DisplayMetrics()
            private var mDisplayWidth: Int
            private var mDisplayHeight: Int
            private var mRotation = 0f
    
            var _top: Float = 0f
            var _left: Float = 0f
    
            init {
                val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
                wm.defaultDisplay.getMetrics(mDisplay)
                mDisplayWidth = mDisplay.widthPixels
                mDisplayHeight = mDisplay.heightPixels
                mPainter.isAntiAlias = true
                mPainter.color = Color.RED
                mSurfaceHolder = holder
                mSurfaceHolder.addCallback(this)
            }
    
            private fun animateOvals(): Boolean {
                mRotation += 1
                return true
            }
            private fun drawWheel(canvas: Canvas) {
                canvas.drawColor(Color.WHITE)
    //            canvas.rotate(mRotation, mDisplayWidth / 2f, mDisplayHeight / 2f)
    
                //drawOval(float left, float top, float right, float bottom, @NonNull Paint paint)
    
    //            canvas.drawOval(mDisplayWidth/2f - 100f/2f - 100f, mDisplayHeight/2 + 100f/2f,
    //                mDisplayWidth/2f + 100f/2f + 100f, mDisplayHeight/2f - 100/2f, mPainter)
    
                if (_top > 0f && _left > 0f)
                    canvas.drawOval(_left, _top, _left + 200f, _top + 350f, mPainter)
            }
    
            override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
                mDisplayWidth = w
                mDisplayHeight = h
                super.onSizeChanged(w, h, oldw, oldh)
            }
    
            override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
            }
    
            override fun surfaceDestroyed(holder: SurfaceHolder) {
                if (mDrawingThread != null) {
                    mDrawingThread!!.interrupt()
                }
    
            }
    
            override fun surfaceCreated(holder: SurfaceHolder) {
    
                mDrawingThread = Thread(Runnable {
                    var MAX_FRAME_TIME = 1000/60
    
                    var frameStartTime = System.nanoTime();
                    var frameTime: Long = 0
                    var canvas: Canvas? = null
                    while (!Thread.currentThread().isInterrupted && animateOvals()) {
    
                        canvas = mSurfaceHolder.lockCanvas()
                        if (canvas != null) {
                            drawWheel(canvas)
                            mSurfaceHolder.unlockCanvasAndPost(canvas)
                        }
    
    
                        frameTime = (System.nanoTime() - frameStartTime) / 1000000
                        if (frameTime < MAX_FRAME_TIME) // faster than the max fps - limit the FPS
                        {
                            try {
                                Thread.sleep(MAX_FRAME_TIME - frameTime)
                            } catch (e: InterruptedException) {
                                // ignore
                            }
                        }
                    }
                })
                mDrawingThread!!.start()
            }
        }
    }
r6hnlfcb

r6hnlfcb1#

我没有时间深入研究Android源代码并找出到底发生了什么,但您的崩溃是由RunnableSurfaceCanvas交互(创建 “Surface已发布” 异常)产生的。
当你抬起手指时,你会这样做:

MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP -> {
    layout.removeView(ovalsView)
}

removeView的文档中:

注意:不要从View.draw(android.graphics.Canvas)View.onDraw(android.graphics.Canvas)dispatchDraw(android.graphics.Canvas)或任何相关方法调用此方法

对我来说,这意味着调用removeView将导致View发生一些事情,这意味着它们不再被吸引,这就是为什么你不能在draw调用中这样做,你正在积极地吸引他们的Canvas
因为你的绘图线程总是在某个时间点上画Surface,所以你有一个潜在的争用条件,即Surface在线程被中断之前被释放。你在surfaceDestroyed中调用interrupt()-但是释放发生在Surface被实际销毁之前,它只是释放了一个引用,使Surface无效。
因此,您的绘制循环可能仍在尝试绘制到已发布的无效Surface-有很多方法可以解决这个问题,但您可能希望查看Surface#isValid(您可以使用X1 M19 N1 X访问X1 M18 N1 X的底层表面)。而且由于这里有多个线程,您可能希望实现某种并发锁定,以避免UI线程在绘图线程运行Canvas接触代码时使用removeView使Surface无效,这可能会导致罕见的崩溃

相关问题