SurfaceView和TextureView的使用以及区别
一 、 什么时候使用SurfaceView
我们平时学习自定义view的过程中会发现自定义的View可以满足大部分的绘图需求,但是当我们需要绘制的内容多且复杂(或者需要频繁刷新)时,View就有些难堪重任了,这时候就需要使用到android 为我们提供的SurfaceView了
二 、 SurfaceView相对于自定义View的优势
- View在主线程更新UI,SurefaceView在子线程更新UI
- View更适用于较为简单耗时短的绘图,SurfaceView可以绘制复杂的页面不会因为16ms内未绘制完出现卡顿的情况
- SurfaceView更倾向于主动频繁刷新的UI
三 、 SurfaceView的使用
- 创建自定义View继承SurfaceView,实现SurfaceHolder.Callback接口
- 初始化SurfaceView,设置回调addCallback(this) 和一下窗口属性
- 开启创建开启线程,在run()方法中通过mSurfaceHolder.lockCanvas()获取canvas进行绘图,绘制完成后通过unlockCanvasAndPost()方法释放提交画布
下面是示例代码
public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback ,Runnable{
private SurfaceHolder mSurfaceHolder;
private Thread mThread;
private boolean isRunning;
private Canvas mCanvas;
public MySurfaceView(Context context) {
this(context,null);
}
public MySurfaceView(Context context, AttributeSet attrs) {
this(context, attrs,-1);
}
public MySurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
private void initView() {
mSurfaceHolder = getHolder();
//设置回调
mSurfaceHolder.addCallback(this);
//获取焦点
setFocusable(true);
setFocusableInTouchMode(true);
//保持常亮
setKeepScreenOn(true);
//设置View位于布局最上层 这个属性搭配下面的设置可以实现背景透明(解决透明后直接展示桌面的BUG)
//可以通过windowManager的addView方法添加布局位于surfaceView的上方
setZOrderOnTop(true);
mSurfaceHolder.setFormat(PixelFormat.TRANSLUCENT);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
mThread = new Thread(this);
isRunning = true;
mThread.start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
//销毁的时候,关闭线程
isRunning = false;
}
@Override
public void run() {
while (isRunning) {
long start = System.currentTimeMillis();
draw();
long end = System.currentTimeMillis();
if ((end - start) < 50) {
//控制绘制间隔
SystemClock.sleep(50-(end - start));
}
}
}
private void draw(){
try {
mCanvas = mSurfaceHolder.lockCanvas();
if (mCanvas != null) {
//背景透明时设置清屏
mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
//todo 绘图
}
} catch (Exception e) {
} finally {
//释放canvas避免内存泄漏
mSurfaceHolder.unlockCanvasAndPost(mCanvas);
}
}
}
使用过程值得注意的地方
- surfaceView更像是一个独立的window不可以像View一样执行动画,也不受View的属性控制
- SurfaceView就是在窗口上挖一个洞,它就是显示在这个洞里,其他的View是显示在窗口上,所以View可以显式在 SurfaceView之上,你也可以添加一些层在SurfaceView之上
- surfaceView采用了双缓冲技术(对双缓冲感兴趣的自行百度),每次的绘制都是覆盖绘制(在上一次的显示基础上进行绘制),如果不想覆盖(例如设置背景透明时绘制的背景图片边缘有阴影效果的情况)可以添加清屏操作,然后再进行绘制
- surfaceView的绘制间隔最好保持一致,否则可能会出现卡顿现象
- 使用setZOrderOnTop(true);设置背景透明会导致遮挡其他布局的现象,可以考虑使用windowManager的addView方法添加位于surfaceView上方的布局(或者使用TextureView替换surfaceView)
四 、 TextureView的使用
textureView的使用和surfaceView十分相似,示例代码如下
public class MyTextureView extends TextureView implements Runnable, TextureView.SurfaceTextureListener {
private Thread mThread;
private boolean isRunning;
private Canvas mCanvas;
public MyTextureView(Context context) {
this(context,null);
}
public MyTextureView(Context context, AttributeSet attrs) {
this(context, attrs,-1);
}
public MyTextureView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
private void initView() {
//设置监听
setSurfaceTextureListener(this);
//获取焦点
setFocusable(true);
setFocusableInTouchMode(true);
//保持常亮
setKeepScreenOn(true);
//设置背景透明
setOpaque(false);
}
@Override
public void run() {
while (isRunning) {
long start = System.currentTimeMillis();
draw();
long end = System.currentTimeMillis();
if ((end - start) < 50) {
//控制绘制间隔
SystemClock.sleep(50-(end - start));
}
}
}
private void draw(){
try {
mCanvas = lockCanvas();
if (mCanvas != null) {
//背景透明时设置清屏
mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
//todo 绘图
}
} catch (Exception e) {
} finally {
if (mCanvas != null) {
//释放canvas避免内存泄漏
unlockCanvasAndPost(mCanvas);
}
}
}
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
mThread = new Thread(this);
isRunning = true;
mThread.start();
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
//销毁的时候,关闭线程
isRunning = false;
return false;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
}
五 、SurfaceView和TextureView和自定义View的区别
- SurfaceView和TextureView都是在子线程更新UI
- SurfaceView和TextureView都继承自View
- SurfaceView不能使用动画,两个SurfaceView不能相互覆盖
- TextureView4.0以后才能使用
- TextureView只能在开启了硬件加速的Window中使用,消费的内存要比SurfaceView多,并伴随着1-3帧的延迟。
- TextureView 需要通过UI Renderer输出。除了性能比较 SurfaceView 会有明显下降外(低端机,高 GPU 负荷场景可能存在 15% 左右的帧率下降),另外因为需要在三个线程之间进行写读同步(包括 CPU 和 GPU 的同步),当同步失调的时候,比较容易出现掉帧或者吞帧导致的卡顿和抖动现象。
- SurfaceView的窗口刷新的时候不需要重绘应用程序的窗口而android普通窗口的视图绘制机制是一层一层的,任何一个子元素或者是局部的刷新都会导致整个视图结构全部重绘一次。