让我们保持简单,看下面的图表
android上面任何事情都是发生在Activity内部,Activity会新建一个view,view是一切事情发生的地方,像触摸和最后的图片显示。把activity想成桌子,而view是一张白纸让我们画东西,我们用铅笔画图,化学反应都是在纸上的,产生了图片,同样也适用于Activity和View,像下图所示
让我们打开DroidzActivity.java,看这行代码
1 |
setContentView(R.layout.main); |
只是Activity在创建时候把R.layout.view赋给了它,我们的例子中 在开始时候就赋值了
让我们创建了一个新的view,一个View是一个简单的类,提供了事件处理,和可见的矩形供我们画,最简单的是扩展Android自己的Surfaceview,我们也需要实现surface.callback来获得surface改变的路径,例如当view被销毁或者设备的方向改变了
MainGamePanel.java
01 package net.obviam.droidz;
02
03 import android.content.Context;
04 import android.graphics.Canvas;
05 import android.view.MotionEvent;
06 import android.view.SurfaceHolder;
07 import android.view.SurfaceView;
08
09 public class MainGamePanel extends SurfaceView implements
10 SurfaceHolder.Callback {
11
12 public MainGamePanel(Context context) {
13 super(context);
14 // adding the callback (this) to the surface holder to intercept events
15 getHolder().addCallback(this);
16 // make the GamePanel focusable so it can handle events
17 setFocusable(true);
18 }
19
20 @Override
21 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
22 }
23
24 @Override
25 public void surfaceCreated(SurfaceHolder holder) {
26 }
27
28 @Override
29 public void surfaceDestroyed(SurfaceHolder holder) {
30 }
31
32 @Override
33 public boolean onTouchEvent(MotionEvent event) {
34 return super.onTouchEvent(event);
35 }
36
37 @Override
38 protected void onDraw(Canvas canvas) {
39 }
40 }
上面的代码只是重写了我们感兴趣的方法,除了15和17行,其他都没什么特别的
1 | getHolder().addCallback( this ); |
这行代码把当前类设置成了在实际surface上发生的事件需要处理的handler
1 | setFocusable( true ); |
上面的代码使 game panel获得焦点,为了能够获得事件。我们加入了回调并使得view获得焦点,我们不会丢失这个view
20行后面重写的方法会被用到,但是现在让他们为空
让我们建立一个线程,它会是我们实际的游戏循环
01 package net.obviam.droidz;
02
03 public class MainThread extends Thread {
04
05 // flag to hold game state
06 private boolean running;
07 public void setRunning(boolean running) {
08 this.running = running;
09 }
10
11 @Override
12 public void run() {
13 while (running) {
14 // update game state
15 // render state to the screen
16 }
17 }
18
你看到它没有做太多事情,重写了run方法,当running标识为true时,做了个无限循环
当前的进程没有实例化,当屏幕加载的时候,我们来让它运行
看一下修改过的MainGamePanel类
01 package net.obviam.droidz;
02
03 import android.content.Context;
04 import android.graphics.Canvas;
05 import android.view.MotionEvent;
06 import android.view.SurfaceHolder;
07 import android.view.SurfaceView;
08
09 public class MainGamePanel extends SurfaceView implements
10 SurfaceHolder.Callback {
11
12 private MainThread thread;
13
14 public MainGamePanel(Context context) {
15 super(context);
16 getHolder().addCallback(this);
17
18 // create the game loop thread
19 thread = new MainThread();
20
21 setFocusable(true);
22 }
23
24 @Override
25 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
26 }
27
28 @Override
29 public void surfaceCreated(SurfaceHolder holder) {
30 thread.setRunning(true);
31 thread.start();
32 }
33
34 @Override
35 public void surfaceDestroyed(SurfaceHolder holder) {
36 boolean retry = true;
37 while (retry) {
38 try {
39 thread.join();
40 retry = false;
41 } catch (InterruptedException e) {
42 // try again shutting down the thread
43 }
44 }
45 }
46
47 @Override
48 public boolean onTouchEvent(MotionEvent event) {
49 return super.onTouchEvent(event);
50 }
51
52 @Override
53 protected void onDraw(Canvas canvas) {
54 }
55 }
我们加入了下面代码行
12行声明了线程是私有的
1 | private MainThread thread; |
19行我们实例化了这个线程
1 | thread = new MainThread(); |
In the surfaceCreated
method we set the running flag to true and we start up the thread (lines 30 and 31). By the time the this method is called the surface is already created and the game loop can be safely started.
Take a look at the surfaceDestroyed
method.
在surfacecreated的方法中 我们设置了running为true,然后我们让线程开始运行(30和31行)。当surface被创建的时候,游戏循环可以安全的开始运行了
看一下surfaceDestroy方法
01 public void surfaceDestroyed(SurfaceHolder holder) {
02 // tell the thread to shut down and wait for it to finish
03 // this is a clean shutdown
04 boolean retry = true;
05 while (retry) {
06 try {
07 thread.join();
08 retry = false;
09 } catch (InterruptedException e) {
10 // try again shutting down the thread
11 }
12 }
13 }
这个方法在surface被销毁前被调用,这里不是设置running标识的地方,但是这里的代码保证了线程被干净的关闭了,我们只是阻塞线程,等待它的死亡
如果在模拟器运行,我们啥也看不这,但是我们可以打一些log来测试,不要担心,后面会告诉你是怎么回事的,更多的请参考Android site.
加入和屏幕的交互
我们会关闭程序当我们按屏幕下方的时候,如果我们点别的地方,我们只是记录坐标位置
在mainThread类中 我们加入如下行
1 private SurfaceHolder surfaceHolder;
2 private MainGamePanel gamePanel;
3 public MainThread(SurfaceHolder surfaceHolder, MainGamePanel gamePanel) {
4 super();
5 this.surfaceHolder = surfaceHolder;
6 this.gamePanel = gamePanel;
7 }
我们声明了变量gamepanel和surfaceHolder,在构造函数中实例化了他们
我们拥有他们是非常重要的,不仅仅是gamepanel,因为我们需要锁住surface来画图,而这个操作只能通过surfaceHolder
改变在构造函数中的方法
1 | thread = new MainThread(getHolder(), this ); |
我们传递了当前的holder和panel,所以线程可以访问他们,我们稍后会新建一个游戏更新的函数在game panel中,在线程中也会触发它,但是现在我们保持现状
TAG 常量是android log函数的第一个参数
打log的笔记
打开log的视图,
Now you should see the LogCat view. This is nothing more than a console where you can follow Android’s log. It’s a great tool as you can filter for logs containing a specific text or logs with a certain tag which is quite useful.
回到代码,MainThread.java现在是这样了
01 package net.obviam.droidz;
02
03 import android.util.Log;
04 import android.view.SurfaceHolder;
05
06 public class MainThread extends Thread {
07
08 private static final String TAG = MainThread.class.getSimpleName();
09
10 private SurfaceHolder surfaceHolder;
11 private MainGamePanel gamePanel;
12 private boolean running;
13 public void setRunning(boolean running) {
14 this.running = running;
15 }
16
17 public MainThread(SurfaceHolder surfaceHolder, MainGamePanel gamePanel) {
18 super();
19 this.surfaceHolder = surfaceHolder;
20 this.gamePanel = gamePanel;
21 }
22
23 @Override
24 public void run() {
25 long tickCount = 0L;
26 Log.d(TAG, "Starting game loop");
27 while (running) {
28 tickCount++;
29 // update game state
30 // render state to the screen
31 }
32 Log.d(TAG, "Game loop executed " + tickCount + " times");
33 }
34 }
在8行 我们定义了 log的tag
在run函数中我们定义tickcount,当游戏循环被执行,它会加1
让我们回到MainGamePanel.java类,我们修改OnTOuchEvent,来处理触摸消息
01 public boolean onTouchEvent(MotionEvent event) {
02 if (event.getAction() == MotionEvent.ACTION_DOWN) {
03 if (event.getY() > getHeight() - 50) {
04 thread.setRunning(false);
05 ((Activity)getContext()).finish();
06 } else {
07 Log.d(TAG, "Coords: x=" + event.getX() + ",y=" + event.getY());
08 }
09 }
10 return super.onTouchEvent(event);
11 }
同样也修改了DroidzActivity
01 package net.obviam.droidz;
02
03 import android.app.Activity;
04 import android.os.Bundle;
05 import android.util.Log;
06 import android.view.Window;
07 import android.view.WindowManager;
08
09 public class DroidzActivity extends Activity {
10 /** Called when the activity is first created. */
11
12 private static final String TAG = DroidzActivity.class.getSimpleName();
13
14 @Override
15 public void onCreate(Bundle savedInstanceState) {
16 super.onCreate(savedInstanceState);
17 // requesting to turn the title OFF
18 requestWindowFeature(Window.FEATURE_NO_TITLE);
19 // making it full screen
20 getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
21 // set our MainGamePanel as the View
22 setContentView(new MainGamePanel(this));
23 Log.d(TAG, "View added");
24 }
25
26 @Override
27 protected void onDestroy() {
28 Log.d(TAG, "Destroying...");
29 super.onDestroy();
30 }
31
32 @Override
33 protected void onStop() {
34 Log.d(TAG, "Stopping...");
35 super.onStop();
36 }
37 }
运行程序,在程序上部点击几下,然后点击下部,程序应该退出
目前做的事情
1 创建了一个全屏程序
2 有一个独立的线程控制程序
3 解析基本手势 例如触摸手势
4 优雅的关闭程序
代码大家可以去这里下载