Краткая SurfaceView и часто задаваемые вопросы

  В разработке Android, SurfaceView обычно не используется, но встречается несколько фотографий или видео-плеер, и т.д., мы должны использовать. Следующее описание управления просты, и описываются проблемы, возникающие в процессе обеспечения качества в виде ответов!

Отказ от ответственности: SurfaceView по принципу статьи описывает основную ссылку: HTTPS: // blog.csdn.net/luoshengyang/article/details/8661317

Во-первых, использование сценариев:

  Обыкновенное Android контролирует их интерфейс рисуется в основном потоке приложения. Приложения в дополнение к рисунку, но и необходимость своевременного реагирования на ввод пользователя, в противном случае, это приведет к ANR. Для предотвращения ANR, некоторые игры экран, изображение предварительного просмотра, воспроизведения видео UI сложным и требует умения эффективно чертежный вид, что не подходит для визуализации пользовательского интерфейса в главном потоке приложения. Время, необходимые для создания такого вида на отдельный рисунок поверхность, и использует отдельный поток, чтобы привлечь эти UI представления, в этом случае вы должны использовать развитие SurfaceView.

Во-вторых, то, что SurfaceView что?

1. Определение:

  Унаследованные от View, класса , построенный специально для вытяжки поверхности. Вы можете контролировать формат и размер поверхности. SurfaceView управление нарисованным положения по поверхности.

  Поверхность сортировка глубины (Z-заказано), указывая, что он всегда находится в задней части, где их окно. SurfaceView обеспечивает видимую область, только на поверхность видна часть содержимого в этой области видно, какой-то невидимый вне видимой области. Дисплей макета поверхности влияет иерархию зрения, ее вид родственного узла будет отображаться в верхней части. Это означает, что содержимое поверхности затененных вид его собрата, эта функция может быть использована, чтобы поместить крышку (накладки) (например, текст и кнопки и другие элементы управления). Обратите внимание, что если прозрачная поверхность управления выше, то она меняется каждый раз, когда рамка приведет к перерасчету прозрачности и ее контроля верхнего уровня, которые могут повлиять на производительность.

2, ответственный за SurfaceFlinger рисовать интерфейс:

  Упомянутые поверхности, для этого требуется краткий обзор SurfaceFlinger, ответственный за отрисовку службы приложений Android UI. служба SurfaceFlinger работает в технологической системе Android системы, которая отвечает за управление буфером кадров Android системы (Буфер кадров). Android приложений, чтобы иметь возможность опираться на свою собственную систему буфера кадра UI, необходимо общаться с конкретными процессами обслуживания SurfaceFlinger резюмировать следующим образом:

    (1) Android приложение запрашивает создание службы SurfaceFlinger поверхности;

    После того, как (2) создание поверхности, Android приложение рисует свой собственный пользовательский интерфейс на вершине;

     (3) услуга будет снова запросить SurfaceFlinger UI уже обращается к поверхности отображения на дисплее устройства.

    

 

  В общем случае, каждое окно службы соответствует SurfaceFlinger слоя, он используется для описания поверхности чертежа. Для тех окон, имеющих SurfaceView для каждой службы SurfaceView SurfaceFlinger также соответствует в отдельном слое или LayerBuffer, он используется для описания одной поверхности рисунка, чтобы отличить его от хозяина чертежа поверхности окна.

  С отдельной чертежной поверхности, так что SurfaceView ПИ можно сделать в отдельном потоке. Кроме того, поскольку основной поток не будет занимать ресурсы, SurfaceView с одной стороны, позволяет комплексный и эффективный пользовательский интерфейс, с другой стороны, не приводит к отсутствию своевременной реакции на пользовательский ввод.

3, ввести единый переменный член SurfaceView только не понимают :()

  Переменные-члены класса SurfaceView типа описана mRequestedType SurfaceView рисунок поверхности, следующие четыре значения: Взятый источник.  

