公认的,游戏开发比内容性APP开发要复杂和困难,除了应用状态的保存和还原、数据存储交互、游戏逻辑复杂性等等,更主要的就是视图界面的显示和处理的复杂性——在Android系统提供的控件中,几乎找不到合适的组件,都要自己动手去自定义。其实不管光是游戏开发,平时我们在开发中要实现一个比较美观或炫酷的效果,都是需要自己动手实现的。之前写过几篇关于自定控件的文章——Android自定义控件开发系列,虽然效果不错,但是从学习和提高的角度来看,有点舍本逐末了:在学习中我们的重点是掌握原理,了解机制,而不是看最终效果怎么样;至于学习是否有成果,就是看学完之后能不能根据需要实现心中的效果。所以今天,我就来返回头来从最初的起跑线开始补上之前基本功的缺失——借游戏开发来说说Android View和SurfaceView视图框架(下文“游戏开发”包含自定义控件的意义)。
*注* 本文和解下来的Android 视图框架系列2/3——SurfaceView视图框架、Android 视图框架系列3/3——View和SurfaceView之间的抉择是一个模块,有对比才有特点和区别。
Android游戏开发中常用的3种视图是 View、SurfaceView、GLSurfaceView,它们的继承关系如下:
不过还是建议看Android自定义控件开发系列(零)——基础原理篇中那个唯一的大图!
public class MyView extends View implements Runnable{
/**
* 两个构造函数,先不用看
*/
public MyView(Context context) {
this(context, null);
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher); //根据需要改变的绘制内容
paint = new Paint();
new Thread(this).start(); //运行子线程
}
/**
* 从这里开始看
*/
private int startX = 0, startY = 0; //绘制起点的X、Y坐标
private Bitmap bitmap; //要绘制的内容
private Paint paint;
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
paint.setColor(Color.BLACK);
canvas.drawBitmap(bitmap, startY, startY, paint);
}
@Override
public void run() {
int i = 0;
while (i < 100) {
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher); //根据需要改变的绘制内容
startX += 10; //根据需要变
startY += 10;
//invalidate(); //报错,错误看截图
handler.sendEmptyMessage(0x001); //给主线程发消息,让主线程来完成invalidate()方法的调用
i++;
try {
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Handler handler = new Handler(){
public void handleMessage(Message msg) {
if(msg.what == 0x001){
invalidate(); //在主线程的Handler调用,通知onDraw()重绘
}
};
};
}
简单解释一下:开子线程要完成绘制内容和位置的计算,计算完后就要通知 onDraw() 方法进行重绘了,但是由于 invalidate() 方法不能在子线程中调用(否则报下图错误),所以我们通过给 UI 线程的 Handler 发消息,让 UI 线程调用 invalidate() 方法。