1. SurfaceView介绍
如果需要在另外的线程绘制界面、需要迅速的更新界面,或者渲染UI界面需要较长的时间,这种情况就要使用SurfaceView了。SurfaceView中包含一个Surface对象,而Surface是可以在后台线程中绘制的。
通过SurfaceView的getHolder()函数可以获取SurfaceHolder对象,Surface 就在SurfaceHolder对象内。虽然Surface保存了当前窗口的像素数据,但是在使用过程中是不直接和Surface打交道的,由SurfaceHolder的Canvas.lockCanvas()或则Canvas.lockCanvas(Rect dirty)函数来获取Canvas对象,通过在Canvas上绘制内容来修改Surface中的数据。如果Surface不可编辑,或者尚未创建调用该函数会返回null,在 unlockCanvas() 和 lockCanvas()中Surface的内容是不缓存的,所以需要完全重绘Surface的内容,为了提高效率只重绘变化的部分则可以调用lockCanvas(Rect dirty)函数来指定一个dirty区域,这样该区域外的内容会缓存起来。在调用lockCanvas函数获取Canvas后,SurfaceView会获取Surface的一个同步锁直到调用unlockCanvasAndPost(Canvas canvas)函数才释放该锁,这里的同步机制保证在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对象了。
注意:一个SurfaceView只在SurfaceHolder.Callback.surfaceCreated() 和 SurfaceHolder.Callback.surfaceDestroyed()调用之间是可用的,其他时间是得不到它的Canvas对象的(null)。
3. SurfaceView和View的区别
SurfaceView和View最本质的区别在于,surfaceView是在一个新起的单独线程中可以重新绘制画面,而View必须在UI的主线程中更新画面。
在UI的主线程中更新画面可能会引发问题,比如你更新画面的时间过长,那么你的主UI线程会被你正在画的函数阻塞。那么将无法响应按键,触屏等消息。当使用SurfaceView时,由于是在新的线程中更新画面所以不会阻塞UI主线程。但这也带来了另外一个问题,就是事件同步。比如你触屏了一下,你需要SurfaceView中的Thread处理,一般就需要有一个event queue的设计来保存touch event,这会稍稍复杂一点,因为涉及到线程同步。
所以基于以上,根据游戏特点,一般分成两类:
(1)被动更新画面的。比如棋类,用View就好了。因为画面的更新是依赖于 onTouch 来更新,可以直接使用 invalidate。 因为在这种情况下,这一次Touch和下一次的Touch需要的时间比较长些,不会产生影响。
(2)主动更新画面的。比如一个人在一直跑动。这就需要一个单独的thread不停的重绘人的状态,避免阻塞UI主线程。所以显然view不合适,需要surfaceView来控制。
简单代码示例:
public class BoomFlowerView extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder mHolder;
private DrawThread mDrawThread;
public BoomFlowerView(Context context) {
super(context);
init();
}
public BoomFlowerView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public BoomFlowerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mHolder = getHolder();
mDrawThread = new DrawThread();
mHolder.addCallback(this);
}
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
mDrawThread.start();
}
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
}
class DrawThread extends Thread{
@Override
public void run() {
super.run();
Paint mPaint = new Paint();//实例化画笔
AssetManager manager = getResources().getAssets();
int i = 1;
while (true){
String fileName = null;
Canvas canvas = mHolder.lockCanvas();//锁定画布
canvas.drawColor(Color.BLACK);
InputStream is = null;
if(i<=9){
fileName= "loading000"+i+".png";
}else {
fileName = "loading00"+i+".png";
}
try {
is = manager.open(fileName);
Bitmap bitmap = BitmapFactory.decodeStream(is);
if(canvas!=null){
canvas.drawBitmap(bitmap,200,400,mPaint);
mHolder.unlockCanvasAndPost(canvas);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if(is!=null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
i++;
if(i>26){
i = 1;
}
}
}
}
}
public class CricleView extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder mSurfaceHolder;
private DrawThread mDrawThread;
public CricleView(Context context) {
super(context);
initView();
}
public CricleView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public CricleView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
private void initView() {
mSurfaceHolder = getHolder();
mDrawThread = new DrawThread();//绘制线程
mSurfaceHolder.addCallback(this);
}
//创建的时候开始绘制
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
mDrawThread.start();
}
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
}
//子线程 用来绘制 图的
class DrawThread extends Thread{
@Override
public void run() {
super.run();
Paint paint = new Paint();//画笔
paint.setColor(Color.GREEN);//画笔的颜色
paint.setStrokeWidth(5);//画笔的宽度
paint.setAntiAlias(true);//画笔的抗锯齿
int rales = 0;
while (true){
if(rales > 150){
rales = 0;
}
rales+=5;
Canvas canvas = mSurfaceHolder.lockCanvas();//锁定一个画布
if(canvas!=null){
canvas.drawColor(Color.RED);//设置画布的颜色
canvas.drawLine(20,20,200,200,paint);
canvas.drawCircle(200,200,rales,paint);
mSurfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}
}
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="alice.bw.com.surfaceviewdemo.MainActivity">
<alice.bw.com.surfaceviewdemo.CricleView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
/>
<alice.bw.com.surfaceviewdemo.BoomFlowerView
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>