SurfaceView
概念
SurfaceView的使用可以直接从内存或者DMA等硬件接口取得图像数据,是个非常重要的绘图容器。
它的特性:
可以在主线程之外的线程中向屏幕绘图上。这样可以避免画图任务繁重时造成主线程阻塞,从
而提高了程序的反应速度。在游戏开发中多用到SurfaceView,游戏中的背景、人物、动画等
等尽量在画布canvas中画出。
SurfaceView提供直接访问一个可画图的界面,可以控制在界面顶部的子视图层。
SurfaceView是提供给需要直接画像素而不是使用窗体不见的应用使用的。Android系统中一
个重要的概念和线索是Surface.View及其子类(TextView,Button等)要画在Surface上。每个
Surface创建一个Canvas对象(但属性时常改变),用来管理view在Surface上的绘图操作,如画
点画线,使用它是,一般都是出现在最顶层。
SurfaceView类 和View类的区别:
SurfaceView 和View的最本质的区别在于,surfaceView是在一个在新起的单独线程中可以重
新绘制画面,而View必须在UI的主线程中更新画面。那么在UI的主线程中更新画面,可能会
引发问题,比如你更新画面的时间过长,那么你的主UI线程会被你正在画的函数阻塞,那么将
无法响应按键,触摸等消息。当使用surfaceView由于是在新的线程中更新画面所以不会阻塞
你的UI主线程,但是这也会有另外一个问题,就是事件同步。比如你触屏了一下,你需要
surfaceView中thread处理,一般就需要有一个event queue的设计来保存touch event,这
样就会有点复杂了。
View:必须在UI的主线程中更新画面,用于被动更新画面。
surfaceView:UI线程和子线程中都可以。在一个新启动的线程中重新绘制画面,主动更新画
面。
所以在游戏的应用上,根据游戏的特点,一般分为两类:
a. 被动更新画面的。比如棋类,这种用view就好。因为画面的跟新依赖于onTouch来更新,
可以直接使用invalidate.因为这种情况下,这一次Touch和下一次Touch需要的时间比较长
些,不会产生
影响。
b.主动更新:比如一个人在一直跑动。这就需要一个单独的thread不停地重绘人的转台,避免
阻塞mian UI Thread 。所以显然view 不适合,需要surfaceView来控制。
实现方法
继承SurfaceView
实现SurfaceHolder.Callback接口并实现抽象方法
public void surfaceChanged(SurfaceHolder holder,int format,int width,int height){
//在surface的大小发生改变时激发
}
public void surfaceCreated(SurfaceHolder holder){
//在创建时激发,一般在这里调用画图的线程。
}
public void surfaceDestroyed(SurfaceHolder holder) {
//销毁时激发,一般在这里将画图的线程停止、释放。
}
SurfaceHolder:
surface的控制器,用来操纵surface。处理它的Canvas上画的效果和动画,控制表面,大小,
像素等
几个需要注意的方法:
// 给SurfaceView当前的持有者一个回调对象。
abstract void addCallback(SurfaceHolder.Callback callback);
// 锁定画布,一般在锁定后就可以通过其返回的画布对象Canvas,在其上面画图等操作了。
abstract Canvas lockCanvas();
// 锁定画布的某个区域进行画图等..因为画完图后,会调用下面的unlockCanvasAndPost
来改变显示内容。
abstract Canvas lockCanvas(Rect dirty);
// 相对部分内存要求比较高的游戏来说,可以不用重画dirty外的其它区域的像素,可以提高
速度。
// 结束锁定画图,并提交改变。
abstract void unlockCanvasAndPost(Canvas canvas);
下面一个案例,小球从左上掉到右下角
MainActivity
public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback {
SurfaceView sv;
SurfaceHolder holder;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//使用控件
sv = (SurfaceView) findViewById(R.id.sv);
//获取控制器
holder = sv.getHolder();
//添加一个监听
holder.addCallback(this);
}
@Override
public void surfaceCreated(final SurfaceHolder holder) {
//创建
//只有在生命周期中,才能获取surfaceView的高度和宽度
new Thread() {
Paint paint = new Paint();
@Override
public void run() {
paint.setColor(Color.RED);
paint.setAntiAlias(true);
paint.setDither(true);
super.run();
int index = 0;
while (index++ < 100) {
int x = sv.getWidth() / 100 * index;
int y = sv.getHeight() / 100 * index;
//绘制
//锁住屏幕的一块矩阵,然后开始绘制
Canvas canvas = holder.lockCanvas();
//清屏
canvas.drawColor(Color.WHITE);
canvas.drawCircle(x, y, 20, paint);
//解锁并且复制到主界面
holder.unlockCanvasAndPost(canvas);
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
//大小修改, 横竖屏切换时
//至少执行一次
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
//销毁
}
}
activity_main.xml;
<?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:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.administrator.lesson12_surfaceview.MainActivity">
<SurfaceView
android:id="@+id/sv"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>