由于視頻的渲染,本質(zhì)上就是顯示一張一張的圖片跨嘉,所以學(xué)習(xí)第一步就是學(xué)習(xí)顯示圖片。
這次我們使用三種不同的方式來(lái)顯示一張圖片。
ImageView
使用 ImageView 是最簡(jiǎn)單也是最常用的方式了陷猫。
context?.let {
image.setImageBitmap(decodeStream(it.assets.open("images/test_image.jpeg")))
}
SurfaceView
我們知道View是通過(guò)刷新來(lái)重繪視圖,系統(tǒng)通過(guò)發(fā)出
VSSYNC
信號(hào)來(lái)進(jìn)行屏幕的重繪的妖,刷新的時(shí)間間隔是 16 ms绣檬,如果我們可以在 16 ms 以內(nèi)將繪制工作完成,則沒(méi)有任何問(wèn)題嫂粟,如果我們繪制過(guò)程邏輯很復(fù)雜娇未,而且我們的界面更新還非常頻繁,這時(shí)候就會(huì)造成界面的卡頓星虹,影響用戶體驗(yàn)零抬。為此Android提供了SurfaceView
來(lái)解決這一問(wèn)題镊讼。
SurfaceView 和 View 有什么區(qū)別?
- View 適用于被動(dòng)更新的場(chǎng)景平夜,而 SurfaceView 適用于主動(dòng)更新的情況蝶棋,比如頻繁的刷新界面。(具體原因見(jiàn)下條)
- View 在主線程中對(duì)頁(yè)面進(jìn)行刷新忽妒,而 SurfaceView 則開(kāi)啟一個(gè)子線程來(lái)對(duì)頁(yè)面進(jìn)行刷新玩裙。(最本質(zhì)的區(qū)別)
- View 在繪圖的時(shí)候沒(méi)有雙緩沖機(jī)制,而 SurfaceView 在底層中實(shí)現(xiàn)了雙緩沖機(jī)制段直。
代碼
最后肯定要上代碼的 [狗頭]
surfaceView.holder.addCallback(object : SurfaceHolder.Callback {
override fun surfaceCreated(holder: SurfaceHolder?) {
holder?.let {
Thread {
val canvas = it.lockCanvas()
val bitmap =
decodeStream(context?.assets?.open("images/test_image.jpeg"))
val bitmapW = bitmap.width
val bitmapH = bitmap.height
val src = Rect(0, 0, bitmapW, bitmapH)
val dst = if (bitmapW > bitmapH) {
Rect(
0,
0,
surfaceView.width,
(surfaceView.width * (bitmapH.toFloat() / bitmapW)).toInt()
)
} else {
Rect(
0,
0,
surfaceView.height * (bitmapW / bitmapH),
surfaceView.height
)
}
canvas.drawColor(Color.parseColor("#ffffff"))
canvas.drawBitmap(
bitmap,
src,
dst,
Paint()
)
it.unlockCanvasAndPost(canvas)
}.start()
}
}
override fun surfaceChanged(
holder: SurfaceHolder?,
format: Int,
width: Int,
height: Int
) {
}
override fun surfaceDestroyed(holder: SurfaceHolder?) {
}
})
自定義 View
其實(shí)自定義 View 和 SurfaceView 差不多吃溅,直接看代碼:
class CustomImageView : View {
constructor(context: Context?) : super(context) {}
constructor(context: Context?, attrs: AttributeSet?) : super(
context,
attrs
)
constructor(
context: Context?,
attrs: AttributeSet?,
defStyleAttr: Int
) : super(context, attrs, defStyleAttr)
constructor(
context: Context?,
attrs: AttributeSet?,
defStyleAttr: Int,
defStyleRes: Int
) : super(context, attrs, defStyleAttr, defStyleRes)
private val bitmap: Bitmap =
BitmapFactory.decodeStream(context?.assets?.open("images/test_image.jpeg"))
private val src = Rect()
private val dst = Rect()
private val paint = Paint()
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
val bitmapW = bitmap.width
val bitmapH = bitmap.height
src.set(0, 0, bitmapW, bitmapH)
if (bitmapW > bitmapH) {
dst.set(
0,
0,
this.width,
(this.width * (bitmapH.toFloat() / bitmapW)).toInt()
)
} else {
dst.set(
0,
0,
this.height * (bitmapW / bitmapH),
this.height
)
}
canvas?.drawBitmap(bitmap, src, dst, paint)
}
}
最后
OK,第一個(gè)任務(wù)算是完成了~代碼更新在 GitHub 倉(cāng)庫(kù)中鸯檬。新年新氣象决侈,下個(gè)任務(wù)見(jiàn)~
- GitHub 倉(cāng)庫(kù): https://github.com/T-Oner/MediaPractice
最新更新請(qǐng)關(guān)注微信公眾號(hào)