利用SurfaceView打造一个闪屏页动画

参考:http://blog.csdn.net/heikefangxian23/article/details/50110007

首先我们先来看下官方API对SurfaceView的介绍

SurfaceView的API介绍
Provides a dedicated drawing surface embedded inside of a view hierarchy. You can control the format of this surface and, if you like, >its size; the SurfaceView takes care of placing the surface at the correct location on the screen

The surface is Z ordered so that it is behind the window holding its SurfaceView; the SurfaceView punches a hole in its window to allow its surface to be displayed. The view hierarchy will take care of correctly compositing with the Surface any siblings of the SurfaceView that would normally appear on top of it. This can be used to place overlays such as buttons on top of the Surface, though note however that it can have an impact on performance since a full alpha-blended composite will be performed each time the Surface changes.

Access to the underlying surface is provided via the SurfaceHolder interface, which can be retrieved by calling getHolder().

The Surface will be created for you while the SurfaceView's window is visible; you should implement surfaceCreated(SurfaceHolder) and surfaceDestroyed(SurfaceHolder) to discover when the Surface is created and destroyed as the window is shown and hidden.

One of the purposes of this class is to provide a surface in which a secondary thread can render in to the screen. If you are going to use it this way, you need to be aware of some threading semantics:

•All SurfaceView and SurfaceHolder.Callback methods will be called from the thread running the SurfaceView's window (typically the main thread of the application). They thus need to correctly synchronize with any state that is also touched by the drawing thread.
•You must ensure that the drawing thread only touches the underlying Surface while it is valid -- between SurfaceHolder.Callback.surfaceCreated() and SurfaceHolder.Callback.surfaceDestroyed().

对应的中文翻译

SurfaceView是视图(View)的继承类,这个视图里内嵌了一个专门用于绘制的Surface。你可以控制这个Surface的格式和尺寸。Surfaceview控制这个Surface的绘制位置。
surface是纵深排序(Z-ordered)的,这表明它总在自己所在窗口的后面。surfaceview提供了一个可见区域,只有在这个可见区域内 的surface部分内容才可见,可见区域外的部分不可见。surface的排版显示受到视图层级关系的影响,它的兄弟视图结点会在顶端显示。这意味者 surface的内容会被它的兄弟视图遮挡,这一特性可以用来放置遮盖物(overlays)(例如,文本和按钮等控件)。注意,如果surface上面 有透明控件,那么它的每次变化都会引起框架重新计算它和顶层控件的透明效果,这会影响性能。
你可以通过SurfaceHolder接口访问这个surface,getHolder()方法可以得到这个接口。
surfaceview变得可见时,surface被创建;surfaceview隐藏前,surface被销毁。这样能节省资源。如果你要查看 surface被创建和销毁的时机,可以重载surfaceCreated(SurfaceHolder)和 surfaceDestroyed(SurfaceHolder)。
surfaceview的核心在于提供了两个线程:UI线程和渲染线程。这里应注意:
1> 所有SurfaceView和SurfaceHolder.Callback的方法都应该在UI线程里调用,一般来说就是应用程序主线程。渲染线程所要访问的各种变量应该作同步处理。
2> 由于surface可能被销毁,它只在SurfaceHolder.Callback.surfaceCreated()和 SurfaceHolder.Callback.surfaceDestroyed()之间有效,所以要确保渲染线程访问的是合法有效的surface。

SurfaceView理解

什么是SurfaceView呢?
为什么是SurfaceView呢?Surface的意思是表层,表面的意思,那么SurfaceView就是指一个在表层的View对象。为什么 说是在表层呢,这是因为它有点特殊跟其他View不一样,其他View是绘制在表层外,而它就是充当表层对象。假设你要在一个球上画画,那么球的表层就当 做你的画布对象,你画的东西会挡住它的表层,我们默认没使用SurfaceView,那么球的表层就是空白的,如果我们使用了SurfaceView,我 们可以理解为我们拿来的球本身表面就具有纹路,你是画再纹路之上的,如果你画的是半透明的,那么你将可以透过你画的东西看到球面本身的纹路。SDK的文档 说到:SurfaceView就是在Window上挖一个洞,它就是显示在这个洞里,其他的View是显示在Window上,所以View可以显式在 SurfaceView之上,你也可以添加一些层在SurfaceView之上。
SurfaceView还有其他的特性,上面我们讲了它可以控制帧数,那它是什么控制的呢?这就需要了解它的使用机制。一般在很多游戏设计中,我们都是开辟一个后台线程计算游戏相关的数据,然后根据这些计算完的新数据再刷新视图对象,由于对View执行绘制操作只能在UI线程上, 所以当你在另外一个线程计算完数据后,你需要调用View.invalidate方法通知系统刷新View对象,所以游戏相关的数据也需要让UI线程能访 问到,这样的设计架构比较复杂,要是能让后台计算的线程能直接访问数据,然后更新View对象那改多好。我们知道View的更新只能在UI线程中,所以使 用自定义View没办法这么做,但是SurfaceView就可以了。它一个很好用的地方就是允许其他线程(不是UI线程)绘制图形(使用Canvas),根据它这个特性,你就可以控制它的帧数,你如果让这个线程1秒执行50次绘制,那么最后显示的就是50帧。

官话说完了,再来看一下具体的说明:

一.为什么使用SurfaceView

大多数情况下我们的自定义View都会选择去继承View或ViewGroup来实现,但是为什么系统还要为我们提供一个SurfaceView呢?

