当我们点击屏幕时,我的程序会画一个椭圆,然后+会根据手指的坐标改变椭圆的坐标,当我们松开手指时,椭圆就会消失。
现在当我点击屏幕时,我得到了我的椭圆形,+当我移动手指时,椭圆形总是在它旁边,但当我松开手指时,我得到了一个错误:
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()
}
}
}
1条答案
按热度按时间r6hnlfcb1#
我没有时间深入研究Android源代码并找出到底发生了什么,但您的崩溃是由
Runnable
与Surface
的Canvas
交互(创建 “Surface已发布” 异常)产生的。当你抬起手指时,你会这样做:
在
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
无效,这可能会导致罕见的崩溃