SurfaceView要点
- SurfaceView拥有独立的Surface(绘图表面)
- SurfaceView是用Zorder排序的,他默认在宿主Window的后面,SurfaceView通过在Window上面“挖洞”(设置透明区域)进行显示
SurfaceView与View的区别
- View的绘图效率不高,主要用于动画变化较少的程序
- SurfaceView 绘图效率较高,用于界面更新频繁的程序
- SurfaceView拥有独立的Surface(绘图表面),即它不与其宿主窗口共享同一个Surface。
一般来说,每一个窗口在SurfaceFlinger服务中都对应有一个Layer,用来描述它的绘图表面。对于那些具有SurfaceView的窗口来说,每一个SurfaceView在SurfaceFlinger服务中还对应有一个独立的Layer或者LayerBuffer,用来单独描述它的绘图表面,以区别于它的宿主窗口的绘图表面。因此SurfaceView的UI就可以在一个独立的线程中进行绘制,可以不会占用主线程资源。 - SurfaceView使用双缓冲机制,播放视频时画面更流畅
什么是双缓冲机制
- 在运用时可以理解为:
SurfaceView在更新视图时用到了两张 Canvas,一张 frontCanvas 和一张 backCanvas ,每次实际显示的是 frontCanvas ,backCanvas 存储的是上一次更改前的视图。当你在播放这一帧的时候,它已经提前帮你加载好后面一帧了,所以播放起视频很流畅。 - 当使用lockCanvas()获取画布时,得到的实际上是backCanvas 而不是正在显示的 frontCanvas ,之后你在获取到的 backCanvas 上绘制新视图,再 unlockCanvasAndPost(canvas)此视图,那么上传的这张 canvas 将替换原来的 frontCanvas 作为新的frontCanvas ,原来的 frontCanvas 将切换到后台作为 backCanvas 。例如,如果你已经先后两次绘制了视图A和B,那么你再调用 lockCanvas()获取视图,获得的将是A而不是正在显示的B,之后你将重绘的 A 视图上传,那么 A 将取代 B 作为新的 frontCanvas 显示在SurfaceView 上,原来的B则转换为backCanvas。
相当于多个线程,交替解析和渲染每一帧视频数据。
双缓冲机制导致的闪屏问题
- 原因
第一次绘制的时候是操作的缓存A,这次绘制是有绘制背景颜色,但是下次绘制缓存B的时候不绘制背景,这种情况下他们交替显示的时候就会出现背景快速变换而导致的闪屏现象。 - 解决办法
核心是创建一个Bitmap bitmapCache;
每次画图时,都在bitmapCache继续画画,最后再投到屏幕上去。
这样做的原理是,每次绘制都在上一次的基础上绘制,使帧连贯
if (bitmapCache == null) {
bitmapCache = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Bitmap.Config.ARGB_8888);
}
Canvas canvasCache = new Canvas(bitmapCache);
drawCache(canvasCache);
canvas.drawBitmap(bitmapCache, 0, 0, null);
package com.example.zhangyu.cutvideomusic;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import com.example.zhangyu.cutvideomusic.utils.ThumbUtils;
public class BgSurfaceView extends SurfaceView {
private SurfaceHolder holder;
private Canvas canvas;
private Thread thread;
private boolean isRunning;
public BgSurfaceView(Context context) {
this(context, null);
}
public BgSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
holder = getHolder();
holder.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
isRunning = true;
thread = new Thread(new Runnable() {
@Override
public void run() {
while (isRunning) {
draw();
}
}
});
thread.start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
isRunning = false;
}
});
}
Bitmap bitmapCache;
private void draw() {
try {
canvas = holder.lockCanvas();
if (canvas != null) {
if (bitmapCache == null) {
bitmapCache = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Bitmap.Config.ARGB_8888);
}
Canvas canvasCache = new Canvas(bitmapCache);
drawCache(canvasCache);
canvas.drawBitmap(bitmapCache, 0, 0, null);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (canvas != null) {
try {
holder.unlockCanvasAndPost(canvas);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
private void drawCache(Canvas canvas) {
}
}