实验4:Android界面设计

8.1 知识点

1.

2.

8.2 实例讲解

1. 设计仿微信导航界面

简要说明…… 详细步骤……

2. 制作黑白棋游戏界面

简要说明:我做的是一个黑白棋的应用,在主界面是用垂直布局来弄的,中间会涉及到几个相对布局和表格布局,还是相对来说比较简单的。 详细步骤:

  1. android:background=”@drawable/net13140803138background” //一开始用了这一句调用了drawable里的背景图片作为背景。
  2. android:orientation=”vertical”//然后这里是说明我是垂直方向进行线性布局。
  3. 然后我就开始按照布局放控件了,其中: <ImageView

1. android:layout_width="match_parent" 2. android:layout_weight="1" 3. android:layout_height="0dp" 4. android:scaleType="fitXY" 5. android:id="@+id/top1" 6. android:src="@drawable/net13140803138top"/>

这一部分代码即我最上面的一个ImageView,调用了drawable/net13140803138top图片。 4. 1. <ImageView 2. android:layout_width="0dp" 3. android:layout_weight="2" 4. android:layout_height="match_parent" 5. android:scaleType="fitCenter" 6. android:src="@drawable/net13140803138daojian" 7. />

1. </LinearLayout>

这里我是用了一个水平方向的线性布局,这里值得一提的是,我放了3个位置,但是我只放了一个控件,调用了drawable/net13140803138daojian图片,如果我后面增加了新的功能,可以有位置添加进去。

  1. 这里则是由12个黑白棋子组成的一个布局,由12个黑白棋图片组成,代码会有点长,但是代码其实是比较简单的

``` 1.

  1. <ImageView
  2. android:id="@+id/iv1"
  3. android:layout_width="@dimen/width_of_chess1" android:layout_margin="@dimen/chess_margin1"
  4. android:layout_height="@dimen/width_of_chess1"
  5. android:scaleType="fitCenter"
  6. android:src="@drawable/net13140803138white_chess"/>

  7. <ImageView

  8. android:id="@+id/iv2"
  9. android:layout_width="@dimen/width_of_chess1" android:layout_margin="@dimen/chess_margin1"
  10. android:scaleType="fitCenter"
  11. android:layout_height="@dimen/width_of_chess1"
  12. android:src="@drawable/net13140803138black_chess"
  13. />

  14. <ImageView

  15. android:id="@+id/iv3"
  16. android:layout_width="@dimen/width_of_chess1" android:layout_margin="@dimen/chess_margin1"
  17. android:scaleType="fitCenter"
  18. android:layout_height="@dimen/width_of_chess1"
  19. android:src="@drawable/net13140803138black_chess"
  20. />

  21. <ImageView

  22. android:id="@+id/iv4"
  23. android:layout_width="@dimen/width_of_chess1" android:layout_margin="@dimen/chess_margin1"
  24. android:scaleType="fitCenter"
  25. android:src="@drawable/net13140803138white_chess"
  26. android:layout_height="@dimen/width_of_chess1" />

  27. <ImageView

  28. android:id="@+id/iv5"
  29. android:layout_width="@dimen/width_of_chess1" android:layout_margin="@dimen/chess_margin1"
  30. android:scaleType="fitCenter"
  31. android:layout_height="@dimen/width_of_chess1"
  32. android:src="@drawable/net13140803138black_chess"/>

  33. <ImageView

  34. android:id="@+id/iv6"
  35. android:layout_width="@dimen/width_of_chess1" android:layout_margin="@dimen/chess_margin1"
  36. android:scaleType="fitCenter"
  37. android:layout_height="@dimen/width_of_chess1"
  38. android:src="@drawable/net13140803138white_chess"
  39. />

  40. <ImageView

  41. android:id="@+id/iv7"
  42. android:layout_width="@dimen/width_of_chess1" android:layout_margin="@dimen/chess_margin1"
  43. android:scaleType="fitCenter"
  44. android:layout_height="@dimen/width_of_chess1"
  45. android:src="@drawable/net13140803138white_chess"
  46. />

  47. <ImageView

  48. android:id="@+id/iv8"
  49. android:layout_width="@dimen/width_of_chess1" android:layout_margin="@dimen/chess_margin1"
  50. android:scaleType="fitCenter"
  51. android:src="@drawable/net13140803138black_chess"
  52. android:layout_height="@dimen/width_of_chess1" />

  53. <ImageView

  54. android:id="@+id/iv9"
  55. android:layout_width="@dimen/width_of_chess1" android:layout_margin="@dimen/chess_margin1"
  56. android:scaleType="fitCenter"
  57. android:layout_height="@dimen/width_of_chess1"
  58. android:src="@drawable/net13140803138black_chess"/>

  59. <ImageView

  60. android:id="@+id/iv10"
  61. android:layout_width="@dimen/width_of_chess1" android:layout_margin="@dimen/chess_margin1"
  62. android:scaleType="fitCenter"
  63. android:layout_height="@dimen/width_of_chess1"
  64. android:src="@drawable/net13140803138white_chess"
  65. />

  66. <ImageView

  67. android:id="@+id/iv11"
  68. android:scaleType="fitCenter"
  69. android:layout_width="@dimen/width_of_chess1" android:layout_margin="@dimen/chess_margin1"
  70. android:layout_height="@dimen/width_of_chess1"
  71. android:src="@drawable/net13140803138black_chess"
  72. />

  73. <ImageView

  74. android:id="@+id/iv12"
  75. android:layout_width="@dimen/width_of_chess1" android:layout_margin="@dimen/chess_margin1"
  76. android:scaleType="fitCenter"
  77. android:src="@drawable/net13140803138white_chess"
  78. android:layout_height="@dimen/width_of_chess1" /> ```