首先我们知道View类如果需要更新视图,必须我们主动的去调用invalidate()或者postInvalidate()方法来再走一次onDraw()完成更新。但是呢,Android系统规定屏幕的刷新间隔为16ms,如果这个View在16ms内更新完毕了,就不会卡顿,但是如果逻辑操作太多,16ms内没有更新完毕,剩下的操作就会丢到下一个16ms里去完成,这样就会造成UI线程的阻塞,造成View的运动过程掉帧,自然就会卡顿了。
所以这些原因也就促使了SurfaceView的存在。毕竟,如果是一个游戏,它有可能相当频繁的有更新画面的需求。

二.SurfaceView的优点:

1.SurfaceView的刷新处于主动,有利于频繁的更新画面。

2.SurfaceView的绘制在子线程进行,避免了UI线程的阻塞。

3.SurfaceView在底层实现了一个双缓冲机制,效率大大提升。

4 .SurfaceView可以自定义帧数,例如让这个线程1秒执行50次绘制,那么最后显示的就是50帧

三.SurfaceView的使用

public class AnimView extends SurfaceView implements SurfaceHolder.Callback {


    private SurfaceHolder holder = null; //控制对象

    public AnimView(Context context, AttributeSet attr) {
        super(context, attr);
        holder = getHolder();
        holder.addCallback(this);
        this.setZOrderOnTop(true);
        this.getHolder().setFormat(PixelFormat.TRANSLUCENT);
    }


    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width,
                               int height) {
            //当SurfaceChange的时候
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
           //Surface创建的时候调用  一般用于创建线程 执行一些初始化
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {


    }


    @Override
    public void draw(Canvas canvas) {
        super.draw(canvas);
    }

    public synchronized void doDraw(int tt, int width, int height, Paint p, int argb1, int argb2) {

        Canvas canvas = holder.lockCanvas();
        if(canvas==null) {
            return;
        }
        canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
        p.setColor(argb1);
        canvas.drawCircle(width / 2, height, tt, p);
        if (tt - 300 > 0) {
            p.setColor(argb2);
            canvas.drawCircle(width / 2, height, tt - 300, p);
        }
        holder.unlockCanvasAndPost(canvas);
    }

    public void startAnimation() {
        try {
            Thread t = new Thread(new MyLoop());
            t.start();
            t.join();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {

        }
        return;
    }

    class MyLoop implements Runnable {
        @Override
        public void run() {
            // TODO Auto-generated method stub
            int width = getWidth();
            int height = getHeight();
            Paint p = new Paint();
            int argb1 = Color.argb(80, 250, 0, 0);
            int argb2 = Color.argb(120, 250, 0, 0);
            p.setAntiAlias(true); //反锯齿
            for (int i = 0; i < height + 500; i += 50) {
                doDraw(i,width,height,p,argb1,argb2);
            }
        }
    }

}

1.先看AnimView的构造方法:

 holder = getHolder(); //getHolder获取SurfaceHolder对象用来添加 生命周期回调方法
 holder.addCallback(this);//添加生命周期回调
 this.setZOrderOnTop(true);//设置可以悬浮在其他View上面
 this.getHolder().setFormat(PixelFormat.TRANSLUCENT);//设置悬浮为透明,否则会遮盖下面的View,例如背景之类的。

2.再看SurfaceView的三个生命周期方法

surfaceCreated(SurfaceHolder holder)//当SurfaceView创建的时候,一般在surfaceCreated方法里开启一个子线程。
surfaceChanged(SurfaceHolder holder, int format, int width, int height) //当SurfaceView改变的时候
surfaceDestroyed(SurfaceHolder holder)//当SurfaceView完成销毁的时候,一般用于对线程进行销毁

上面代码中没有用到这三个周期,因为可能会在当activity刚显示的时候并不需要就开启线程去开启SurfaceView动画,因此在上面提供了一个
startAnimation去处理线程的开启。

上面我们说SurfaceView不影响主线程的运行去改变View,而我们做的这个动画是当SurfaceView执行完之后再去执行下一步操作,所以在StartAnimation中t.join();让主线程阻塞起来。

3.在MyLoop任务中定义了一个for循环去画两个圆,分别定义两个不同的透明度的颜色

4.注意看doDraw方法

1.Synchronized关键字
因为SurfaceView允许自定义的线程操作Surface对象执行绘制方法,而你可能同时定义多个线程执行绘制,所以当你获取 SurfaceHolder中的Canvas对象时记得加同步操作,避免两个不同的线程同时操作同一个Canvas对象,当操作完成后记得调用 SurfaceHolder.unlockCanvasAndPost方法释放掉Canvas锁。
Canvas canvas = holder.lockCanvas();//获取锁住的Canvas对象
holder.unlockCanvasAndPost(canvas);//操作完成释放

2.**canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); **
在调用doDraw执行绘制时,因为SurfaceView的特点,它会保留之前绘制的图形,所以你需要先清空掉上一次绘制时留下的图形。(View则不会,它默认在调用View.onDraw方法时就自动清空掉视图里的东西)。

最后看下xml布局



    

最后效果

利用SurfaceView打造一个闪屏页动画_第1张图片
Paste_Image.png
利用SurfaceView打造一个闪屏页动画_第2张图片
Paste_Image.png

动态图:

|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
就是从下面咻咻咻上去

你可能感兴趣的:(利用SurfaceView打造一个闪屏页动画)