SURFACE_TYPE_NORMAL:用RAM缓存原生数据的普通Surface 
SURFACE_TYPE_HARDWARE:适用于DMA(Direct memory access )引擎和硬件加速的Surface 
SURFACE_TYPE_GPU:适用于GPU加速的Surface 
SURFACE_TYPE_PUSH_BUFFERS:表明该Surface不包含原生数据,Surface用到的数据由其他对象提供,在Camera图像预览中就使用该类型的Surface,有Camera负责提供给预览Surface数据,这样图像预览会比较流畅。如果设置这种类型则就不能调用lockCanvas来获取Canvas对象了。

  需要说明的是,虽然SurfaceHolder中已经不建议使用setType()方法了,我们自己写demo也可以看到该方法已经被声明为@Deprecated

    /**
     * Sets the surface's type.
     *
     * @deprecated this is ignored, this value is set automatically when needed.
     */
    @Deprecated
    public void setType(int type);

  但是,在使用自定义的SurfaceView实现视频播放时,还为了兼容低版本,仍需要设置setType

  //设置SurfaceHolder类型,为了兼容低版本,需要设置此类型,否则播放时,只有声音,而没有图像
    getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

4、关于View的setWillNotDraw():

  查看SurfaceView的源码可知,在其构造函数中调用了setWillNotDraw(true);该方法会导致draw()、onDraw()都不执行。

 /**
     * If this view doesn't do any drawing on its own, set this flag to
     * allow further optimizations. By default, this flag is not set on
     * View, but could be set on some View subclasses such as ViewGroup.
     *
     * Typically, if you override {@link #onDraw(android.graphics.Canvas)}
     * you should clear this flag.
     *
     * @param willNotDraw whether or not this View draw on its own
     */
    public void setWillNotDraw(boolean willNotDraw) {
        setFlags(willNotDraw ? WILL_NOT_DRAW : 0, DRAW_MASK);
    }

  自定义SurfaceView,运行结果如下:

(1)默认执行结果:

(2)在CommSurfaceView的构造函数中,手动设置setWillNotDraw(false),运行结果如下:

三、SurfaceView的实现过程:(详情请参看原文)

  SurfaceView的整个实现过程分为三步:绘图表面的创建、在宿主窗口设置一块透明区域用于显示、在独立线程中进行自己UI的绘制。

  1.、创建独立的绘图表面;

  2、在宿主窗口上设置一块透明区域来显示自己(使绘制的UI可见);【SurfaceView的窗口类型所表示的Z轴位置小于Activity窗口的Z轴位置

  3、在独立的线程中进行其UI绘制。

四、SurfaceView的使用:

  由于SurfaceView的底层实现过程已经进行了封装,并为开发者提供了上层使用接口,即:系统给SurfaceView提供了一个专门绘图的Surface,嵌入在了SurfaceView视图层中,画面在Surface中绘制完成,在SurfaceView中通过获得SurfaceHolder的对象,管理并展示Surface的数据内容。所以,开发时只需按照下述步骤即可:  

1、一般都是重新自定义SurfaceView,起到封装的作用:

(1)继承SurfaceView;

(2)利用getHolder()获取SurfaceHolder对象;

(3)给SurfaceHolder对象添加实现SurfaceHolder.Callback接口的对象,即添加回调函数SurfaceHolder.addCallback(callback);

(4)重写Callback的三个方法:surfaceChanged,surfaceCreated,surfaceDestroyed;

(5)利用SurfaceHolder对象设置其类型、格式、大小等。

 

  (一)在SurfaceView上进行UI绘制的流程如下:  

(1). 在绘图表面的基础上建立一块画布,即获得一个Canvas对象。

(2). 利用Canvas类提供的绘图接口在前面获得的画布上绘制任意的UI。

(3). 将已经填充好UI数据的画布缓冲区提交给SurfaceFlinger服务,以便SurfaceFlinger服务可以将它合成到屏幕上去。

  代码格式:
  Canvas c=surfaceHolder.lockCanvas();
  .....具体画图操作......
  surfaceHolder.unlockCanvasAndPost(c);

  (二)利用SurfaceView播放视频:

    Android中有三种实现形式,均可实现视频的播放:

    • 原生VideoView(继承SurfaceView)
    • 直接使用SurfaceView
    • 自定义播放控件(继承SurfaceView)