1. </GridLayout>

3. </RelativeLayout>

这一段的代码我是用了一个相对布局,里面在用一个表格布局放在这个相对布局的中间,调用了12个ImageView放在表格布局里面,其中,调用了drawable/net13140803138white_chess和drawable/net13140803138black_chess两个黑白棋子的图片。

  1. 这里我要加一个声音的开关放在右下角,所以要加一个相对布局

1. <CheckBox 2. android:id="@+id/soundSwitch" 3. android:layout_alignParentRight="true" 4. android:layout_width="wrap_content" 5. android:layout_height="wrap_content" 6. android:text="Sound" 7. android:textColor="#FF000000"/>

所以这里我用了一个相对布局,然后放一个CheckBox在这相对布局里面。 2. <ImageButton

1. android:layout_width="match_parent" 2. android:layout_height="0dp" 3. android:layout_weight="1" 4. android:contentDescription="@string/app_name" 5. android:id="@+id/startButton" 6. android:src="@drawable/net13140803138start" 7. android:scaleType="fitXY" 8. android:background="#00000000" 9. android:layout_marginBottom="6dp"/>

这里也是调用了一个ImageButton作为一个开始按钮,其中调用了drawable/net13140803138start图片。 至此,我的主界面就完成了。 ```

1. ###3. 制作练练看游戏界面
2. 简要说明:海贼王连连看游戏主界面是由Net1314080903126main.xml 文件通过调用Net1314080903126CtrlView.java  Net1314080903126GameView.java
3. Net1314080903126OnePieceGame.java 来显示主界面的以及游戏规则和游戏玩法的等。
4. 详细步骤:
5. 1.   游戏的主界面是通过Net1314080903126main.xml 来显示的,代码:
6. <?xml version="1.0" encoding="utf-8"?>
7. &lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
8. android:orientation="vertical" android:layout_width="match_parent"
9. android:layout_height="wrap_content">
10. &lt;TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
11. android:layout_width="fill_parent"    android:layout_height="wrap_content">
12. &lt;TableRow>
13. &lt;ProgressBar android:id="@+id/pb" android:layout_width="fill_parent"
14. android:layout_height="wrap_content" style="?android:attr/progressBarStyleHorizontal"
15. android:layout_weight="9"/>
16. &lt;TextView android:layout_height="wrap_content" android:layout_width="wrap_content"
17. android:text="@string/remain_time" android:layout_weight="1"/>
18. &lt;TextView android:layout_height="wrap_content" android:layout_width="wrap_content"
19. android:id="@+id/show_remainTime" android:layout_weight="1"/>
20. &lt;/TableRow>
21. &lt;/TableLayout>

23. &lt;edu.hzuapps.androidworks.homeworks.net1314080903126.Net1314080903126CtrlView
24. android:id="@+id/cv"
25. android:layout_width="wrap_content" android:layout_height="fill_parent" />
26. &lt;/LinearLayout>

28. 其中:&lt;edu.hzuapps.androidworks.homeworks.net1314080903126.Net1314080903126CtrlView
29. android:id="@+id/cv"
30. android:layout_width="wrap_content" android:layout_height="fill_parent" />
31. 这是显示海贼王人物的以及人物界面布局。
32. 然后:&lt;TableRow>
33. &lt;ProgressBar android:id="@+id/pb" android:layout_width="fill_parent"
34. android:layout_height="wrap_content" style="?android:attr/progressBarStyleHorizontal"
35. android:layout_weight="9"/>
36. &lt;TextView android:layout_height="wrap_content" android:layout_width="wrap_content"
37. android:text="@string/remain_time" android:layout_weight="1"/>
38. &lt;TextView android:layout_height="wrap_content" android:layout_width="wrap_content"
39. android:id="@+id/show_remainTime" android:layout_weight="1"/>
40. &lt;/TableRow>
41. 这是显示时间进度条的,以及时间 剩余时间(秒)和倒计时300.
42. 这是海贼王连连看的界面的大致介绍。
43. 2.  下面详细解释一下海贼王连连看。
44. (1).  public class Net1314080903126GameView extends View {

46. public final int row = 10;
47. public final int col = 10;
48. public float width;
49. public float height;
50. private int selY;
51. private int selX;
52. public boolean isLine = false;
53. public int grid[][] = new int[row][col];
54. private Rect selRect = new Rect();
55. public int lineType = 0;
56. public final int V_LINE = 1;
57. public final int H_LINE = 1;
58. public final int ONE_C_LINE = 2;
59. public final int TWO_C_LINE = 3;
60. public int much = 0;
61. Point[] p;
62. public int[] imageType = new int[] { R.drawable.net1314080903126aa, R.drawable.net1314080903126bb,
63. R.drawable.net1314080903126cc, R.drawable.net1314080903126dd, R.drawable.net1314080903126ee, R.drawable.net1314080903126ff,
64. R.drawable.net1314080903126gg, R.drawable.net1314080903126hh, R.drawable.net1314080903126ii, R.drawable.net1314080903126jj,
65. R.drawable.net1314080903126kk, R.drawable.net1314080903126ll, R.drawable.net1314080903126mm, R.drawable.net1314080903126nn,
66. R.drawable.net1314080903126oo, R.drawable.net1314080903126pp};
67. public Bitmap[] image;
68. public List&lt;Integer> type = new ArrayList&lt;Integer>();
69. 这个是Net1314080903126GameView.java文件中,调用图片。public final int row = 10;
70. public final int col = 10;图片的布局分布十行十列显示。以及显示大小尺寸。调用drawable文件下的图片显示在界面上。

72. (2). &lt;TextView android:layout_height="wrap_content" android:layout_width="wrap_content"
73. android:text="@string/remain_time" android:layout_weight="1"/>
74. 这是显示剩余时间(秒)
75. (3). &lt;ProgressBar android:id="@+id/pb" android:layout_width="fill_parent"
76. android:layout_height="wrap_content" style="?android:attr/progressBarStyleHorizontal"
77. android:layout_weight="9"/>
78. 这是显示倒计时时间进度条的。
79. (4). &lt;TextView android:layout_height="wrap_content" android:layout_width="wrap_content"
80. android:id="@+id/show_remainTime" android:layout_weight="1"/>
81. 这是显示300时间的跑秒。
82. (5).public boolean onCreateOptionsMenu(Menu menu) {
83. // TODO Auto-generated method stub
84. menu.add(0, START_ID, 0, R.string.newgame);
85. menu.add(0, REARRARY_ID, 0, R.string.rearrage);
86. menu.add(0, END_ID, 0, R.string.exit);
87. return super.onCreateOptionsMenu(menu);
88. }
89. 这是页面上方选项菜单,可以选择(新游戏,重排列以及退出游戏)这三个功能。
90. (6).public AlertDialog dialogForSucceed() {
91. AlertDialog.Builder builder = new AlertDialog.Builder(this);
92. builder.setIcon(R.drawable.icon).setMessage(R.string.succeedInfo)
93. .setPositiveButton(R.string.next,
94. new DialogInterface.OnClickListener() {
95. @Override
96. public void onClick(DialogInterface dialog,
97. int which) {
98. // TODO Auto-generated method stub
99. dormant = dormant - 300;
100. newPlay();
101. }
102. }).setNeutralButton(R.string.again_challenge,
103. new DialogInterface.OnClickListener() {
104. @Override
105. public void onClick(DialogInterface dialog,
106. int which) {
107. // TODO Auto-generated method stub
108. newPlay();
109. }
110. });
111. return builder.create();
112. }

114. public AlertDialog dialogForFail() {
115. AlertDialog.Builder builder = new AlertDialog.Builder(this);
116. builder.setIcon(R.drawable.icon).setMessage(R.string.failInfo)
117. .setPositiveButton(R.string.again_challenge,
118. new DialogInterface.OnClickListener() {
119. @Override
120. public void onClick(DialogInterface dialog,
121. int which) {
122. // TODO Auto-generated method stub
123. newPlay();
124. }
125. }).setNegativeButton(R.string.exit,
126. new DialogInterface.OnClickListener() {
127. @Override
128. public void onClick(DialogInterface dialog,
129. int which) {
130. // TODO Auto-generated method stub
131. isCancel=false;
132. finish();
133. }
134. });
135. return builder.create();
136. }
137. 这是显示300秒内完成游戏提示通往下一关,以及没有完成游戏提示你不气馁。
138. 以上就是海贼王练练看看游戏界面的详细实现过程。

141. ###4. 制作扫雷游戏界面
142. 简要说明:这里介绍的是扫雷游戏的初始界面和游戏进行界面的实现方法,它是通过获取每个格子的id值来调用显示相应的背景图片。
143. 详细步骤:
144. 1. 先获取每个格子对象,再给每个格子对象编号或下标获取id值,代码如下:

public int getCount() { return levellevel; } /*

1. * 方法:获取每个格子对象
2. * @param position 格子编号,位置下标
3. * @return 格子类型的GameGroundEntity
4. * */
5. @Override
6. public GridEntity getItem(int position) {

// 调用GameGroundEntity中的getEntity方法获取格子对象 return gameGround.getEntity(position); } /**

1. * 方法:通过适配器给每个格子对象编号或下标获取id值
2. * @return long类型,在java中,byte和short可自动转换为int,int可自动转换为long
3. * */
4. @Override
5. public long getItemId(int position) {
6. return position;
7. }
1. 2.设置格子对象的背景图片, 不同状态下设置不同的背景图片(i00,i13等是图片的名字)

@param grid :格子对象

1. * */
2. public int getRes(GridEntity grid){

// 设置格子对象的背景图片的ID为0 int resID=0; // 判断,如果格子对象被标记了且标记正确 if(grid.isFlag()&&!grid.isFlagWrong()){ resID=R.drawable.i_flag; } // 判断,如果格子对象被标记了但标记不正确 else if(grid.isFlag()&&grid.isFlagWrong()){ resID=R.drawable.i14; } // 判断,如果格子对象没有被点击,isShow()属性为false else if(!grid.isShow()){ resID=R.drawable.i00; } // 判断,格子对象是地雷且非自动显示 else if(grid.isBoom()&&!grid.isAutoShow()){ resID=R.drawable.i13; } // 判断,格子对象是地雷,自动显示 else if(grid.isBoom()&&grid.isAutoShow()){ resID=R.drawable.i12; } // 判断,格子周围没有地雷,是空白格 else if(grid.getBoomsCount()==0){ resID=R.drawable.i09; } // 判断,格子中卫有地雷,个数为1-8个 else if(grid.getBoomsCount()!=0){ // 动态拼接图片名,格式为图片名称,图片类型,资源所在包名 resID=context.getResources().getIdentifier(“i0”+grid.getBoomsCount(),”drawable”,context.getPackageName()); } return resID; }

1. 源代码:!https://github.com/hzuapps/android-labs/tree/master/app/src/main/java/edu/hzuapps/androidworks/homeworks/com1314080901110

4. ###5. 制作围住神经猫游戏界面
5. 简要说明:简单版的围住神经猫游戏的背景是一排排的圆圈,可以用二维数组创建,而这些圆圈有三种状态,分别为猫可以走并可以设置路障的位置(灰色),猫所处的位置(红色),猫不能走并已开启路障的位置(橘色),这些状态定义在Dot类中,显然其应该有坐标X和Y;游戏的背景等我都放在Playground类中,背景是基于SurfaceView开发的,而里面还有很多功能,如猫和已设路障的初始化位置,猫如何躲路障,如何判断路可走,判断处在边界位置等等;最后在Activity中调用Playground。

7. 详细步骤:
8. 1. 建立Dot类,用来记录每个场景中的元素它的X,Y坐标点的状态。并不会直接参与界面的响应和界面的绘制。每一个点都是一个抽象的对象,需要把每一个点抽象为一个类,然后让每一个圆圈继承于这个类,或者直接把它实现为这个类的实例。每个点有三个状态:灰色-猫可走的路径;橘色-路障的状态
9. 无法改变;红色-猫当前的位置。

public class Net1314080903142Dot {

1. int x,y;//当前点的X,Y坐标
2. int status;//记录这个点的状态
3. //三个表征圆点状态静态常量
4. public static final int STATUS_ON = 1;//已经开启路障的状态
5. public static final int STATUS_OFF = 0;//代表灰色可走路径
6. public static final int STATUS_IN = 9;//猫当前的位置
7. //三个数字不同即可,具体用哪个数字无所谓

10. //指定X,Y坐标
11. public Net1314080903142Dot(int x, int y) {
12. super();
13. this.x = x;
14. this.y = y;
15. status = STATUS_OFF;
16. }

18. //指定geter和sette方法
19. public int getX() {
20. return x;
21. }

23. public void setX(int x) {
24. this.x = x;
25. }

27. public int getY() {
28. return y;
29. }

31. public void setY(int y) {
32. this.y = y;
33. }

35. public int getStatus() {
36. return status;
37. }

39. public void setStatus(int status) {
40. this.status = status;
41. }

43. //同时设置X,Y坐标的方法
44. public void setXY(int x,int y) {
45. this.y = y;
46. this.x = x;
47. }

}

2. 2.建立Playground类,用来绘制界面还有实现游戏的各种算法。以下只详细介绍界面的绘制还有每个圆圈状态转化的过程。

public class Net1314080903142Playground extends SurfaceView implements OnTouchListener{ //界面的响应和界面的绘制在SurfaceView完成,触摸事件的响应通过OnTouchListener接口实现

1. private static  int WIDTH = 40;
2. private static final int ROW = 10;//行高:每行储存10个元素
3. private static final int COL = 10;//列宽:每列储存10个元素
4. private static final int BLOCKS = 15;//默认添加的路障数量

7. private Dot Net1314080903142matrix[][];//声明二维数组来存放点元素
8. private Dot Net1314080903142cat;//声明猫这个点

10. public Net1314080903142Playground(Context context) {
11. super(context);//使用Context创建当前类构造函数
12. getHolder().addCallback(callback);//将Callback对象指定给getholder
13. matrix = new Net1314080903142Dot[ROW][COL];//将行高,列宽传递进去,指定数组大小
14. for (int i = 0; i < ROW; i++) {//循环添加数据
15. for (int j = 0; j < COL; j++) {
16. matrix[i][j] = new Net1314080903142Dot(j, i);/*X,Y坐标值和行列值是相反的。
17. 即通过查找列值获得X坐标,查找行值获得Y坐标*/
18. }
19. }
20. setOnTouchListener(this);//设定为自己的触摸监听器
21. initGame();//调用游戏初始化
22. }

25. //坐标反转:封装一个getDot函数实现X,Y坐标反过来传递,所有的操作通过X,Y调用
26. private Net1314080903142Dot getDot(int x,int y) {
27. return matrix[y][x];
28. }

31. //实现猫移动到下一个点
32. private void MoveTo(Net1314080903142Dot one) {
33. one.setStatus(Net1314080903142Dot.STATUS_IN);//one的状态设置为猫所处的点
34. getDot(cat.getX(), cat.getY()).setStatus(Net1314080903142Dot.STATUS_OFF);;//将猫当前点的状态复位
35. cat.setXY(one.getX(), one.getY());//将猫移动到新的点
36. }
37. //猫的移动
38. private void move() {
39. if (isAtEdge(cat)) {
40. lose();return;/猫处于游戏边缘,失败
41. }
42. Vector&lt;Net1314080903142Dot> avaliable = new Vector&lt;Net1314080903142Dot>();//avaliable容器记录可用点
43. Vector&lt;Net1314080903142Dot> positive = new Vector&lt;Net1314080903142Dot>();//positive容器记录这个方向上可以直接到达屏幕边缘的路径
44. HashMap&lt;Net1314080903142Dot, Integer> al = new HashMap&lt;Net1314080903142Dot, Integer>();//al容器记录方向
45. for (int i = 1; i < 7; i++) {//如果当前猫被6个邻点围住
46. Net1314080903142Dot n = getNeighbour(cat, i);
47. if (n.getStatus() == Net1314080903142Dot.STATUS_OFF) {
48. avaliable.add(n);//如果相邻点可用,把它添加到avaliable记录器中
49. al.put(n, i);//为al传入方向i
50. if (getDistance(n, i) > 0) {
51. positive.add(n);//当它有一个路径可以直接到达屏幕边缘,把n传递进positive中

53. }
54. }
55. }
56. //移动算法的优化
57. if (avaliable.size() == 0) {
58. win();//周围的6个点都不可走,没有可用点,成功围住猫
59. }else if (avaliable.size() == 1) {
60. MoveTo(avaliable.get(0));//只有一个方向可走,可用点有一个,移动到这个可用点上
61. }else{//有多个方向可走
62. Net1314080903142Dot best = null;
63. if (positive.size() != 0 ) {//存在可以直接到达屏幕边缘的走向
64. System.out.println("向前进");
65. int min = 999;//999远大于场景中的所有可用步长,其他数也可
66. for (int i = 0; i < positive.size(); i++) {
67. int a = getDistance(positive.get(i), al.get(positive.get(i)));
68. if (a < min) {
69. min = a;//把最短路径长度传给min
70. best = positive.get(i);//选出拥有最短路径的点
71. }
72. }
73. MoveTo(best);
74. }else {//所有方向都存在路障
75. System.out.println("躲路障");
76. int max = 0;
77. for (int i = 0; i < avaliable.size(); i++) {
78. int k = getDistance(avaliable.get(i), al.get(avaliable.get(i)));
79. if (k <= max) {//所有方向都存在路障,距离k为负数
80. max = k;
81. best = avaliable.get(i);//选出拥有最短路径的点
82. }
83. }
84. MoveTo(best);//移动到最短路径的下一点
85. }
86. }
87. }

91. //实现界面绘制,在redraw方法中将所有元素以图形化显示出来,也就是将它绘制在Canvas对象上
92. private void redraw() {
93. Canvas c = getHolder().lockCanvas();//锁定画布
94. c.drawColor(Color.LTGRAY);//设置颜色为浅灰色
95. Paint paint = new Paint();//创建Paint对象
96. paint.setFlags(Paint.ANTI_ALIAS_FLAG);//开启抗锯齿,优化视频质量

98. //用两个For循环嵌套将所有的点显示到界面中来
99. for (int i = 0; i < ROW; i++) {
100. int offset = 0;//引入偏移量
101. if (i%2 != 0) {
102. offset = WIDTH/2;//对偶数行进行缩进
103. }
104. for (int j = 0; j < COL; j++) {
105. Net1314080903142Dot one = getDot(j, i);//将坐标赋值给内部变量one
106. //由于每个点对应的三种状态颜色不一样,要用一个switch语句
107. switch (one.getStatus()) {
108. case Net1314080903142Dot.STATUS_OFF:
109. paint.setColor(0xFFEEEEEE);//STATUS_OFF状态时设置颜色为浅灰色
110. break;
111. case Net1314080903142Dot.STATUS_ON:
112. paint.setColor(0xFFFFAA00);//STATUS_ON状态时设置颜色为橘色
113. break;
114. case Net1314080903142Dot.STATUS_IN:
115. paint.setColor(0xFFFF0000);//STATUS_IN状态时设置颜色为红色
116. break;

118. default:
119. break;
120. }
121. c.drawOval(new RectF(one.getX()*WIDTH+offset, one.getY()*WIDTH,
122. (one.getX()+1)*WIDTH+offset, (one.getY()+1)*WIDTH), paint);
123. /*在Canvas画布上画椭圆并界定它的上下左右边界宽度且有错位*/
124. }

126. }
127. getHolder().unlockCanvasAndPost(c);//取消Canvas的锁定,吧绘图内容更新到界面上
128. }

130. //为Surfaceview添加Callback
131. Callback callback = new Callback() {//声明并实例化一个Callback接口

133. @Override
134. public void surfaceDestroyed(SurfaceHolder arg0) {
135. // TODO Auto-generated method stub

137. }

139. @Override
140. public void surfaceCreated(SurfaceHolder arg0) {
141. // TODO Auto-generated method stub
142. redraw();//执行redraw函数,在界面第一次显示时将指定的内容显示到界面上
143. }

145. @Override
146. //使用surfaceChanged方法来适配不同的屏幕尺寸
147. public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {

149. //surfacechanged方法包含四个参数:SurfaceHolder holder,int format,int width,int height
150. // TODO Auto-generated method stub
151. WIDTH = arg2/(COL+1);//需要修改width,即arg2。
152. redraw();//重绘界面
153. }
154. };
155. //游戏初始化:分别对可走路径位置,猫的位置和路障位置进行初始化
156. private void initGame() {
157. //用for循环将所有点设置为STATUS_OFF,即可用状态
158. for (int i = 0; i < ROW; i++) {
159. for (int j = 0; j < COL; j++) {
160. matrix[i][j].setStatus(Net1314080903142Dot.STATUS_OFF);
161. }
162. }
163. cat = new Net1314080903142Dot(4, 5);//设置猫的起始点
164. getDot(4, 5).setStatus(Net1314080903142Dot.STATUS_IN);//把猫的起始点的状态设置为STATUS_IN,才能记录猫的位置

166. //用for循环随机的指定15个点的坐标作为路障
167. for (int i = 0; i < BLOCKS;) {
168. int x = (int) ((Math.random()*1000)%COL);
169. int y = (int) ((Math.random()*1000)%ROW);//随机获取1对坐标点
170. if (getDot(x, y).getStatus() == Net1314080903142Dot.STATUS_OFF) {//对当前可用路径点进行选择
171. getDot(x, y).setStatus(Net1314080903142Dot.STATUS_ON);//并把这个点设置为路障
172. i++;//循环内自加避免当前路障被重复添加
173. //System.out.println("Block:"+i);
174. }
175. }
176. }

178. @Override
179. //触摸事件的处理
180. public boolean onTouch(View arg0, MotionEvent e) {
181. if (e.getAction() == MotionEvent.ACTION_UP) {/当用户触摸之后手离开屏幕释放的瞬间才对事件进行响应
182. //    Toast.makeText(getContext(), e.getX()+":"+e.getY(), Toast.LENGTH_SHORT).show();
183. //将屏幕的坐标转换为游戏的坐标
184. int x,y;
185. y = (int) (e.getY()/WIDTH);//横向状态下,奇、偶数行有坐标偏移,而纵向的Y值是不变的,将y进行转换
186. if (y%2 == 0) {
187. x = (int) (e.getX()/WIDTH);//奇数行直接将屏幕的X坐标转换成游戏的X坐标
188. }else {
189. x = (int) ((e.getX()-WIDTH/2)/WIDTH);//偶数行偏移半个元素宽度,故需减去WIDTH/2
190. }
191. //数组越界异常时,对坐标进行保护
192. if (x+1 > COL || y+1 > ROW) {
193. initGame();//触摸超出边界时初始化游戏
194. }else if(getDot(x, y).getStatus() == Net1314080903142Dot.STATUS_OFF){
195. getDot(x, y).setStatus(Net1314080903142Dot.STATUS_ON);//当这个点可用时被点击之后设定为路障状态
196. move();
197. }
198. redraw();//将改变更新到界面
199. }
200. return true;
201. }

}

3.最后创建Activity,调用Playground。

public class Net1314080903142Activity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(new Net1314080903142Playground(this));//Context把MainActivity的this传递进去
}

}

备注:实现该游戏的其他算法未列出,有兴趣的可以在该网站看全部代码:https://github.com/hzuapps/android-labs/issues/139

###6. 自动滚动Banner图片

####简要说明:
本程序实现的是图片轮播banner,主要运用得到的控件为ViewPager,具体有以下功能:

>a.定时功能,每隔5S切换下一张图片。

>b.手动切换,可手势左右滑动选择上一张或下一张图片。
>
>c.跳转功能,点击跳转对应Activity

####详细步骤

1.指示器圆点,有两个状态,分别为选中和为选中。用两个shape实现。存放在drawable文件夹下

- 未选中状态:net1314080903118_dot_normal.xml

```xml

    <?xml version="1.0" encoding="utf-8"?>
    <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="oval" >

        <solid android:color="#33000000" />

        <corners android:radius="5dip" />

    </shape>
  • 选中状态:net1314080903118_dot_press.xml
    <?xml version="1.0" encoding="utf-8"?>
    <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="oval" >

        <solid android:color="#aaFFFFFF" />

        <corners android:radius="5dip" />

    </shape>

