android开发——数独游戏
最近研究了一下android,写了一个数独游戏,具体如下:
游戏界面需要重写一个ShuduView继承View,
然后自定义一个Dialog:
1.需要继承 Dialog 类,
2.并要定义一个有参构造函数(因为父类里面没有无参构造函数)
3.重写 onCreate方法,一切操作将在此方法进行
流程:
为每个按钮添加监听事件,
刷新九宫格里的数字,也就是重新绘制画面(在view类中调用 invalidate();),
更新备选数字数组 ( 每次修改之后都得 进行重新计算 不可用的值 calculateAllUsedTiles() ; )
下面介绍主要代码:
ShuduView:
1 package com.soccer.shudu;
2
3 import android.content.Context;
4 import android.graphics.*;
5 import android.graphics.Paint.FontMetrics;
6
7
8 import android.view.MotionEvent;
9 import android.view.View;
10 import android.view.WindowManager;
11
12
13 public class ShuduView extends View{
14
15 public ShuduView(Context context) {
16 super(context);
17 // TODO Auto-generated constructor stub
18 }
19
20 private float width;
21 private float height;
22
23
24 private Game game = new Game();
25
26 @Override
27 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
28 //计算当前单元格宽度和高度
29 this.height = h / 9f;
30 this.width = w / 9f;
31 super.onSizeChanged(w, h, oldw, oldh);
32 }
33
34
35
36 //当android的系统需要绘制一个view对象时,就需要调用该对象的onDraw方法
37 @Override
38 protected void onDraw(Canvas canvas) {
39 //生成用户绘制背景色的画笔
40 Paint backgroundPaint = new Paint();
41 //设置画笔颜色
42 backgroundPaint.setColor(getResources().getColor(R.color.shudu_background));
43 //绘制背景色
44 canvas.drawRect(0, 0, getWidth(), getHeight(), backgroundPaint);
45
46 Paint darkPaint = new Paint();
47 darkPaint.setColor(getResources().getColor(R.color.shudu_dark));
48
49 Paint hilitePaint = new Paint();
50 hilitePaint.setColor(getResources().getColor(R.color.shudu_hilite));
51
52 Paint lightPaint = new Paint();
53 lightPaint.setColor(getResources().getColor(R.color.shudu_light));
54
55 for(int i = 0;i < 9;i++){
56 //绘制横向的线
57 canvas.drawLine(0, i * height,getWidth(), i * height, lightPaint);
58 canvas.drawLine(0, i * height + 1,getWidth(), i * height + 1, hilitePaint);
59 //绘制纵向的线
60 canvas.drawLine(i * width, 0, i * width, getHeight(), lightPaint);
61 canvas.drawLine(i * width + 1, 0, i * width + 1, getHeight(), hilitePaint);
62 }
63
64 for(int i = 0;i < 3;i++){
65 //绘制横向的线
66 canvas.drawLine(0, i * 3 * height, getWidth(), i * 3 * height, darkPaint);
67 canvas.drawLine(0, i * 3 * height + 1, getWidth(), i * 3 * height + 1, darkPaint);
68 //绘制纵向的线
69 canvas.drawLine(i * 3 * width, 0, i * 3 * width, getHeight(), darkPaint);
70 canvas.drawLine(i * 3 * width + 1, 0, i * 3 * width + 1, getHeight(), darkPaint);
71 }
72 //绘制数字
73 Paint numberPaint = new Paint();
74 numberPaint.setColor(Color.BLACK);
75 //numberPaint.setStyle(Paint.Style.STROKE);
76 numberPaint.setTextSize( height * 0.75f);
77 numberPaint.setTextAlign(Paint.Align.CENTER);
78 //将数字加到格子中
79 FontMetrics fm = numberPaint.getFontMetrics();
80 float x = width / 2;
81 float y = height / 2 - (fm.ascent + fm.descent) / 2;
82 //canvas.drawText("1", 3 * width + x, height + y , numberPaint);
83 for(int i = 0;i < 9;i++){
84 for(int j = 0;j < 9;j++){
85 canvas.drawText(game.getTileString(i, j), i * width+x, j * height + y, numberPaint);
86 }
87 }
88
89
90 super.onDraw(canvas);
91 }
92
93 int selectedX;
94 int selectedY;
95
96
97
98 @Override
99 public boolean onTouchEvent(MotionEvent event) {
100 if(event.getAction() != MotionEvent.ACTION_DOWN){
101 return super.onTouchEvent(event);
102 }
103
104 selectedX = (int)(event.getX() / width);
105 selectedY = (int)(event.getY() / height);
106
107 int used[] = game.getUsedTileByCoor(selectedX, selectedY);
108 StringBuffer sb = new StringBuffer();
109 for(int i = 0; i < used.length;i++){
110 System.out.println(used[i] );
111 //sb.append(used[i]);
112 }
113
114 //生成一个LayoutInflater对象
115 //LayoutInflater layoutInflater = LayoutInflater.from(this.getContext());
116 //View layoutView = layoutInflater.inflate(R.layout.dialog, null);
117 //从生成好的textView中 取出相应的控件
118 //TextView textView =(TextView)layoutView.findViewById(R.id.usedTextId);
119
120 //textView.setText(sb.toString());
121
122 //AlertDialog.Builder builder = new AlertDialog.Builder(this.getContext());
123 //builder.setView(layoutView);
124
125 //AlertDialog dialog = builder.create();
126 //dialog.show();
127 //dialog.getWindow().setLayout(300, 200);
128 KeyDialog keyDialog = new KeyDialog(getContext(), used,this);
129 //透明度
130 WindowManager.LayoutParams lp=keyDialog.getWindow().getAttributes();
131 lp.alpha=0.9f;
132 keyDialog.getWindow().setAttributes(lp);
133
134 keyDialog.show();
135 return true;
136 }
137
138 public void setSelectedTile(int tile){
139
140 if(game.setTileIfValid(selectedX,selectedY,tile)){
141 invalidate();
142 }
143 }
144
145 }
KeyDialog:
1 package com.soccer.shudu;
2
3 import android.app.Dialog;
4 import android.content.Context;
5 import android.os.Bundle;
6 import android.view.View;
7 import android.widget.Button;
8
9
10 //该类用于实现dialog自定义对话框功能
11 public class KeyDialog extends Dialog{
12 private final View keys [] = new View[9];
13 private final int used[];
14
15 private ShuduView shuduView = null;
16
17 public KeyDialog(Context context,int [] used,ShuduView shuduView){
18 super(context);
19 this.used = used;
20 this.shuduView = shuduView;
21 }
22 //当一个dialog第一次显示的时候,会调用其onCreate方法
23 @Override
24 protected void onCreate(Bundle savedInstanceState) {
25 super.onCreate(savedInstanceState);
26 setTitle("可选数字");
27 setContentView(R.layout.keypad);
28 findViews();
29 for(int i = 0;i < used.length;i++){
30 if(used[i] != 0){
31 keys[used[i] - 1].setVisibility(View.INVISIBLE);
32 }
33 }
34 //为每个键设置监听器
35 setListeners();
36
37 //为返回按钮设置监听器
38 Button back_Button = (Button)findViewById(R.id.back_1);
39 back_Button.setOnClickListener(new View.OnClickListener() {
40 @Override
41 public void onClick(View v) {
42 dismiss();
43
44 }
45 });
46 }
47
48
49
50
51 private void findViews(){
52 keys[0] = findViewById(R.id.keypad_1);
53 keys[1] = findViewById(R.id.keypad_2);
54 keys[2] = findViewById(R.id.keypad_3);
55 keys[3] = findViewById(R.id.keypad_4);
56 keys[4] = findViewById(R.id.keypad_5);
57 keys[5] = findViewById(R.id.keypad_6);
58 keys[6] = findViewById(R.id.keypad_7);
59 keys[7] = findViewById(R.id.keypad_8);
60 keys[8] = findViewById(R.id.keypad_9);
61 }
62
63 //通知ShuduView对象,刷新整个九宫格显示的数据
64 private void returnResult(int tile){
65 shuduView.setSelectedTile(tile);
66 dismiss();
67 }
68
69 private void setListeners(){
70 for(int i = 0; i < keys.length; i++){
71 final int t = i + 1;
72 keys[i].setOnClickListener(new View.OnClickListener() {
73
74 @Override
75 public void onClick(View v) {
76 returnResult(t);
77
78 }
79 });
80 }
81 }
82
83 }
keypad.xml:
1 <?xml version="1.0" encoding="utf-8"?>
2 <TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 android:id="@+id/keypad"
4 android:orientation="vertical"
5 android:layout_width="wrap_content"
6 android:layout_height="wrap_content"
7 android:stretchColumns="*" >
8
9 <TableRow>
10 <Button android:id="@+id/keypad_1"
11 android:text="1">
12 </Button>
13 <Button android:id="@+id/keypad_2"
14 android:text="2">
15 </Button>
16 <Button android:id="@+id/keypad_3"
17 android:text="3">
18 </Button>
19 </TableRow>
20
21 <TableRow>
22 <Button android:id="@+id/keypad_4"
23 android:text="4">
24 </Button>
25 <Button android:id="@+id/keypad_5"
26 android:text="5">
27 </Button>
28 <Button android:id="@+id/keypad_6"
29 android:text="6">
30 </Button>
31 </TableRow>
32
33 <TableRow>
34 <Button android:id="@+id/keypad_7"
35 android:text="7">
36 </Button>
37 <Button android:id="@+id/keypad_8"
38 android:text="8">
39 </Button>
40 <Button android:id="@+id/keypad_9"
41 android:text="9">
42 </Button>
43 </TableRow>
44
45 <TableRow>
46 <Button android:text="">
47 </Button>
48 <Button android:id="@+id/back_1"
49 android:text="@string/back_1">
50 </Button>
51 <Button android:text="">
52 </Button>
53 </TableRow>
54
55 </TableLayout>
Game.java:
1 package com.soccer.shudu;
2
3 public class Game {
4 //数独初始化数据
5 private final String str = "360000000004230800000004200"
6 +"070460003820000014500013020"
7 +"001900000007048300000000045";
8 private int sudoku [] = new int[9*9];
9 //用于存储每个单元格已经不可用的数据
10 private int used[][][] = new int[9][9][];
11
12 public Game(){
13 sudoku = fromPuzzleString(str);
14 calculateAllUsedTile();
15 }
16 //根据九宫格当中的坐标,返回该坐标对应的数字
17 private int getTile(int x,int y){
18 return sudoku[y*9 + x];
19 }
20 private void setTile(int x,int y,int value){
21 sudoku[y * 9 + x] = value;
22 }
23
24 public String getTileString(int x, int y){
25 int v = getTile(x,y);
26 if(v == 0){ //0就不显示
27 return "";
28 }
29 else
30 return String.valueOf(v);
31 }
32 protected boolean setTileIfValid(int x,int y,int value){
33 int tiles[] = getUsedTileByCoor(x,y);
34 if(value != 0){
35 for(int tile : tiles){
36 if(tile == value)
37 return false;
38 }
39 }
40 setTile(x,y,value);
41 calculateAllUsedTile();
42 return true;
43 }
44
45 //根据一个字符串数据生成一个整形数组,作为数独游戏的初始化数据
46 protected int[] fromPuzzleString(String src){
47 int []sudo = new int[src.length()];
48 for(int i = 0;i < sudo.length;i++)
49 {
50 sudo[i] = src.charAt(i) - '0';
51 }
52 return sudo;
53 }
54
55 //计算所有单元格对应的不可用数据
56 public void calculateAllUsedTile(){
57 for(int x = 0;x < 9;x++){
58 for(int y = 0;y < 9;y++){
59 used[x][y] = calculateUsedTiles(x, y);
60 }
61 }
62 }
63
64 //取出某一单元格当中已经不可用的数据
65 public int[] getUsedTileByCoor(int x,int y){
66 return used[x][y];
67 }
68
69 //计算某一单元格当中已经不可用的数据
70 public int[] calculateUsedTiles(int x,int y){
71 int c[] = new int[9];
72
73 for(int i = 0;i < 9;i++){
74 if(i == y)
75 continue;
76 int t = getTile(x, i);
77 if(t != 0)
78 c[t - 1] = t;
79 }
80
81 for(int i = 0;i < 9;i++){
82 if(i == x)
83 continue;
84 int t = getTile(i, y);
85 if(t != 0)
86 c[t - 1] = t;
87 }
88
89 int startx = (x / 3) * 3;
90 int starty = (y / 3) * 3;
91 for(int i = startx; i < startx + 3; i++){
92 for(int j = starty;j < starty + 3; j++){
93 if(i == x && j == y)
94 continue;
95 int t = getTile(i,j);
96 if(t!=0)
97 c[t-1] = t;
98 }
99 }
100 //compress
101 int nused = 0;
102 for(int t:c){
103 if(t!=0)
104 nused++;
105 }
106 int c1[] = new int[nused];
107 nused = 0;
108 for(int t:c){
109 if(t!=0)
110 c1[nused++] = t;
111 }
112 return c1;
113 }
114 }
界面效果如下: