Android中SurfaceView和普通view的区别及使用

1 SurfaceView介绍

SurfaceView第一印象它是一个view,因为它继承了View,有两个直接子类GLSurfaceView,VideoView。但根据SDK文档SurfaceView和普通的view又有较大区别。

最显著的区别就是普通view和它的宿主窗口共享一个绘图表面(Surface),SurfaceView虽然也在View的树形结构中,但是它有属于自己的绘图表面,Surface 内部持有一个Canvas,可以利用这个Canvas绘制。

SurfaceView提供一个直接的绘图表面(Surface)嵌入到视图结构层次中。你可以控制这个Surface的格式,大小,SurfaceView负责在屏幕上正确的摆放Surface。简单说就是SurfaceView拥有自己的Surface,它与宿主窗口是分离的。
我们知道窗口中的view共享一个window,window又对应一个Surface,所以窗口中的view共享一个Surface,而SurfaceView拥有自己的Surface。SurfaceView会创建一个置于应用窗口之后的新窗口,SurfaceView相当于在Window上挖一个洞,它就是显示在这个洞里,其他的View是显示在Window上,所以View可以显示在 SurfaceView之上,也可以添加一些层在SurfaceView之上。

SurfaceView的窗口刷新的时候不需要重绘应用程序的窗口而android普通窗口的视图绘制机制是一层一层的,任何一个子元素或者是局部的刷新都会导致整个视图结构全部重绘一次。

