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。
特点:它和普通的View最大的区别在于,它有个SurfaceHolder,封装了一个Callback。使它能够在UI线程之外对View进行绘制。绘制完了之后,再把整个View提交给UI线程( “pia”地一声贴了上去)。这样,有效地防止了在绘制UI的过程中,对UI线程的影响,比如造成卡顿,甚至ANR。
用处:现在的我认识不深,常说地适合作游戏背景什么的暂时也不了解。就目前我的经验来看,它适合做实时变动的UI。比如,一个不停歇的倒计时~
下面就来一段普通的一段计时的代码,也许还有诸多问题,但也算是开个头,了解一下解决类似问题的方向。
这是一个小小的自定义SurfaceView:
package com.dream.fishbonelsy.surfaceviewdemo; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.util.AttributeSet; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; /** * Created by fishboneLsy on 2015/6/13. */ public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback { private SurfaceHolder surfaceHolder ; private class MyThread extends Thread{ //surfaceView的控制器 private SurfaceHolder holder; //绘画线程开关 public boolean isRun; //surfaceView的绘制线程 public MyThread(SurfaceHolder holder) { this.holder = holder; isRun = false; } @Override public void run() { int count = 0; while (isRun){ Canvas cv = null; try { //同步holder synchronized (holder){ //控制器拿下画布,开始画画 cv = holder.lockCanvas(); //将背景设置成黑色 cv.drawColor(Color.BLACK); //获取一个画笔 Paint p = new Paint(); //画笔设成白色 p.setColor(Color.WHITE); //在画布上用画笔,在(100,310)处,绘制时间 cv.drawText("开启界面"+(count++)+"秒" , 100, 310, p); //每隔一秒绘制一次 Thread.sleep(1000); } }catch (Exception e){ e.printStackTrace(); } finally { if (cv!=null){ //解锁画布并提交 holder.unlockCanvasAndPost(cv); } } } Log.d("tag" , "thread has been end"); } } private MyThread myThread; public MySurfaceView(Context context) { super(context); initView(context); } public MySurfaceView(Context context, AttributeSet attrs) { super(context, attrs); initView(context); } public MySurfaceView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initView(context); } private void initView(Context context){ //获取控制器 surfaceHolder = this.getHolder(); //添加surface动作的接口回调 surfaceHolder.addCallback(this); } //surfaceview从不可见到可见时调用 @Override public void surfaceCreated(SurfaceHolder holder) { Log.d("tag", "holder has been created"); if( myThread == null){ myThread = new MyThread(holder); myThread.isRun = true; myThread.start(); } } //surfaceview尺寸或区域改变时调用,当然启动时也会调用 @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Log.d("tag" , "holder has been changed" ); Log.d("tag" , "holder format is " + format ); Log.d("tag" , "holder width is " + width ); Log.d("tag" , "holder height is " + height ); } //surfaceview不可见时调用 @Override public void surfaceDestroyed(SurfaceHolder holder) { Log.d("tag" , "holder has been destroyed" ); } }
有几个知识点值得说一下(因为我不会:
1、关于synchronized
记下来,很重要。
Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。
一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。
三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。
四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。
五、以上规则对其它对象锁同样适用.
2、在这个里面,我没有关闭线程,这是个危险的事情,app关了,线程不一定会关,它会一直跑到天荒地老,直到GC爸爸的到来。为啥我没关闭,是因为surfaceDestroyed只要surfaceView不可见时就会调用。但是我的计时部分在线程中,我并不想在用户按Home键时就结束线程。所以我暂时没写,要写可以写一个方法,结束线程。然后在外面的Activity的ondestroy的方法中去调用。
3、关于如何优雅地结束一个线程
isRun = false; myThread.interrupt(); myThread = null;
以上,简陋记载,有空再来深入。