绘制过程
View
和SurfaceView
绘制过程
PhoneWindow
:Window
的具体实现,在Activity
中调用setContentView()
方法时,一个PhoneWindow
实例会对应一个ViewRootImpl
实例,绘制,事件分发传递给ViewRootImpl
进行ViewRootImpl
:View
树绘制的根节点,自顶向下绘制- 普通
View
绘制:ViewRootImpl
会调用View.draw(Canvas canvas)
方法在Canvas
对象上进行绘制,绘制完成后将绘制结果(一张Bitmap
),最终交给SurfaceFlinger
进行合成和显示 SurfaceView
绘制:在绘制开始时,SurfaceView
会通过SurfaceHolder.lockCanvas()
方法来获取并锁定Canvas
,然后在Canvas
上进行绘制。通过SurfaceHolder.unlockCanvasAndPost()
方法将绘制的内容提交到自己的Surface
上,最终交给SurfaceFlinger
进行合成和显示。SurfaceView
是用来展示Surface
数据的地方,用来控制整个Surface
中绘制内容的位置和大小
- 普通
Surface
:每一个Surface
对应了一块屏幕缓冲区,包含了显示到屏幕的绘制内容
View
和SurfaceView
的区别
-
绘制线程:普通
View
是在主线程绘制的,而SurfaceView
可以在子线程绘制。当绘制操作非常复杂,普通View
可能会阻塞主线程,SurfaceView
可以避免UI变得不流畅。 -
重绘方式:普通
View
需要更新时,整个View
树都需要重新绘制。而SurfaceView
可以只更新自身的内容,而不影响到其他的View
-
Z轴顺序:
SurfaceView
的Surface
则默认位于其所在窗口的背景之上、其他普通View
之下 -
透明度:普通
View
可以设置任意的透明度,而SurfaceView
则只能是完全透明或者完全不透明。 -
综上所述,
View
适合用于构建常规的用户界面,而SurfaceView
则更适合用于需要频繁更新并且绘制操作复杂的场景,比如视频播放、游戏等
XML文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/cardview_dark_background"
tools:context=".MainActivity"
android:orientation="vertical">
<com.example.drag.MySurfaceView
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
自定义SurfaceView
代码
class MySurfaceView(context: Context) : SurfaceView(context), SurfaceHolder.Callback, Runnable {
private val TIME_IN_FRAME = 30L
private var isDrawing = false
private var drawThread: Thread? = null
private var mCanvas: Canvas? = null
private var mPath = Path()
private val mPaint = Paint().apply {
color = Color.WHITE
strokeWidth = 5f
style = Paint.Style.STROKE
}
init {
holder.addCallback(this)
isFocusable = true // 键盘事件获取焦点
isFocusableInTouchMode = true // 触摸事件获取焦点
keepScreenOn = true // 屏幕常亮
}
constructor(context: Context, attrs: AttributeSet) : this(context)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : this(context)
override fun onTouchEvent(event: MotionEvent): Boolean {
val x = event.x
val y = event.y
when (event?.action) {
MotionEvent.ACTION_DOWN -> {
mPath.moveTo(x, y)
}
MotionEvent.ACTION_MOVE -> {
mPath.lineTo(x, y)
}
MotionEvent.ACTION_UP -> {
// mPath.reset() 会导致刷新闪烁
}
}
return true
}
override fun surfaceCreated(holder: SurfaceHolder) {
// 初始化操作,例如加载资源或设置画布
isDrawing = true
drawThread = Thread(this).apply { start() }
}
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
// 处理 Surface 尺寸变化的逻辑
}
override fun surfaceDestroyed(holder: SurfaceHolder) {
// 释放资源,停止线程等清理操作
isDrawing = false
}
private fun draw() {
try {
synchronized(holder) {
mCanvas = holder.lockCanvas()
// mCanvas?.drawColor(Color.BLACK) 清空画布
// 获取Canvas对象开始绘制
mCanvas?.drawPath(mPath, mPaint)
}
} catch (e: Exception) {
Log.e(TAG, Log.getStackTraceString(e))
} finally {
if (mCanvas != null) {
// 绘制内容提交给Surface
holder.unlockCanvasAndPost(mCanvas)
}
}
}
override fun run() {
while (isDrawing) {
var startTime = System.currentTimeMillis()
draw()
var endTime = System.currentTimeMillis()
while ((endTime-startTime) < TIME_IN_FRAME) {
// 没有达到绘制帧间隔时间,线程等待
Thread.sleep(TIME_IN_FRAME - (endTime - startTime))
endTime = System.currentTimeMillis()
}
}
}
}