具体视频的播放均是使用的MediaPlayer,只是在自定义及VideoView中,被封装在各自的类中,对调用者不可见。

视频的画面一般由两部分组成:视频内容画面+上层操作布局(标题、播放进度、时长、暂停等控件)。

为了配合上层操作布局,一般都会实现MediaPlayerControl接口(当然,完全可以自定义),获取视频播放相关信息。

 

    以原生VideoView为例,展现代码格式:
    VideoView  nativeVideoView = (VideoView) findViewById(R.id.nativeVideoView);
    MediaController  nativeMediaController = new MediaController(this);

    //设置播放路径:实际内部封装了MediaPlayer的创建及数据源、监听事件、播放类型、画面显示等的设置,即视频播放前的准备工作,
    //其中 mMediaPlayer.setDisplay(mSurfaceHolder);即是画面相关的设置
    nativeVideoView.setVideoPath(path);
    nativeVideoView.setMediaController(nativeMediaController);     nativeVideoView.requestFocus();     nativeVideoView.start();//内部执行mediaPlayer.start()

五、常见问题:(以VideoView为例)

  1、在播放视频时,滑动进度,会出现闪屏(画面一直在变)?

  原因:原生VideoView使用SeekBar,在滑动进度的过程中,在OnSeekBarChangeListener的onProgressChanged()中调用了MediaPlayerControl的seekTo(),即实时更新视频画面,出现闪屏。

  

 1 源码:frameworks\base\core\java\android\widget\MediaController.java
 2 
 3     private final OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
 4         @Override
 5         public void onStartTrackingTouch(SeekBar bar) {
 6             show(3600000);
 7 
 8             mDragging = true;
 9 
10             // By removing these pending progress messages we make sure
11             // that a) we won't update the progress while the user adjusts
12             // the seekbar and b) once the user is done dragging the thumb
13             // we will post one of these messages to the queue again and
14             // this ensures that there will be exactly one message queued up.
15             removeCallbacks(mShowProgress);
16         }
17 
18          @Override
19         public void onProgressChanged(SeekBar bar, int progress, boolean fromuser) {
20             if (!fromuser) {
21                 // We're not interested in programmatically generated changes to
22                 // the progress bar's position.
23                 return;
24             }
25 
26             long duration = mPlayer.getDuration();
27             long newposition = (duration * progress) / 1000L;
28             mPlayer.seekTo( (int) newposition);   //实时更新播放画面
29             if (mCurrentTime != null)
30                 mCurrentTime.setText(stringForTime( (int) newposition));
31         }
32 
33         @Override
34         public void onStopTrackingTouch(SeekBar bar) {
35             mDragging = false;
36             setProgress();
37             updatePausePlay();
38             show(sDefaultTimeout);
39 
40             // Ensure that progress is properly updated in the future,
41             // the call to show() does not guarantee this because it is a
42             // no-op if we are already showing.
43             post(mShowProgress);
44         }
45     };    

  处理:解决此问题,可将视频画面的更新放在滑动结束(即onStopTrackingTouch()中)即可。

  引入新的问题:若用户看视频,需要从30分钟开始播放,但是具体位置不确定,因此需要在30分钟附近查看,此时则需要随着用户的滑动,画面切换,
但是,又不能出现上述闪屏问题。

  新问题的解决方案:可以监听两次滑动的时间间隔,然后画面更新(例:两次间隔500ms更新一次画面)

  测试结果:证实可以。具体为在滑动过程中即onProgressChanged()中进行如下操作:  

