surfaceview 是游戏开发和视频播放常用的控件,因此我们要自己开发一些小游戏或者视频播放器多用来继承该view,并添加自己的独特功能。下面就以做游戏的框架来介绍:
先介绍一些基础知识:
1、继承surfaceview的view(就简称MyView),要想在布局文件中能使用,我们要覆写surfacview的构造方法:
public MySurfaceView(Context context, AttributeSet attrs) { super(context, attrs); }
2、当系统在加载游戏时候,比如初始化MyView的时候需要时间,为了去掉游戏开始前短暂的黑屏情况,我们通常写一个欢迎界面,或者写一个关级等,方法有很多,我们可以用一个比较简单的方法:
在布局文件里加上我们的surfacview和关级或者图片的控件。初始的时候将Myview设置为Gone , 其他的设置为Visibale。在resume里面开始准备我们的MyView游戏里面图片、声音等素材的加载(注意设置标志位,否则home再次进入的时候会又初始化导致游戏数据混乱)。然后在利用handler异步发送消息,当我们的MyView的游戏素材加载完了,就发送消息,将Myview设置为Visiable,将其他的设置为Gone。(在刚开始没有想到思路的时候利用setContentView()2次,发现就是resume()设置的一次生效,主要是我们的界面显示是在Onresume()方法执行完后,因此前面我们设置布局文件只是加载进资源初始化一些数据)。
3、surfacview占用系统资源比较大,当应用失去屏幕焦点的时候,如:按下home键,电源键、back键时候就会回收,且surfaceView的显示主要是通过surfacHolder来锁定和绘画画布来控制的,而为了更好的控制surfacview的画面通常添加一个回调方法来处理事情:addCallback(new CallBack()),而实现callBack得实现它的三个方法:
class MyCallBack implements SurfaceHolder.Callback { @Override public void surfaceCreated(SurfaceHolder holder) { } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed(SurfaceHolder holder) { } }
为了让我们的游戏在后台运行后重新进入时能够继续,就得了解我们按下home、电源键后的生命周期:(只要不重新执行view的构造函数我们的游戏就可以继续玩,前提是我们的绘画线程中的数据不能乱)
程序第一次启动时,调用view的构造函数->surfaceCreated->surfaceChanged
按下home键时:执行surfaceDestroyed;点击图标返回程序时,调用surfaceCreated->surfaceChanged
解决办法:发现surfacview并不会重新执行构造函数,因此只要我们的绘图线程重新启动,且之前的游戏数据不乱就可以继续玩了: (后面的解决办法都是基于满足home键解决方案上)
(1)将线程设为view的成员
(2)在surfaceCreated中把线程new出来,并设置运行标志true、调用线程的start(不要在构造函数中new)
(3)在surfaceDestroyed中将运行标志设为false
按返回键时,调用surfaceDestroyed;点击图标返回程序时,调用view的构造函数->调用surfaceCreated->surfaceChanged
解决办法:back键默认是退出游戏,若想按下back键后台运行就得覆写OnBackPressed() 方法里写moveTaskToBack()。
效果:surfaceDestroyed ;点击图标进入时候:surfaceCreated->surfaceChanged
按下电源键锁频时:surfaceDestroyed,解锁进入游戏时:surfaceCreated->surfaceChanged
解决办法:1、为了锁屏时,不重新初始化surfacview,我们可以在清单文件activity里添加:android:configChanges="orientation|keyboardHidden|screenSize" ,因为锁屏引起的也是screenSize的变化导致,添加后就是屏幕的变化不影响游戏。
效果:按电源键时 surfaceChanged,点击进入时:surfaceChanged
Node: 1、有时候我们玩游戏的时候,想要休息一会就暂停了,等几分钟玩的时候会发现屏幕黑了,因此我们可以在activity里用 getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 让屏幕长亮)
2、 为了在绘图的时候后台运行等不相互影响,在构造函数的时候需要init()游戏里的所有素材。
class MyCallBack implements SurfaceHolder.Callback { @Override public void surfaceCreated(SurfaceHolder holder) { isRunning = true; drawThread = new Thread(new MyRunnable()); drawThread.start(); log("surfaceCreated"); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { log("surfaceChanged"); } @Override public void surfaceDestroyed(SurfaceHolder holder) { isRunning = false; log("surfaceDestroyed"); } }
4、画图
画图的时候我们先锁住画布,然后绘图,绘图完了就解除锁定并传递显示:这里是一个线程画图对一个surfacview绘图,若涉及多线程最好是在锁定屏幕到绘图完成解锁 实行同步.
class MyRunnable implements Runnable { @Override public void run() { paint = new Paint(); paint.setColor(Color.RED); while(isRunning) { canvas = surfaceHolder.lockCanvas(); if(canvas!= null) { canvas.drawColor(Color.WHITE);//设置背景 drawTanks(canvas, tanks, paint); checkState(myTank , tanks); canvas.drawBitmap(directionBt , 0 ,directionBtCenter.y -r ,paint); canvas.drawBitmap(directionCenterBt , directionCenterBtCenter.x - r3 ,directionCenterBtCenter.y -r3 ,paint); canvas.drawBitmap(boomBt , boomCenter.x -r2,boomCenter.y - r2,paint); surfaceHolder.unlockCanvasAndPost(canvas); } } Log.d( Logcat , "线程运行结束"); } }
)