对于普通的view,Android中的窗口界面包括多个View组成的View Hierachy的树形结构,只有最顶层的DecorView才对WMS可见,这个DecorView在WMS中有一个对应的WindowState,此时APP请求创建Surface时,会在SurfaceFlinger内部建立对应的Layer。而对于SurfaceView它自带一个Surface,这个Surface在WMS有自己对应的WindowState,在SurfaceFlinger中有自己对应的layer。SurfaceView从APP端看它仍然在View hierachy结构中,但在WMS和SurfaceFlinger中它与宿主窗口是分离的。因此SurfaceView的Surface的渲染可以放到单独线程去做,不会影响主线程对事件的响应。但因为这个Surface不在View hierachy中,它的显示也不受View的属性控制,所以不能进行平移,缩放等变换(对SurfaceView进行ScrollBy,ScrollTo操作没有效果(还有透明度,旋转),普通View进行平移操作,内部内容会移动,SurfaceView进行这些操作内部不会移动。如果对包裹它的ViewGroup进行平移旋转等操作,也无法达到我们想要的效果。同时SurfaceView不能放在类似RecyclerView或ScrollView中,一些View中的特性也无法使用。

SurfaceView不支持平移,缩放,旋转等动画,但是当我们利用SurfaceView测试这些不支持的动画时,如果使用的是7.0 甚至更高版本的Android系统,会发现SurfaceView也支持平移,缩放的动画操作。

View和SurfaceView的区别:

View适用主动更新,SurfaceView 适用被动更新,如频繁的刷新
View在UI线程更新,在非UI线程更新会报错,当在主线程更新view时如果耗时过长也会出错, SurfaceView在子线程刷新不会阻塞主线程,适用于界面频繁更新、对帧率要求较高的情况。
SurfaceView可以控制刷新频率。
SurfaceView底层利用双缓存机制,绘图时不会出现闪烁问题。

双缓冲技术是游戏开发中的一个重要的技术,主要是为了解决 反复局部刷屏带来的闪烁。游戏,视频等画面变化较频繁,前面还没有显示完,程序又请求重新绘制,这样屏幕就会不停地闪烁。双缓冲技术会把要处理的图片在内存中处理好之后,把要画的东西先画到一个内存区域里,然后整体的一次性画出来,将其显示在屏幕上。

2 SurfaceView 使用步骤

使用SurfaceView的步骤:

首先要继承SurfaceView,实现SurfaceHolder.Callback接口。

重写方法:

  • surfaceChanged:surface大小或格式发生变化时触发,在surfaceCreated调用后该函数至少会被调用一次。
  • surfaceCreated:Surface创建时触发,一般在这个函数开启绘图线程(新的线程,不要再这个线程中绘制Surface)。
  • surfaceDestroyed:销毁时触发,一般不可见时就会销毁。

利用getHolder()获取SurfaceHolder对象,调用SurfaceHolder.addCallback添加回调

SurfaceHolder.lockCanvas 获取Canvas对象并锁定画布,调用Canvas绘图,SurfaceHolder.unlockCanvasAndPost 结束锁定画布,提交改变。

3 SurfaceHolder

SurfaceView的双缓冲的机制非常消耗系统内存,Android规定SurfaceView不可见时,会立即销毁SurfaceView的SurfaceHolder,以达到节约系统资源的目的,所以需要利用SurfaceHolder的回调函数对SurfaceHolder进行维护。
提供了三个回调函数让我们知道SurfaceHolder的创建、销毁或者改变
void surfaceDestroyed(SurfaceHolder holder):当SurfaceHolder被销毁的时候回调。
void surfaceCreated(SurfaceHolder holder):当SurfaceHolder被创建的时候回调。
void surfaceChange(SurfaceHolder holder):当SurfaceHolder的尺寸或格式发生变化的时候被回调。

abstract Canvas lockCanvas():
获取一个Canvas对象,并锁定,得到的Canvas对象,就是Surface中一个成员。

** abstract Canvas lockCanvas(Rectdirty):**
仅仅锁定dirty所指定的矩形区域。

abstract void unlockCanvasAndPost(Canvascanvas)
当改动Surface中的数据后,释放同步锁,并提交改变,然后将新的数据进行展示,同一时候Surface中相关数据会被丢失。

public abstract void setType (int type):
设置Surface的类型,高版本中,setType这种方法已经被depreciated了,系统会自动设置。

  • SURFACE_TYPE_NORMAL:用RAM缓存原生数据的普通Surface
  • SURFACE_TYPE_HARDWARE:适用于DMA(Direct memory access )引擎和硬件加速的
  • SurfaceSURFACE_TYPE_GPU:适用于GPU加速的Surface
  • SURFACE_TYPE_PUSH_BUFFERS:表明该Surface不包括原生数据,Surface用到的数据由其它对象提供,在Camera图像预览中就使用该类型的Surface,有Camera负责提供给预览Surface数据,生成图像更流畅。

兼容性:
SurfaceView的兼容性
  Android4.0以下SurfaceView不会自动维护缓冲区,播放视频时,如果使用SurfaceView开发游戏应用,就需要我们自己维护这个缓冲区了。

// 4.0版本之下需要设置的属性
getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

4 SurfaceView的简单使用

绘制圆形:

public class SurfaceViewDemo extends SurfaceView implements SurfaceHolder.Callback{
    private SurfaceHolder mSurfaceHolder;
    private Canvas mCanvas;
    private Paint paint;

    public SurfaceViewDemo(Context context) {
        this(context,null,0);
    }

    public SurfaceViewDemo(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

    public SurfaceViewDemo(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        mSurfaceHolder = getHolder();
        mSurfaceHolder.addCallback(this);
        setFocusable(true);
        setFocusableInTouchMode(true);
        this.setKeepScreenOn(true);
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(Color.RED);
        paint.setStrokeWidth(5);
        paint.setStyle(Paint.Style.STROKE);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        System.out.println("=========surfaceCreated========");
        new Thread(new Runnable() {
            @Override
            public void run() {
                draw();
            }
        }).start();
    }

    private void draw() {
        try {
            System.out.println("============draw========");
            mCanvas = mSurfaceHolder.lockCanvas();
            mCanvas.drawCircle(500,500,300,paint);
            mCanvas.drawCircle(100,100,20,paint);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (mCanvas != null)
                mSurfaceHolder.unlockCanvasAndPost(mCanvas);
        }
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        System.out.println("=========surfaceChanged========");
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        System.out.println("=========surfaceDestroyed========");
    }
}

回调函数的调用:
首次进入:
=surfaceCreated
=surfaceChanged
====draw
点击Home键:
=surfaceDestroyed

再次回到原来页面:
=surfaceCreated
=surfaceChanged
====draw

所以当SurfaceView不可见时会销毁SurfaceHolder,再次进入会重新调用surfaceCreated生成新的SurfaceHolder。surfaceChanged函数在surfaceCreated调用后该函数至少会被调用一次。

SurfaceView播放视频,不要忘记存储权限
mediaPlayer.setDisplay(getHolder());
视频资源为利用模拟器录制的mp4视频

public class SurfaceViewDemo2 extends SurfaceView implements SurfaceHolder.Callback{
    private SurfaceHolder mSurfaceHolder;
    private Canvas mCanvas;
    private Paint paint;
    private MediaPlayer mediaPlayer;

    public SurfaceViewDemo2(Context context) {
        this(context,null,0);
    }

    public SurfaceViewDemo2(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

    public SurfaceViewDemo2(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        mSurfaceHolder = getHolder();
        mSurfaceHolder.addCallback(this);
        setFocusable(true);
        setFocusableInTouchMode(true);
        this.setKeepScreenOn(true);
        setZOrderOnTop(true);
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(Color.RED);
        paint.setStrokeWidth(5);
        paint.setStyle(Paint.Style.STROKE);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        System.out.println("=========surfaceCreated========");
        new Thread(new Runnable() {
            @Override
            public void run() {
                //draw();
              play();
            }
        }).start();
    }

    private void draw() {

        try {
            System.out.println("============draw========");
            mCanvas = mSurfaceHolder.lockCanvas();
            mCanvas.drawCircle(500,500,300,paint);
            mCanvas.drawCircle(100,100,20,paint);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (mCanvas != null)
                mSurfaceHolder.unlockCanvasAndPost(mCanvas);
        }
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        System.out.println("=========surfaceChanged========");
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        System.out.println("=========surfaceDestroyed========");
        if (mediaPlayer != null ){
            stop();
        }
    }

    protected void stop() {
        if (mediaPlayer != null && mediaPlayer.isPlaying()) {
            mediaPlayer.stop();
            mediaPlayer.release();
            mediaPlayer = null;
        }
    }

    protected void play() {
        String path = "/sdcard/DCIM/Camera/VID_20190110_102218.mp4";
        File file = new File(path);
        if (!file.exists()) {
            return;
        }
            try {
                mediaPlayer = new MediaPlayer();
                mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
                // 设置播放的视频源
                mediaPlayer.setDataSource(file.getAbsolutePath());
                // 设置显示视频的SurfaceHolder
                mediaPlayer.setDisplay(getHolder());

                mediaPlayer.prepareAsync();
                mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {

                    @Override
                    public void onPrepared(MediaPlayer mp) {
                        mediaPlayer.start();
                    }
                });
                mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {

                    @Override
                    public void onCompletion(MediaPlayer mp) {
                        replay();
                    }
                });

                mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {

                    @Override
                    public boolean onError(MediaPlayer mp, int what, int extra) {
                        play();
                        return false;
                    }
                });
            } catch (Exception e) {
                e.printStackTrace();
            }
    }

    protected void replay() {
        if (mediaPlayer!=null){
         mediaPlayer.start();
        }else{
            play();
        }
    }

    protected void pause() {
        if (mediaPlayer != null && mediaPlayer.isPlaying()) {
            mediaPlayer.pause();
        }else{
            mediaPlayer.start();
        }
    }
}

