android之surfaceView学习

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android之surfaceView学习相关的知识,希望对你有一定的参考价值。

  最近做项目遇到一个问题,需要在屏幕上实时的显示手的坐标,这样话就涉及到一个实时画图的问题了。对于实时更新UI这个问题,懂点android的都知道,android的UI更新都需要在主线程中更新,但是如果将一个实时绘图的操作放在主线程,必定会出现阻塞主线程的问题,即便是不阻塞主线程,也会降低程序运行的速度。因此,我就想到android的一个控件,surfaceView。这个控件在官网上是这样描述的:

  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提供了UI线程。可以自己更新UI,因此,这样我们在surfaceView中进行实时的绘画,然后通过更改其中的绘画的数据,既可以实现我们想要的实时的更新UI的这个问题了,并且消耗较小的资源。那么接下来我们来具体谈下实现的问题。

  那么如何使用surfaceView实现我们的需求呢?首先我们需要自定义一个控件,并且继承surfaceView这个类。并且要在其中实现SurfaceHolder.Callback的接口。之所以实现接口,是因为我们要确保我们所做的操作需要在surfaceView创建后才能开始。然后我们来具体看下这个函数需要实现的三个函数。

new SurfaceHolder.Callback() {
    @Override
    public void surfaceCreated(SurfaceHolder surfaceHolder) {
    //创建时需要执行的操作
    }

    @Override
    public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
    //surfaceView大小改变时需要执行的操作
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
    //销毁时进行的操作。
    }
}

  然后我们如果要绘制内容,并且实时的更新,需要在surfaceCreated中开一个线程,然后实时的进行绘制。这样即完成我们所需要的需求了。但是在绘制的时候需要有一些注意的地方。这个在接下来呈现出代码后我们来具体分析和具体的看,接下来是自定义view的具体的代码。我们来具体的看下。

public class MyView extends SurfaceView {
    private final String TAG = "acmTest";
    private SurfaceHolder mSurfaceHolder;
    private Canvas mCanvas;//画布
    public boolean isRunning;//用来标识是否开启绘画的线程。
    Context context;
    int positionX;//所绘制图形的x坐标
    int positionY;//所绘制图形的y坐标
    public MyView(Context context) {
        super(context);
        init();
    }
    public MyView(Context context, AttributeSet attributeSet)
    {
        super(context,attributeSet);
        this.context = context;
        init();
    }
  //初始化surfaceView,在这个函数中,实现surfaceHolder.Callback
    public void init()
    {
  setBackgroundColor(Color.parseColor("#6A14b7f5"));//设置surfaceView的背景颜色
  setZOrderOnTop(true);//使surfaceview放到最顶层
  getHolder().setFormat(PixelFormat.TRANSLUCENT);//使窗口支持透明度
  mSurfaceHolder = getHolder(); mSurfaceHolder.addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder surfaceHolder) {
          //启动开始绘画的线程,这个线程中的函数不需要再在主线程中执行,可以直接执行并更新UI。
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        while (isRunning)
                        {
                            drawPoint();
                        }
                    }
                }).start();
            }

            @Override
            public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {

            }

            @Override
            public void surfaceDestroyed(SurfaceHolder surfaceHolder) {

            }
        });
    }
  //具体绘画的操作
    public void drawPoint()
    {
     //需要注意这里需要通过这个方式获得canvas,不能够自己申明,为了保证从始至终用的一个canvas。
        mCanvas = mSurfaceHolder.lockCanvas();
        if (mCanvas != null) {
            try {
                mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);//是清理上次画的内容,这个根据需求选择是否添加。因为我的需求是跟踪位置,如果不加,会将上次所画的内容保留。
                Paint paint = new Paint();//声明画笔,在此基础上进行绘制
                paint.setColor(Color.RED);//为红色画笔
                mCanvas.drawCircle(positionX,positionY,10,paint);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                mSurfaceHolder.unlockCanvasAndPost(mCanvas);//提交后会将提交的画布更新,然后显示
            }
        }


    }
    public void setPosition(int positionX,int positionY) {
        this.positionX = positionX;
        this.positionY = positionY;
    }

}

  

   这是通过继承surfaceView来进行实现实时的更新UI,然后占用少量内存。值得注意的时候,我们在初始化的时候需要考虑啊init的前三行代码,第一个设置背景颜色,不要再xml中设置,需要在这设置,否则默认背景为纯黑的。然后后两句是支持为透明的颜色,和保持能够在布局的最上方。

     还有一点就是在获取画布这,需要严格的按照规定,先获取,再提交更新,在其中不可重新声明,并且提交和获取的需要为同一个canvas。

  







以上是关于android之surfaceView学习的主要内容,如果未能解决你的问题,请参考以下文章

Android SurfaceView导致屏幕闪烁

Android 音视频开发之基础篇 使用 SurfaceView绘制一张图片

Android SurfaceView入门学习

Android游戏开发之七(游戏开发中需要的样式)再次剖析游戏开发中对SurfaceView中添加组件方案!

Android之TextureView浅析

丑陋的片段过渡到带有覆盖的surfaceview