先看看实现效果:



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 介绍

  1. SurfaceView 就是带 Surface 的 view,它是一个 View,是 View 的子类,所以和其他 View 一样,可以在屏幕上展示东西接收用户输入,具有 View 的生命周期回调函数,如 onMeasure、onLayout、onDraw、onTouchEvent 等
  2. SurfaceView 带有独立的 Surface(独立与 window 的 surface),这可以让子线程在独立的 Surface 上面绘制东西,进行 SurfaceView 的界面绘制,这个子线程就叫做渲染线程,但是要让独立的 Surface 上面的东西在 View 上面展示出来,需要 post 一个消息给主线程,目的是把该 Surface 中 canvas 上的东西绘制到 View 的真正的画布上面(window 的 surface 的 canvas上),这样就可以把 UI 线程空闲出来处理用户的交互
  3. Surface 可能被销毁,它只在SurfaceHolder.Callback.surfaceCreated() 和 SurfaceHolder.Callback.surfaceDestroyed() 之间有效,这只是说 Surface 创建和销毁的时候会回到前面两个方法,所以要确保渲染线程访问的是合法有效的 surface
  4. 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 之间的关系

Android 自定义SurfaceView实现加载GIF动画_第1张图片


SurfaceView 使用的步骤

  1. 获取到 SurfaceView 对应的 SurfaceHolder,给 SurfaceHolder 添加一个 SurfaceHolder.callback 对象。
  2. 创建渲染线程对象
  3. 在子线程中开始在 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;    }@Overridepublic 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;@Overrideprotected 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)结束锁定画图,并提交改变,将图形显示。

具体流程如下图所示:

Android 自定义SurfaceView实现加载GIF动画_第2张图片

源码下载










更多相关文章

  1. Android 子线程请求ASP.NET后台
  2. android之UI线程处理
  3. android之多线程
  4. Android 中的线程
  5. 浅析android线程模型【Android】
  6. Android底下多线程下载远程图片

随机推荐

  1. 环境变量的安装以及python cahrm的安装以
  2. Python多行正则表达式忽略字符串中的n行
  3. 后端传给前端int 类型数据自增或自减
  4. FP-growth算法思想和其python实现
  5. 在生产中是否应该减少服务器代码?
  6. Python学习手册(第四版)学习笔记(二)我学Pyth
  7. python 按位置关系输出矩阵元素
  8. 基础入门_Python-进线协程.分分钟玩转mul
  9. python 函数、参数及参数解构
  10. 具有1位条目的numpy布尔数组