Android 自定义SurfaceView实现加载GIF动画

来源:互联网 发布:2018中考倒计时软件 编辑:程序博客网 时间:2024/04/30 16:11


  先看看实现效果:



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 之间的关系

这里写图片描述


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)结束锁定画图,并提交改变,将图形显示。

具体流程如下图所示:

      

    源码下载










0 0
原创粉丝点击