补充 对SurfaceView进行平移,旋转等操作

添加 mSurfaceView.scrollBy(10,10);
效果如下:完全没有效果

对包裹它的viewgroup添加缩放动画。

mContainer.animate().scaleX(0.4f).scaleY(0.7f);

此时拍摄出来的照片为:

拍摄出来的照片完全没有受缩放的影响。

到此这篇关于Android中SurfaceView和普通view的区别及使用的文章就介绍到这了,更多相关Android中SurfaceView和普通view内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Android Fragment中使用SurfaceView切换时闪一下黑屏的解决办法

    重构了下之前自己的一个新闻客户端,全部使用了Fragment来进行页面切换,只有一个入口Activity作为程序的启动Activity,其中有一个界面需要调用摄像头识别二维码,于是就会用到SurfaceView进行预览,那么问题来了,当切换到对应的Fragment时,屏幕会黑一下,黑了1秒左右就显示出正常的界面,而且这种现象只有第一次进入该Fragment才会出现,之后进入都不会出现,解决方法是无意在github上看到了,试了一下,可以行的通,下面贴出解决方法. 方法一.在Activity的on

  • Android截屏SurfaceView黑屏问题的解决办法

    最近项目中有截屏的需求,普通的view截屏方法网上一搜一大把,但是SurfaceView截屏黑屏问题很多文章说的并不清楚,自己参考了一些别的博客,再加上自己的思考,算是找到了一种解决方案. 1.首先看我们一般是怎么用SurfaceView的 public class SuperSurfaceView extends SurfaceView implements SurfaceHolder.Callback { SurfaceHolder surfaceHolder; public SuperSu

  • Android SurfaceView预览变形完美解决方法

    这个问题百度上一搜一大把,基本上都是说找到和SurfaceView的比例相近的camera预览尺寸,但是发现预览时候还是差了点意思,具体看下面这个回调就知道是为什么了. @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Log.i(TAG, "surfaceChanged: " + width + " " + height);

  • Android切换至SurfaceView时闪屏(黑屏闪一下)以及黑屏移动问题的解决方法

    1.最近的项目中,有一个Activity用到Fragment+ViewPager,其中一个fragment中实现了视频播放的功能,包含有SurfaceView.结果,每次打开程序第一次进入到该Activity时都会闪屏黑一下.原因就出在SurfaceView. 详解: I think I found the reason for the black flash. In my case I'm using a SurfaceView inside a Fragment and dynamicall

  • android图像绘制(四)自定义一个SurfaceView控件

    自定义控件(类似按钮等)的使用,自定义一个SurfaceView. 如某一块的动态图(自定义相应),或者类似UC浏览器下面的工具栏. 如下图示例:  自定义类代码: 复制代码 代码如下: public class ImageSurfaceView extends SurfaceView implements Callback{ //用于控制SurfaceView private SurfaceHolder sfh; private Handler handler = new Handler();

  • Android利用SurfaceView实现下雨的天气动画效果

    首先是最终实现的效果图: 先分析一下雨滴的实现: 每个雨滴其实就是一条线,通过 canvas.drawLine() 绘制 线(雨滴)的长度.宽度.下落速度.透明度以及位置都是在一定范围内随机生成 每 draw 一次然后改变雨滴的位置然后重绘即可实现雨滴的下落效果 分析完了,那么可以直接写一个类直接继承 View ,然后重写 onDraw() 吗?可以看到效果图中的雨滴的下落速度很快,那么意味着每一帧都要调用 onDraw() 一次使其重新绘制一次,假如你的 onDraw() 方法里面的渲染代码稍

  • Android中SurfaceView用法简单实例

    本文实例讲述了Android中SurfaceView用法.分享给大家供大家参考,具体如下: 这里贴上一个小程序代码,主要运用SurfaceView来实现在屏幕上画一个圆,你可以通过按方向键和触摸屏幕来改变圆的位置 代码: Activity: package com.view; import android.app.Activity; import android.os.Bundle; import android.view.Window; import android.view.WindowMa

  • Android中TextureView与SurfaceView用法区别总结

    SurfaceView和TextureView均继承于android.view.View 与其它View不同的是,两者都能在独立的线程中绘制和渲染,在专用的GPU线程中大大提高渲染的性能. 一.SurfaceView专门提供了嵌入视图层级的绘制界面,开发者可以控制该界面像Size等的形式,能保证界面在屏幕上的正确位置. 但也有局限: 由于是独立的一层View,更像是独立的一个Window,不能加上动画.平移.缩放: 两个SurfaceView不能相互覆盖. 二.TextureView更像是一般的

  • Android提高之SurfaceView的基本用法实例分析

    前文介绍了Android中MediaPlayer用法的时候稍微介绍了SurfaceView,SurfaceView由于可以直接从内存或者DMA等硬件接口取得图像数据,因此是个非常重要的绘图容器,这次我就来较为详细的介绍SurfaceView的用法.网上介绍SurfaceView的用法有很多,写法也层出不同,例如继承SurfaceView类,或者继承SurfaceHolder.Callback类等,这个可以根据功能实际需要自己选择,本文所述方法就直接在普通的用户界面调用SurfaceHolder的

  • Android中SurfaceView和view画出触摸轨迹

    一.引言          想实现一个空白的画板,上面可以画出手滑动的轨迹,就这么一个小需求.一般就来讲就两种实现方式,view或者surfaceview.下面看看两种是如何实现的. 二.实现原理          先简单说一下实现原理:        (1)用一张白色的Bitmap作为画板        (2)用canvas在bitmap上画线        (3)为了画出平滑的曲线,要用canvas的drawPath(Path,Paint)方法.        (4)同时使用贝塞尔曲线来使曲

随机推荐