2.布局文件,主要用于显示图片的是ViewPager,九个View分别对应九个圆点,即可同时存放九张图片用于轮播。View的background使用上面定义的选中未选中状态。这里我单独抽取出来,使用时在主布局文件用 include 引入即可。

  • net1314080903118_banner.xml
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical" >

        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="160dp" >

            <android.support.v4.view.ViewPager
                android:id="@+id/vp"
                android:layout_width="match_parent"
                android:layout_height="160dp" />

            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="10dip"
                android:layout_gravity="bottom|center_horizontal"
                android:layout_marginBottom="10dp"
                android:gravity="center" >

                <View
                    android:id="@+id/v_dot0"
                    android:layout_width="5dip"
                    android:layout_height="5dip"
                    android:background="@drawable/net1314080903118_dot_press"
                    android:layout_marginLeft="1.5dip"
                    android:layout_marginRight="1.5dip"
                    android:visibility="invisible" />

                <View
                    android:id="@+id/v_dot1"
                    android:layout_width="5dip"
                    android:layout_height="5dip"
                    android:background="@drawable/net1314080903118_dot_normal"
                    android:layout_marginLeft="1.5dip"
                    android:layout_marginRight="1.5dip"
                    android:visibility="invisible"/>

                <View
                    android:id="@+id/v_dot2"
                    android:layout_width="5dip"
                    android:layout_height="5dip"
                    android:background="@drawable/net1314080903118_dot_normal"
                    android:layout_marginLeft="1.5dip"
                    android:layout_marginRight="1.5dip"
                    android:visibility="invisible"/>

                <View
                    android:id="@+id/v_dot3"
                    android:layout_width="5dip"
                    android:layout_height="5dip"
                    android:background="@drawable/net1314080903118_dot_normal"
                    android:layout_marginLeft="1.5dip"
                    android:layout_marginRight="1.5dip"
                    android:visibility="invisible"/>

                <View
                    android:id="@+id/v_dot4"
                    android:layout_width="5dip"
                    android:layout_height="5dip"
                    android:background="@drawable/net1314080903118_dot_normal"
                    android:layout_marginLeft="1.5dip"
                    android:layout_marginRight="1.5dip"
                    android:visibility="invisible" />
                <View
                    android:id="@+id/v_dot5"
                    android:layout_width="5dip"
                    android:layout_height="5dip"
                    android:background="@drawable/net1314080903118_dot_normal"
                    android:layout_marginLeft="1.5dip"
                    android:layout_marginRight="1.5dip"
                    android:visibility="invisible" />
                <View
                    android:id="@+id/v_dot6"
                    android:layout_width="5dip"
                    android:layout_height="5dip"
                    android:background="@drawable/net1314080903118_dot_normal"
                    android:layout_marginLeft="1.5dip"
                    android:layout_marginRight="1.5dip"
                    android:visibility="invisible" />
                <View
                    android:id="@+id/v_dot7"
                    android:layout_width="5dip"
                    android:layout_height="5dip"
                    android:background="@drawable/net1314080903118_dot_normal"
                    android:layout_marginLeft="1.5dip"
                    android:layout_marginRight="1.5dip"
                    android:visibility="invisible" />
                <View
                    android:id="@+id/v_dot8"
                    android:layout_width="5dip"
                    android:layout_height="5dip"
                    android:background="@drawable/net1314080903118_dot_normal"
                    android:layout_marginLeft="1.5dip"
                    android:layout_marginRight="1.5dip"
                    android:visibility="invisible" />
            </LinearLayout>
        </FrameLayout>

    </LinearLayout>

