Android中SurfaceView,是不使用游戏引擎,开发一款游戏的常用view控件。
SurfaceView常常被用来显示那些更新速度比较快(这个速度通常人眼无法识别)的图像,常常被用来显示照相机的当前效果,视频的播放,游戏界面的播放。
SurfaceView的创建:
继承SurfaceView就可以使用构造方法创建了。
所要从新写的方法也只不过一个构造方法,这里使用的构造常常有两种,一种使用只有一个参数的构造方法,参数为context,如果使用这样的构造方法,则创建SurfaceView的Activity不能加载layout文件夹中的xml文件,只能加载new出来的view如
public class GameMainActivity extends Activity {
public static GameView Game;
public static AssetManager assets;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Assets.mContentResolver=getContentResolver();
Game=new GameView(GameMainActivity.this);
setContentView(Game);
assets=getAssets();
}
}
如果使用两个参数的构造方法:
Context context, AttributeSet attrs
则可以将自己View类当做Activity布局文件中的一个空间,当布局文件显示时会自动调用这个构造方法,第二参数用于返回相应的指令给View。
但是这里建议使用第一个方法构造,因为在游戏中的界面跳转与外部Activity的跳转逻辑完全不同,所以在布局文件中加载会有一定的弊端。但是好处是这样可以使用Android封装好的控件(如button)。
SurfaceView有一个特点:
他并不是当Activity创建 就会可以调用的,里面的那些控件,并不是真的控件,当Activity创建完成就可以使用了,而是当SurfaceView创建,并出现在屏幕上之后才可以开始绘画(即伪控件的加载)。
这时就需要一个监听器监听SurfaceView的创建,改变和销毁。
使用监听器
SurfaceHolder.Callback
这里要实现这个接口,重写其中的三个主要方法:
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) public void surfaceDestroyed(SurfaceHolder holder) 这三个方法代表了SurfaceView生命周期,注意只有在这三个声明周期中才可以进行其中的操作,在Activity中的操作要与之区分。 当创建时自动调用createed和changed方法,在home键,back键返回时调用destroyed方法,但是注意home键返回再回来依次调用created方法,和changed方法,但是back键返回再回来就会调用构造方法和created方法,changed方法。设置监听时要先初始化SurfaceView的管家类SurfaceHolder,通过在View中调用父类SurfaceView的方法getholder就可以简单的获取管家。
public class MyView extends SurfaceView implements Runnable,SurfaceHolder.Callback,View.OnTouchListener {
Context main;
SurfaceHolder holder;//主holder
Canvas canvas;//主画布
Paint p;
Thread t;
Bitmap buffer=null;//二级缓存
Path mpath;//触控中的轨迹
float startX;
float startY;
Rect window;
Rect bufferRect;
boolean run=false;
public MyView(Context context) {
super(context);
init(context);
setOnTouchListener(this);
}
private void init(Context context){
holder=getHolder();
holder.addCallback(this);//勿忘addcallback
this.main=context;
p=new Paint();
p.setAntiAlias(true);//消除锯齿
p.setStyle(Paint.Style.STROKE);//设置画笔风格
p.setAlpha(255);//画笔的不透明度
p.setStrokeWidth((float)2);//设置笔触宽度
p.setColor(Color.WHITE);
mpath=new Path();
buffer= Bitmap.createBitmap(1440,2256, Bitmap.Config.ARGB_8888);//很重要创建二级缓存的必要方法
t=new Thread(this);
}
@Override
public void run() {
while(run){
canvas=holder.lockCanvas();
window=canvas.getClipBounds();
Canvas c=new Canvas(buffer);
c.drawRect(window,p);
p.setColor(Color.rgb(new Random().nextInt(255),new Random().nextInt(255),new Random().nextInt(255)));
c.drawPath(mpath,p);
bufferRect=new Rect(0,0,buffer.getWidth(),buffer.getHeight());
canvas.drawBitmap(buffer,bufferRect,window,new Paint());
holder.unlockCanvasAndPost(canvas);
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
run=true;
Log.i("Created", "surfaceCreated: ");
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Log.i("Changed", "surfaceChanged: ");
t=new Thread(this);
t.start();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
run=false;
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.i("Destroyed", "surfaceDestroyed: ");
}
private void onTouchDown(MotionEvent event){
startX=event.getX();
startY=event.getY();
mpath.reset();
mpath.moveTo(startX,startY);
}
private void onTouchMove(MotionEvent event){
float touchX=event.getX();
float touchY=event.getY();
float dx=Math.abs(touchX-startX);//移动的距离
float dy =Math.abs(touchY-startX);//移动的距离
if(dx>3||dy>3){
float cX=(touchX+startX)/2;
float cY=(touchY+startY)/2;
mpath.quadTo(startX, startY, cX, cY);//绘制贝塞尔曲线
startX=touchX;
startY=touchY;//改变开始绘制的点
}//这里巧妙地进行筛选过于短的移动
}
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
this.onTouchDown(event);
break;
case MotionEvent.ACTION_MOVE:
this.onTouchMove(event);
break;
case MotionEvent.ACTION_UP:
break;
}
return true;
}
}