绘制过程
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.MySurfaceViewandroid: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 = 30Lprivate var isDrawing = falseprivate var drawThread: Thread? = nullprivate var mCanvas: Canvas? = nullprivate var mPath = Path()private val mPaint = Paint().apply {color = Color.WHITEstrokeWidth = 5fstyle = 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.xval y = event.ywhen (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 = truedrawThread = 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) {// 绘制内容提交给Surfaceholder.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()}}}
}