3.关键代码

定时切换用到一个类:ScheduledExecutorService

作用是定时执行任务,我们这里要做的定时任务是,5秒执行一次图片切换

    private void startAd() {
            scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
            // 当Activity显示出来后,每两秒切换一次图片显示
            scheduledExecutorService.scheduleAtFixedRate(new ScrollTask(), 1, 5,
                    TimeUnit.SECONDS);
        }

定义一个线程,滑动时通知handle响应,同时更改圆点索引号

    private class ScrollTask implements Runnable {

            @Override
            public void run() {
                synchronized (adViewPager) {
                    currentItem = (currentItem + 1) % imageViews.size();
                    handler.obtainMessage().sendToTarget();
                }
            }
        }

通过handle来通知ViewPager进行视图切换

    private Handler handler = new Handler() {
            public void handleMessage(android.os.Message msg) {
                adViewPager.setCurrentItem(currentItem);
            };
        };

异步线程加载网络图片,具体代码我封装在Net1314080903118MyRequest.java中,详情可查看最后的链接。

4.具体代码如下:

    public class BannerTestActivity extends AppCompatActivity {

        private Context mContext;
        private ViewPager adViewPager;
        private List<ImageView> imageViews;// 滑动的图片集合
        private List<View> dots; // 图片标题正文的那些点
        private List<View> dotList;

        private int currentItem = 0; // 当前图片的索引号
        // 定义的五个指示点
        private View dot0,dot1,dot2,dot3,dot4,dot5,dot6,dot7,dot8;

        // 定时任务
        private ScheduledExecutorService scheduledExecutorService;

        // 轮播banner的数据
        private List<String> mDataList = null;

        private Handler handler = new Handler() {
            public void handleMessage(android.os.Message msg) {
                adViewPager.setCurrentItem(currentItem);
            };
        };

        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.net1314080903118);
            setTitle("Net1314080903118");
            mContext = BannerTestActivity.this;
            initDatas();
            initView();
            startAd();
        }

        /**
         * 添加九张图片的数据,用作模拟数据
         */
        private void initDatas() {
            mDataList = new ArrayList<>();
            mDataList.add("http://image.wufazhuce.com/Fi7nIAI3UFK4x42mwu4g18O3s8JO");
            mDataList.add("http://image.wufazhuce.com/Fn8JdRZ1g9IVbfSxrdAD6zYFVz3h");
            mDataList.add("http://image.wufazhuce.com/FjJPOSVbypiddiRbyHrws7AN6NlX");
            mDataList.add("http://image.wufazhuce.com/FiyEgeiSFJFLrOQxerXZOY-50LQr");
            mDataList.add("http://image.wufazhuce.com/Fg-EwcAuXvdd2ZvOQI8ym4EToRot");
            mDataList.add("http://image.wufazhuce.com/Fi7nIAI3UFK4x42mwu4g18O3s8JO");
            mDataList.add("http://image.wufazhuce.com/Fn8JdRZ1g9IVbfSxrdAD6zYFVz3h");
            mDataList.add("http://image.wufazhuce.com/FjJPOSVbypiddiRbyHrws7AN6NlX");
            mDataList.add("http://image.wufazhuce.com/FiyEgeiSFJFLrOQxerXZOY-50LQr");
            mDataList.add("http://image.wufazhuce.com/Fg-EwcAuXvdd2ZvOQI8ym4EToRot");
            mDataList.add("http://image.wufazhuce.com/Fi7nIAI3UFK4x42mwu4g18O3s8JO");
        }

        private void initView() {
            imageViews = new ArrayList<ImageView>();
            // 点
            dots = new ArrayList<View>();
            dotList = new ArrayList<View>();
            dot0 = findViewById(R.id.v_dot0);
            dot1 = findViewById(R.id.v_dot1);
            dot2 = findViewById(R.id.v_dot2);
            dot3 = findViewById(R.id.v_dot3);
            dot4 = findViewById(R.id.v_dot4);
            dot5 = findViewById(R.id.v_dot5);
            dot6 = findViewById(R.id.v_dot6);
            dot7 = findViewById(R.id.v_dot7);
            dot8 = findViewById(R.id.v_dot8);
            dots.add(dot0);
            dots.add(dot1);
            dots.add(dot2);
            dots.add(dot3);
            dots.add(dot4);
            dots.add(dot5);
            dots.add(dot6);
            dots.add(dot7);
            dots.add(dot8);

            adViewPager = (ViewPager) findViewById(R.id.vp);

            loadViewPager();
        }

        private void addDynamicView() {
            // 动态添加图片和下面指示的圆点
            // 初始化图片资源
            for (int i = 0; i < mDataList.size(); i++) {
                ImageView imageView = new ImageView(mContext);
                new Net1314080903118MyRequest(mContext).getImage(mDataList.get(i),imageView);
                imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
                imageViews.add(imageView);
                dots.get(i).setVisibility(View.VISIBLE);
                dotList.add(dots.get(i));
            }

        }

        /**
         * 定时任务,5s更换一次
         */
        private void startAd() {
            scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
            // 当Activity显示出来后,每两秒切换一次图片显示
            scheduledExecutorService.scheduleAtFixedRate(new ScrollTask(), 1, 5,
                    TimeUnit.SECONDS);
        }

        /**
         * 自定义线程,滑动时通知handle响应
         */
        private class ScrollTask implements Runnable {

            @Override
            public void run() {
                synchronized (adViewPager) {
                    currentItem = (currentItem + 1) % imageViews.size();
                    handler.obtainMessage().sendToTarget();
                }
            }
        }

        @Override
        public void onStop() {
            super.onStop();
            // 当Activity不可见的时候停止切换
            scheduledExecutorService.shutdown();
        }

        /**
         * 重写ViewPager,改变圆点是否选中状态
         * 在这里我们不需要重写ViewPager的滑动动作
         */
        private class MyPageChangeListener implements ViewPager.OnPageChangeListener {

            private int oldPosition = 0;

            @Override
            public void onPageScrollStateChanged(int arg0) {

            }

            @Override
            public void onPageScrolled(int arg0, float arg1, int arg2) {

            }

            @Override
            public void onPageSelected(int position) {
                currentItem = position;
                dots.get(oldPosition).setBackgroundResource(R.drawable.net1314080903118_dot_normal);
                dots.get(position).setBackgroundResource(R.drawable.net1314080903118_dot_press);
                oldPosition = position;
            }
        }

        private void loadViewPager() {
            addDynamicView();
            // 设置填充ViewPager页面的适配器
            adViewPager.setAdapter(new Net1314080903118MyAdapter(mContext,mDataList, imageViews));
            // 设置一个监听器,当ViewPager中的页面改变时调用
            adViewPager.setOnPageChangeListener(new MyPageChangeListener());
        }

        /**
         * 自定义ViewPager的适配器,具体点击图片的跳转逻辑在instantiateItem中实现
         */
        public class Net1314080903118MyAdapter extends PagerAdapter {

            private List<String> DataList;
            private List<ImageView> imageList;
            private Context mContext;

            public Net1314080903118MyAdapter(Context context,List<String> adList,List<ImageView> imageViews){
                this.DataList = adList;
                this.imageList = imageViews;
                this.mContext = context;
            }

            @Override
            public int getCount() {
                return DataList.size();
            }

            @Override
            public Object instantiateItem(ViewGroup container, int position) {
                ImageView iv = imageList.get(position);
                ((ViewPager) container).addView(iv);
                // 在这个方法里面设置图片的点击事件
                iv.setOnClickListener(new View.OnClickListener() {

                    @Override
                    public void onClick(View v) {
                        // 处理跳转逻辑
                    }
                });
                return iv;
            }

            @Override
            public void destroyItem(View arg0, int arg1, Object arg2) {
                ((ViewPager) arg0).removeView((View) arg2);
            }

            @Override
            public boolean isViewFromObject(View arg0, Object arg1) {
                return arg0 == arg1;
            }

            @Override
            public void restoreState(Parcelable arg0, ClassLoader arg1) {

            }

            @Override
            public Parcelable saveState() {
                return null;
            }

            @Override
            public void startUpdate(View arg0) {

            }

            @Override
            public void finishUpdate(View arg0) {

            }

        }
    }

备注:完整代码请访问以下链接

https://github.com/hzuapps/android-labs/issues/139