先看看实现效果:
SurfaceView类介绍:
进入源码:
public class SurfaceView extends View {
public SurfaceView(Context context) {
super((Context)null, (AttributeSet)null, 0, 0);
throw new RuntimeException("Stub!");
}
public SurfaceView(Context context, AttributeSet attrs) {
super((Context)null, (AttributeSet)null, 0, 0);
throw new RuntimeException("Stub!");
}
public SurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
super((Context)null, (AttributeSet)null, 0, 0);
throw new RuntimeException("Stub!");
}
public SurfaceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super((Context)null, (AttributeSet)null, 0, 0);
throw new RuntimeException("Stub!");
}
public SurfaceHolder getHolder() {
throw new RuntimeException("Stub!");
}
protected void onAttachedToWindow() {
throw new RuntimeException("Stub!");
}
protected void onWindowVisibilityChanged(int visibility) {
throw new RuntimeException("Stub!");
}
public void setVisibility(int visibility) {
throw new RuntimeException("Stub!");
}
protected void onDetachedFromWindow() {
throw new RuntimeException("Stub!");
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
throw new RuntimeException("Stub!");
}
public boolean gatherTransparentRegion(Region region) {
throw new RuntimeException("Stub!");
}
public void draw(Canvas canvas) {
throw new RuntimeException("Stub!");
}
protected void dispatchDraw(Canvas canvas) {
throw new RuntimeException("Stub!");
}
public void setZOrderMediaOverlay(boolean isMediaOverlay) {
throw new RuntimeException("Stub!");
}
public void setZOrderOnTop(boolean onTop) {
throw new RuntimeException("Stub!");
}
public void setSecure(boolean isSecure) {
throw new RuntimeException("Stub!");
}
}
从源码中可以看到:
SurfaceView 介绍
- SurfaceView 就是带 Surface 的 view,它是一个 View,是 View 的子类,所以和其他 View 一样,可以在屏幕上展示东西接收用户输入,具有 View 的生命周期回调函数,如 onMeasure、onLayout、onDraw、onTouchEvent 等
- SurfaceView 带有独立的 Surface(独立与 window 的 surface),这可以让子线程在独立的 Surface 上面绘制东西,进行 SurfaceView 的界面绘制,这个子线程就叫做渲染线程,但是要让独立的 Surface 上面的东西在 View 上面展示出来,需要 post 一个消息给主线程,目的是把该 Surface 中 canvas 上的东西绘制到 View 的真正的画布上面(window 的 surface 的 canvas上),这样就可以把 UI 线程空闲出来处理用户的交互
- Surface 可能被销毁,它只在SurfaceHolder.Callback.surfaceCreated() 和 SurfaceHolder.Callback.surfaceDestroyed() 之间有效,这只是说 Surface 创建和销毁的时候会回到前面两个方法,所以要确保渲染线程访问的是合法有效的 surface
- SurfaceHolder.CallBack 是通过 SurfaceView 的 SurfaceHolder 的 addCallback 来设置给 SurfaceHolder 的,让 SurfaceView 实现 CallBack 并设置给 SurfaceHolder,SurfaceView 就可以监听这个独立 Surface 的创建和销毁了。
SurfaceHolder 介绍
上源码:
public interface SurfaceHolder {
/** @deprecated */
@Deprecated
int SURFACE_TYPE_GPU = 2;
/** @deprecated */
@Deprecated
int SURFACE_TYPE_HARDWARE = 1;
/** @deprecated */
@Deprecated
int SURFACE_TYPE_NORMAL = 0;
/** @deprecated */
@Deprecated
int SURFACE_TYPE_PUSH_BUFFERS = 3;
void addCallback(SurfaceHolder.Callback var1);
void removeCallback(SurfaceHolder.Callback var1);
boolean isCreating();
/** @deprecated */
@Deprecated
void setType(int var1);
void setFixedSize(int var1, int var2);
void setSizeFromLayout();
void setFormat(int var1);
void setKeepScreenOn(boolean var1);
Canvas lockCanvas();
Canvas lockCanvas(Rect var1);
void unlockCanvasAndPost(Canvas var1);
Rect getSurfaceFrame();
Surface getSurface();
public interface Callback2 extends SurfaceHolder.Callback {
void surfaceRedrawNeeded(SurfaceHolder var1);
}
public interface Callback {
void surfaceCreated(SurfaceHolder var1);
void surfaceChanged(SurfaceHolder var1, int var2, int var3, int var4);
void surfaceDestroyed(SurfaceHolder var1);
}
public static class BadSurfaceTypeException extends RuntimeException {
public BadSurfaceTypeException() {
throw new RuntimeException("Stub!");
}
public BadSurfaceTypeException(String name) {
throw new RuntimeException("Stub!");
}
}
}
SurfaceHolder 是对 SurfaceView 的 Surface 的包装,不但在 SurfaceHolder.callback 接口中负责 Surface 创建和销毁的回调,而且还对 Surface 的关键方法 LockCanvas()、unLockCanvasAndPost() 方法进行了线程安全的包装,所以 SurfaceHolder 是 Surface 对象的持有者,负责 Surface 的生命周期中的对 Surface 操作的方法的调用
脏矩形 Rect dirty,是指标记这块矩形区域的数据作废,也就是需要重写绘制的矩形区域,LockCanvas(Rect dirty),可以指定一个矩形区域,让 Surface 中的 Canvas 上部分数据重绘。
SurfaceView、SurfaceHolder、Surface 之间的关系
SurfaceView 使用的步骤
- 获取到 SurfaceView 对应的 SurfaceHolder,给 SurfaceHolder 添加一个 SurfaceHolder.callback 对象。
- 创建渲染线程对象
- 在子线程中开始在 Surface 上面绘制图形,因为SurfaceView没有对我们暴露 Surface,而只是暴露了 Surface 的包装器 SurfaceHolder,所以使用 SurfaceHolder 的 lockCanvas()获取 Surface 上面指定区域的 Canvas,在该 Canvas 上绘制图形,绘制结束后,使用 SurfaceHolder 的 unlockCanvasAndPost()方法解锁 Canvas,并且让 UI 线程把 Surface 上面的东西绘制到 View 的 Canvas 上面
自定义SurfaceView 实现GIF动画DEMO:
首先自定义class GifSurfaceView 继承SurfaceView,并实现SurfaceHolder.Callback接口,
详细代码如下:
public class GifSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder holder;
//gif图片路径
private String path;
private Movie movie;
//执行动画
private Handler handler;
//放大倍数
private int zoom;
//构造函数
public GifSurfaceView(Context context) {
super(context);
initParam();
}
public GifSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
initParam();
}
public GifSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initParam();
}
//线程
private Runnable runnable = new Runnable() {
@Override
public void run() {
//获取画布(加锁)
Canvas canvas = holder.lockCanvas();
canvas.save();
canvas.scale(zoom,zoom); //x为水平方向的放大倍数,y为竖直方向的放大倍数。
//绘制此gif的某一帧,并刷新本身
movie.draw(canvas,0,0);
//逐帧绘制图片(图片数量5)
// 1 2 3 4 5 6 7 8 9 10
// 1 2 3 4 0 1 2 3 4 0 循环
movie.setTime((int) (System.currentTimeMillis()%movie.duration()));
canvas.restore();
//结束锁定画图,并提交改变,画画完成(解锁)
holder.unlockCanvasAndPost(canvas);
handler.postDelayed(runnable , 50); //50ms表示每50ms绘制一帧
}
};
/**
* 初始化参数
*/
private void initParam(){
holder = getHolder();
holder.addCallback(this);
handler = new Handler();
}
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
}
/**
* 计算视图宽高
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//加载GIF图片
//1.获取GIF图片路径
if (!TextUtils.isEmpty(path)){
try {
InputStream stream = getContext().getAssets().open(path);
movie = Movie.decodeStream(stream);
//获取gif的宽高
int width = movie.width();
int height = movie.height();
setMeasuredDimension((int)(width*zoom),(int)(height*zoom));
// setMeasuredDimension(width,height);
//执行gif动画
handler.post(runnable);
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
//停止gif动画
handler.removeCallbacks(runnable);
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public void setZoom(int zoom) {
this.zoom = zoom;
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
}
}
/*
* 整个过程:继承SurfaceView并实现SurfaceHolder.Callback接口 ----> SurfaceView.getHolder()获得SurfaceHolder对象 ---->SurfaceHolder.addCallback(callback)添加回调函数---->SurfaceHolder.lockCanvas()获得Canvas对象并锁定画布----> Canvas绘画 ---->SurfaceHolder.unlockCanvasAndPost(Canvas canvas)结束锁定画图,并提交改变,将图形显示。
*/
在MainActivity中进行调用并传入GIF路径:
public class MainActivity extends Activity {
GifSurfaceView myView;
int scale=1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myView=(GifSurfaceView)findViewById(R.id.gsv);
//myView.setPath("a.gif");
myView.setPath("2.gif");
myView.setZoom(scale);
}
总结:
第一步:继承SurfaceView并实现SurfaceHolder.Callback接口
第二步:SurfaceView.getHolder()获得SurfaceHolder对象
第三步:SurfaceHolder.addCallback(callback)添加回调函数
第四步:SurfaceHolder.lockCanvas()获得Canvas对象并锁定画布
第五步:Canvas绘画
第六步:SurfaceHolder.unlockCanvasAndPost(Canvas canvas)结束锁定画图,并提交改变,将图形显示。
具体流程如下图所示:
源码下载