surfaceview

首先是api,结构如下

java.lang.Object

   ↳android.view.View
    ↳android.view.SurfaceView
Known Direct Subclasses

videoview是子类,所以可以用mediaplayer加上surfaceview来直接播放视频,后面再说这个例子。


 

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) andsurfaceDestroyed(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:

 

surface会被created当surfaceview所在的窗口可见,shown和hidden的时候分别会调用surfacecreated和surfaceDestroyed方法,所以在包含的了surfaceview的界面在调用back键重新打开的时候后有surfaceDestroyed--》new XXview--》surfacecreated,而点击home键重新打开surfaceDestroyed--》surfacecreated,所以要注意后台绘制线程的什么周期。

后台线程的绘制要一定的策略,所有的surfaceview的回调方法都会在surfaceview所在window中来响应,也就是常说的UI主线程,另外绘制线程应该在surfacecreated和surfaceDestroyed方法之间进行绘制,这样才是有效的

 

 

 

我们是通过调用View的invalidate方法通知系统需要重新绘制View,然后它就会调用View.onDraw方法,它的帧数只能靠系统的来进行控制,SurfaceView和View最本质的区别在于,surfaceView是在一个新起的单独线程中可以重新绘制画面而View必须在UI的主线程中更新画面

小总结一下就是这些不同之处

1. SurfaceView允许其他线程更新视图对象(执行绘制方法)而View不允许这么做,它只允许UI线程更新视图对象。

2. SurfaceView是放在其他最底层的视图层次中,所有其他视图层都在它上面,所以在它之上可以添加一些层,而且它不能是透明的。

3. 它执行动画的效率比View高,而且你可以控制帧数。

4. 因为它的定义和使用比View复杂,占用的资源也比较多,除非使用View不能完成,再用SurfaceView否则最好用View就可以。(贪吃蛇,俄罗斯方块,棋牌类这种帧数比较低的可以使用View做就好)

 

 

 

 

为什么是SurfaceView呢?Surface的意思是表层,表面的意思,那么SurfaceView就是指一个在表层的View对象。为什么说是在表层呢,这是因为它有点特殊跟其他View不一样,其他View是绘制在表层外,而它就是充当表层对象。假设你要在一个球上画画,那么球的表层就当做你的画布对象,你画的东西会挡住它的表层,我们默认没使用SurfaceView,那么球的表层就是空白的,如果我们使用了SurfaceView,我们可以理解为我们拿来的球本身表面就具有纹路,你是画再纹路之上的,如果你画的是半透明的,那么你将可以透过你画的东西看到球面本身的纹路。SDK的文档说到:SurfaceView就是在Window上挖一个洞,它就是显示在这个洞里,其他的View是显示在Window上,所以View可以显式在SurfaceView之上,你也可以添加一些层在SurfaceView之上。

 

 

如何使用SurfaceView?

首先SurfaceView也是一个View,它也有自己的生命周期。因为它需要另外一个线程来执行绘制操作,所以我们可以在它生命周期的初始化阶段开辟一个新线程,然后开始执行绘制,当生命周期的结束阶段我们插入结束绘制线程的操作。这些是由其内部一个SurfaceHolder对象完成的。SurfaceHolder,顾名思义,它里面保存了一个队Surface对象的引用,而我们执行绘制方法就是操作这个Surface,SurfaceHolder因为保存了对Surface的引用,所以使用它来处理Surface的生命周期,说到底SurfaceView的生命周期其实就是Surface的生命周期,因为SurfaceHolder保存对Surface的引用,所以使用SurfaceHolder来处理生命周期的初始化。首先我们先看看建立一个SurfaceView的大概步骤。

surfaceCreated方法,是当SurfaceView被显示时会调用的方法,所以你需要再这边开启绘制的线程,surfaceDestroyed方法是当SurfaceView被隐藏会销毁时调用的方法,在这里你可以关闭绘制的线程。

还有一个,如果想让一个view在surfaceview之上,用个layout把它们包一包就可以了,毕竟都是view体系下的。

 

在SurfaceView中内置一个LoopThread线程,这个线程的作用就是用来绘制图形,在SurfaceView中实例化一个LoopThread实例,一般这个操作会放在SurfaceView的构造方法中。然后通过在SurfaceView中的SurfaceHolder的生命周期回调方法中插入一些操作,当Surface被创建时(SurfaceView显示在屏幕中时),开启LoopThread执行绘制,LoopThread会一直刷新SurfaceView对象,当SurfaceView被隐藏时就停止改线程释放资源。这边有几个地方要注意下:

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

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

3. 记得在回调方法:onSurfaceDestroyed方法里将后台执行绘制的LoopThread关闭,这里是使用join方法。这涉及到线程如何关闭的问题,多数人建议是通过一个标志位:isRunning来判断线程是否该停止运行,如果你想关闭线程只需要将isRunning改成false即可,线程会自动执行完run方法后退出。

 

当使用surfaceView 由于是在新的线程中更新画面所以不会阻塞你的UI主线程。但这也带来了另外一个问题,就是事件同步。比如你触屏了一下,你需要surfaceView中thread处理,一般就需要有一个event queue的设计来保存touch event,这会稍稍复杂一点,因为涉及到线程同步。 

 

这次就介绍SurfaceView的双缓冲使用。双缓冲是为了防止动画闪烁而实现的一种多线程应用,基于SurfaceView的双缓冲实现很简单,开一条线程并在其中绘图即可。本文介绍基于SurfaceView的双缓冲实现,以及介绍类似的更高效的实现方法(绘制的时候clip下区域啊,lockCanvas(new Rect())能确定的区域的时候就不要写null了啊,这样只绘制部分,其他的地方用缓存的就可以了)。

为什么要开两个线程一个读一个画,而不去开两个线程像左边那样都“边读边画”呢?因为SurfaceView每次绘图都会锁定Canvas,也就是说同一片区域这次没画完下次就不能画,因此要提高双缓冲的效率,就得开一条线程专门画图,开另外一条线程做预处理的工作(这里要注意同步,如果绘制的太快,图像没变,浪费,如果读取图片太快,那么可能会漏画部分图片)。

 

下面TODO

 

view的自绘注意canvas.save和canvas.restore

 

 

下面是自定义videoview, mediaPlayer.setDisplay(surfaceHolder)连接上绘制的与控制的接口

 

一般情况下,android界面的绘制和更新,要交给主ui线程来操作,通过Handler机制。但是播放视频,需要比较优先和实时的改变和绘制界面。android提供了使用单独线程绘制UI的机制,就是SurfaceView。

使用SurfaceView,需要实现SurfaceHolder.Callback接口:

* surfaceCreated,在Surface(SurfaceView内部包含一个Surface实例)创建后,会立即调用该方法,可在该方法中做绘制界面相关的初始化工作;
* surfaceChanged,当Surface的状态发生变化,比如大小,会调用该方法,在surfaceCreated方法调用过至少会调用一次该方法;
* surfaceDestroyed,当销毁Surface的时候调用。

SurfaceHolder开发者不能直接操作Surface实例,要通过SurfaceHolder,在SurfaceView中可以通过getHandler方法获取到SurfaceHolder实例。

 

SurfaceHolder有一些类型,用来标识Surface实例界面数据来源,可以通过setType来操作:

* SURFACE_TYPE_NORMAL:RAM缓存的原生数据
* SURFACE_TYPE_HARDWARE:通过DMA,direct memory access,就是直接写屏技术获取到的数据,或者其他硬件加速的数据
* SURFACE_TYPE_GPU:通过GPU加速的数据
* SURFACE_TYPE_PUSH_BUFFERS:标识数据来源于其他对象,比如照相机,比如视频播放服务器(android内部有视频播放的服务器,所有播放视频相当于客户端)

CustomerVideoView的构造方法,使用超类的构造方法。都会执行initVideoView()方法用来初始化界面和参数。

另外一个主要的内容是openVideo()方法:

* mediaPlayer.prepareAsync(),用来异步准备播放,另外还有个prepare()方法,是同步的,也就是全部下载完毕才能播放,显然,在播放网上视频的时候需要用前者;
* 通过attachMediaController()方法,把控制条附加到播放视频的SurfaceView上,这里实现的不完全,因此还不能使用,仅仅是把MediaPlayerControl实例通过setMediaPlayer方法设置一下,供OnPreparedListener用来得到加载成功的回调,另外供外面代码调用得到视频的时长和当前时长。

 

 

 

 

 

还有一个问题,canvas.drawText(text, x, y, paint),其中的x,y指的是字符的左下角,而不是左上角,搞得查了半天的响应事件,那就再提一个吧,MotionEvent类 

getX是获取以widget左上角为坐标原点计算的X轴坐标直.

getRawX 获取的是以屏幕左上角为坐标原点计算的X轴坐标直.

getXPrecision()获取的是精确位置,但是我弄出来的全部都是0

 

 

自定义player,http://disanji.net/2010/12/08/android-surfaceview-2/

双缓冲,http://disanji.net/2010/12/08/android-surfaceview-2/

小游戏LunarLander分析,http://disanji.net/2010/12/18/android-example-analysis-note-8/

http://www.android777.com/index.php/tutorial/android-graphics/use-of-2d-animation-surfaceview.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值