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

本文详细介绍了如何使用Android的SurfaceView实现GIF动画。首先,讲解了SurfaceView的基本概念及其与SurfaceHolder的关系,然后通过六步法演示了自定义GifSurfaceView并实现GIF动画的过程,包括继承SurfaceView、实现SurfaceHolder.Callback接口,以及在Surface上绘制的详细步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


  先看看实现效果:



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;
    }

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

具体流程如下图所示:

      

    源码下载










评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值