简介
SurfaceView 是 Android 中一种特殊的 View,它与其他的 View 的区别是:
- 它有自己独立的绘图表面(Surface),它在持有该 SurfaceView 的 Window 的后面,与该 SurfaceView 同一层级的普通 View(比如 TextView、Button 等)会显示在该 SurfaceView 的上面。
- SurfaceView 的 UI 可以在子线程中渲染。
SurfaceView 一般用来显示比较复杂的图像、动画或视频。
简单使用
下面是一个 SurfaceView 的简单实例,该实例会显示一个跟随手指移动的足球:
class MySurfaceView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : SurfaceView(context, attrs, defStyleAttr), Runnable, SurfaceHolder.Callback {
companion object {
private const val TAG = "MySurfaceView"
private const val REQ_SIZE = 120
}
private var football: Bitmap? = null
private var x: Float = 0f
private var y: Float = 0f
private val holder: SurfaceHolder = getHolder()
private var isRunning = false
private var thread: Thread? = null
init {
holder.addCallback(this)
// 获取 football 的 Bitmap
loadBitmap()
}
private fun loadBitmap(){
val options = BitmapFactory.Options()
options.inJustDecodeBounds = true
BitmapFactory.decodeResource(resources, R.mipmap.football, options)
val height = options.outHeight
val width = options.outWidth
var inSampleSize = 1
if((height > REQ_SIZE) or (width > REQ_SIZE)) {
inSampleSize = (height / REQ_SIZE).coerceAtLeast(width / REQ_SIZE)
}
options.inSampleSize = inSampleSize
options.inJustDecodeBounds =false
football = BitmapFactory.decodeResource(resources, R.mipmap.football, options)
}
override fun onTouchEvent(me: MotionEvent): Boolean {
when(me.action){
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_MOVE->{
x = me.x
y = me.y
return true
}
}
return super.onTouchEvent(me)
}
override fun run() {
while(isRunning){
if(!holder.surface.isValid){
continue
}
val canvas = holder.lockCanvas()
canvas.drawColor(Color.GREEN)
football?.let {
canvas.drawBitmap(it, x - it.width / 2f, y - it.height / 2f, null)
}
holder.unlockCanvasAndPost(canvas)
}
}
private fun pause(){
isRunning = false
thread?.join()
thread = null
}
private fun resume(){
if (isRunning || thread != null) return
isRunning = true
thread = Thread(this)
thread?.start()
}
override fun surfaceCreated(holder: SurfaceHolder) {
resume()
}
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
}
override fun surfaceDestroyed(holder: SurfaceHolder) {
pause()
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
pause()
football?.recycle()
football = null
}
}
运行后显示如下:
API
下面来学习一下常用的 API:
getHolder(),通过这个函数可以拿到 SurfaceHolder,用于获取和控制 SurfaceView 对应的 surface。
在这里通过 holder.addCallback(this) 给当前 SurfaceView 添加 SurfaceHolder.Callback 回调,这样当 surface 创建后会马上调用 surfaceCreated() 函数,开发者需要在这里开启渲染;surfaceDestroyed() 函数在 surface 销毁之前回调,开发者需要在这里停止渲染;surfaceChanged() 函数在 surface 结构(格式或尺寸)变化的时候回调,它在 surfaceCreated() 后至少会调用一次。注意 SurfaceHolder.Callback 需要在持有 SurfaceView 的 Window 的线程调用(一般是主线程)。
在子线程中渲染 SurfaceView 时,你必须保证对应的 surface 是可用的(可以通过 isValid() 函数校验)。
在修改 surface 中的像素之前要调用 lockCanvas(Rect dirty),修改完成后要调用 unlockCanvasAndPost(Canvas canvas)。