//startTouchTime:首次为拖动开始的时间,之后为每次更新时重新赋值更新时的时间         
//changeTouchTime:时刻获取滑动变化的时间
            
            if(mDragging && changeTouchTime - startTouchTime >= 500){
                Log.e(TAG, "prepare fromuser onProgressChanged ++++++++++++++  ");
                startTouchTime = changeTouchTime;                    //重置,最近一次修改
                mPlayer.seekTo(newPosition);
                if (currentTime != null) {
                    currentTime.setText(stringForTime(newPosition));
                }
            }

  2、横竖屏切换时,视频从头播放,不会连续。

  原因:默认情况下, Activity在横竖屏切换时会重新创建,因此视频重播.结合是否设置属性对Activity的生命周期的影响,即可明白。

  解决方案:横竖屏切换必须在AndroidManifest.xml中设置属性android:configChanges="screenSize|orientation"。

  3、播放完成,停止时,播放进度未显示在最后?(该问题是我的项目中出现的,并非通用问题)

  现象:由于视频播放完成,做退出处理,一般看不到此现象。  

  原因:根据视频“剪辑”时,进度可以完全显示。查找原因,发现剪辑视频时,整个过程中进度条可见,一直在实时更新界面进度,因此可完全显示;而普通播放视频时,进度条等播放布局是可隐藏的,而代码中在MoviePlayer的setProgress()中,判断当进度等布局不可见时,直接返回,而不更新界面显示进度,因此,在播放完成时,界面显示的是最近一次布局可见时的进度(一般不在最后)。

  4、视频播放过程中,操作暂停播放后点击Home键退回桌面,再次进入会出现黑屏?【注:播放情况下正常是由于播放再次进入执行了继续播放,即刷新了界面】  

  原因:点击Home键时,SurfaceView中的Surface被销毁(从执行回调函数surfaceDestroy()即可看出),播放画面显示为黑色。

  测试现象:

      (1) 看到黑屏效果,是因为Activity背景被设置为黑色,在去掉背景色之后,发现仅有播放视频区域显示黑色;

      (2)将窗体设置为白色,按上述操作,发现仅视频播放区域为黑色;将主题设置为android:Theme.Translucent,按上述操作,发现仅视频播放区域为透明,即可看见MainActivity。这也可以解释SurfaceView的实现原理中的第二条,在宿主窗口中设置一块透明区域显示自己。运行效果图如下:MainActivity界面(左侧)、右侧为视频播放界面。

        

  

  得出结论:Surface被销毁后,播放区域显示的背景为当前主题Theme中的配置。

  5、其他问题:SurfaceView、GLSurfaceView、SurfaceTexture、TextureView的区别

  SurfaceView:Android1.0就有,继承自View,因为有单独的Surface【不在View hierachy】中,它的显示也不受View的属性控制,所以不能进行平移,缩放等变换,也不能放在其它ViewGroup中,一些View中的特性也无法使用。

  GLSurfaceView:Android1.5引入,继承自SurfaceView,在SurfaceView的基础上,它加入了EGL的管理,并自带了渲染线程。另外它定义了用户需要实现的Render接口,提供了用Strategy pattern更改具体Render行为的灵活性。作为GLSurfaceView的Client,只需要将实现了渲染函数的Renderer的实现类设置给GLSurfaceView即可。

  SurfaceTexture:Android 3.0(API level 11)加入。单独的类,和SurfaceView不同的是,它对图像流的处理并不直接显示,而是转为GL外部纹理,因此可用于图像流数据的二次处理(如Camera滤镜,桌面特效等)。比如Camera的预览数据,变成纹理后可以交给GLSurfaceView直接显示,也可以通过SurfaceTexture交给TextureView作为View heirachy中的一个硬件加速层来显示。

  TextureView:在4.0(API level 14)中引入。继承自View,它可以将内容流直接投影到View中,可以用于实现Live preview【实时预览】等功能.和SurfaceView不同,它不会在WMS中单独创建窗口,而是作为View hierachy中的一个普通View,因此可以和其它普通View一样进行移动,旋转,缩放,动画等变化。值得注意的是TextureView必须在硬件加速的窗口中。它显示的内容流数据可以来自App进程或是远端进程。

六、总结:

  SurfaceView是android系统中特殊的视图,继承自View。它具有独立的绘图表面,但是,由于其窗口类型所表示的Z轴位置小于Activity窗口的Z轴位置,因此,为了使其可见,需要在宿主窗口申请设置一块透明区域来显示。一切条件就绪后,就可以在独立的线程中进行复杂的UI绘制,且不会影响应用程序的主线程响应用户输入。

  

рекомендация

отwww.cnblogs.com/sparrowlhl/p/11227135.html