android游戏开发项目实战——数独

一、程序运行效果图

android游戏开发项目实战——数独_第1张图片



二、代码实现


1、main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world" />

</RelativeLayout>


2、dialog.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="match_parent"
    android:orientation="vertical" >
    

    <TextView 
        android:id="@+id/usedTextId"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello_world"
        />
</LinearLayout>


3、keypad1.xml

<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" 
    android:id="@+id/keypad"
    android:orientation="vertical"
    >
    
    <TableRow >
        <Button android:id="@+id/keypad_1"
            android:text="1"
            />
        
        <Button android:id="@+id/keypad_2"
            android:text="2"
            />
        
        <Button android:id="@+id/keypad_3"
            android:text="3"
            />
    </TableRow>
    
    
    <TableRow >
        <Button android:id="@+id/keypad_4"
            android:text="4"
            />
        
        <Button android:id="@+id/keypad_5"
            android:text="5"
            />
        
        <Button android:id="@+id/keypad_6"
            android:text="6"
            />
    </TableRow>
    
    
    <TableRow >
        <Button android:id="@+id/keypad_7"
            android:text="7"
            />
        
        <Button android:id="@+id/keypad_8"
            android:text="8"
            />
        
        <Button android:id="@+id/keypad_9"
            android:text="9"
            />
    </TableRow>

</TableLayout>



4、MainActivity

package com.njupt.shudu;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(new ShuduView(this));
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

}


5、ShuduView

package com.njupt.shudu;

import android.app.AlertDialog;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.FontMetrics;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;

public class ShuduView extends View{

	//单元格的宽度和高度
	private float width;
	private float height;
	
	private Game game = new Game();
	private int selectedX;
	private int selectedY;
	
	public ShuduView(Context context) {
		super(context);
		
	}
	
	/**
	 * w:当前view的宽度
	 * h:当前view的高度
	 * 
	 */
	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		//计算当前单元格的宽度和高度
		this.width = w / 9f;
		this.height = h / 9f;
		
		super.onSizeChanged(w, h, oldw, oldh);
	}
	
	@Override
	protected void onDraw(Canvas canvas) {
		//生成用于绘制背景色的画笔
		Paint backgroundPaint = new Paint();
		//设置画笔的颜色
		backgroundPaint.setColor(getResources().getColor(R.color.shudu_background));
		//绘制背景色
		canvas.drawRect(0, 0,getWidth(),getHeight(),backgroundPaint);
		
		Paint darkPaint = new Paint();
		darkPaint.setColor(getResources().getColor(R.color.shudu_dark));
		
		Paint hilitePaint = new Paint();
		hilitePaint.setColor(getResources().getColor(R.color.shudu_hilite));
		
		Paint lightPaint = new Paint();
		lightPaint.setColor(getResources().getColor(R.color.shudu_light));
		
		/**
		 * 绘制用于分割小九宫格的线(即将屏幕分成81个格子)
		 */
		for(int i = 0 ; i < 9 ; ++i){
			/**
			 * canvas.drawLine(0, i*height, getWidth(),i*height, lightPaint)
			 * 第1、2个参数: 起点的坐标
			 * 第3、4个参数: 终点的坐标
			 * 第5个参数: 所使用的画笔
			 */
			canvas.drawLine(0, i*height, getWidth(),i*height, lightPaint);//划横线
			canvas.drawLine(0, i*height + 1, getWidth(), i*height + 1, hilitePaint);//也是划横线,为了对比突出那种"刻出来"的效果
			canvas.drawLine(i*width, 0, i*width, getHeight(), lightPaint);
			canvas.drawLine(i*width + 1, 0, i*width + 1, getHeight() , hilitePaint);
			
		}
		
		/**
		 * 绘制用于将屏幕分成9个大九宫格的线
		 */
		for(int i = 0 ; i < 9 ; ++i){
			if(i % 3 == 0){
				continue;
			}
			
			canvas.drawLine(0, i*height, getWidth(),i*height, darkPaint);//划横线
			canvas.drawLine(0, i*height + 1, getWidth(), i*height + 1, hilitePaint);//也是划横线,为了对比突出那种"刻出来"的效果
			canvas.drawLine(i*width, 0, i*width, getHeight(), darkPaint);
			canvas.drawLine(i*width + 1, 0, i*width + 1, getHeight() , hilitePaint);
			
		}
		
		//绘制数字
		Paint numberPaint = new Paint();
		numberPaint.setColor(Color.BLACK);
		numberPaint.setStyle(Paint.Style.STROKE);
		numberPaint.setTextSize(height*0.75f);
		numberPaint.setTextAlign(Paint.Align.CENTER);//设置对齐方式
		
		FontMetrics fm = numberPaint.getFontMetrics();
		float x = width / 2;
		float y = height/2 - (fm.ascent + fm.descent)/2;
		
		//生成数独的初始化数据
		for(int i = 0 ; i < 9 ; ++i){
			for(int j = 0 ; j < 9 ; ++j){
				canvas.drawText(game.getTileString(i, j), i*width + x, j*height + y, numberPaint);
			}
		}
		
		super.onDraw(canvas);
	}
	
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		if(event.getAction() != MotionEvent.ACTION_DOWN){
			return super.onTouchEvent(event);
		}
		
		//判断用户点击的是哪一个单元格
		selectedX = (int)(event.getX() / width);
		selectedY = (int)(event.getY() / height);
		
		int used[] = game.getUsedTilesByCoor(selectedX, selectedY);
		StringBuffer sb = new StringBuffer();
		for(int i = 0 ; i < used.length ; ++i){//用来验证一下看对不对
			sb.append(used[i]);
		}
		
//		//生成一个LayoutInflater对象
//		LayoutInflater layoutInflater = LayoutInflater.from(this.getContext());
//		//使用LayoutInflater对象根据一个布局文件,生成一个View
//		View layoutView = layoutInflater.inflate(R.layout.dialog, null);
//		//从生成好的TextView中,取出相应的控件
//		TextView textView = (TextView)layoutView.findViewById(R.id.usedTextId);
//		//设置TextView的内容
//		textView.setText(sb.toString());
//		//生成一个对话框的Builder对象
//		AlertDialog.Builder builder = new AlertDialog.Builder(this.getContext());
//		//设置对话框索要显示的内容
//		builder.setView(layoutView);
//		//生成对话框对象,并将其显示出来
//		AlertDialog dialog = builder.create();
//		dialog.show();
//	
		KeyDialog keyDialog = new KeyDialog(getContext(),used,this);
		keyDialog.show();
		
		return true;
	}

	public void setSelectedTile(int tile) {
		if(game.setTileIfValid(selectedX,selectedY,tile)){
			invalidate();
		}
	}
}


7、Game

package com.njupt.shudu;

public class Game {

	//数独初始化数据的基础
	private final String str = "360000000004230800000004200"
			+"070460003820000014500013020"
			+"001900000007048300000000045";
	
	private int sudoku[] = new int[9*9];
	//用于存储每个单元格已经不可用的数据
	private int used[][][] = new int[9][9][];
	
	public Game() {
		sudoku = fromPuzzleString(str);
		calculateAllUsedTiles();
	}
	
	

	/**
	 * 根据九宫格当中的坐标,返回该坐标所应该填写的数字
	 * @param x
	 * @param y
	 * @return
	 */
	private int getTile(int x, int y){
		return sudoku[y*9 + x];
	}
	
	/**
	 * 根据x轴坐标和y轴坐标得到这一单元格不可用的数据
	 * @param x
	 * @param y
	 * @return
	 */
	public String getTileString(int x, int y){
		int v = getTile(x,y);
		if(v == 0){
			return "";
		}else{
			return String.valueOf(v);
		}
	}
	
	/**
	 * 根据一个字符串数据,生成一个整型数组,所谓数独游戏的初始化数据
	 * @param src
	 * @return
	 */
	protected int[] fromPuzzleString(String src){
		int[] sudo = new int[src.length()];
		
		for(int i = 0 ; i < sudo.length ; ++i){
			sudo[i] = src.charAt(i) - '0'; 
		}
		
		return sudo;
	}
	
	/**
	 * 计算所有单元格对应的不可用的数据
	 */
	public void calculateAllUsedTiles(){
		for(int x = 0 ; x < 9 ; ++x){
			for(int y = 0 ; y < 9 ; ++y){
				used[x][y] = calculateUsedTiles(x, y);
			}
		}
	}
	
	
	/**
	 * 取出某一单元格中已经不可用的数据
	 * @param x
	 * @param y
	 * @return
	 */
	public int[] getUsedTilesByCoor(int x, int y){
		return used[x][y];
	}
	
	
	/**
	 * 计算某一单元格之中已经不可用的数据
	 * @param x
	 * @param y
	 */
	public int[] calculateUsedTiles(int x,int y) {
		int c[] = new int[9];
		
		/**
		 * 计算在y轴(列)方向上那些数字不可用...
		 */
		for(int i = 0 ; i < 9 ; ++i){
			if(i == y){//如果这是用户点击的格子
				continue;
			}
			
			int t = getTile(x,i);
			if(t != 0){
				c[t - 1] = t;
			}
		}
		
		for(int i = 0 ; i < 9 ; ++i){
			if(i == x){
				continue;
			}
			
			int t = getTile(i,y);
			if(t != 0){
				c[t - 1] = t;
			}
		}
		
		/**
		 * 计算在小的九宫格中有那些数字已经用过了..
		 */
		int startX = (x/3)*3;
		int startY = (y/3)*3;
		for(int i = startX ; i < startX + 3 ; ++i){
			for(int j = startY ; j < startY + 3 ; ++j){
				if(i == x && j == y){
					continue;
				}
				
				int t = getTile(i, j);
				if(t != 0 ){
					c[t - 1] = t;
				}
			}
		}
		
		
		/**
		 * 把c中的0给去掉
		 */
		int nused = 0;
		for(int t : c){
			if(t != 0){
				nused++;
			}
		}
		
		int c1[] = new int[nused];
		nused = 0;
		for(int t : c){
			if(t != 0){
				c1[nused++] = t;
			}
		}
		
		return c1;
		
		
	}



	public boolean setTileIfValid(int x, int y, int value) {
		int tiles[] = getUesdTiles(x,y);
		if(value != 0){
			for(int tile : tiles){
				if(tile == value){
					return false;
				}
			}
		}
		
		setTile(x,y,value);//把用户输入的数字添加到九宫格中
		calculateAllUsedTiles();//更新该单元格可以使用的数字
		return true;
	}



	protected int[] getUesdTiles(int x, int y) {
		return used[x][y];
	}
	
	private void setTile(int x, int y, int value){
		sudoku[y*9 + x] = value;
	}
}


8、KeyDialog

package com.njupt.shudu;

import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.view.View;

/**
 * 该类用于实现Dialog,实现自定义的对话框功能...
 * @author Administrator
 *
 */
public class KeyDialog extends Dialog{

	//用来存放代表对话框当中按钮的对象
	private final View keys[] = new View[9];
	private final int used[];
	
	private ShuduView shuduView;
	/**
	 * 
	 * 构造函数的第二个参数中保存着当前单元格已经使用过的数字
	 * @param context
	 * @param used
	 */
	public KeyDialog(Context context , int[] used , ShuduView shuduView) {
		super(context);
		this.used = used;
		this.shuduView = shuduView;
	}
	
	/**
	 * 当一个Dialog第一次显示的时候,会调用其onCreate方法
	 */
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		
		//设置对话框的标题
		setTitle("KeyDialog");
		//用于为该Dialog设置布局文件
		setContentView(R.layout.keypad1);
		findViews();
		
		//显示某一单元格中可用的数字
		for(int i = 0 ; i < used.length ; ++i){
			if(used[i] != 0){
				keys[used[i] - 1].setVisibility(View.INVISIBLE);
			}
		}
		
		//为对话框当中所有按钮设置监听器
		setListeners();
	}

	private void findViews() {
		keys[0] = findViewById(R.id.keypad_1);
		keys[1] = findViewById(R.id.keypad_2);
		keys[2] = findViewById(R.id.keypad_3);
		keys[3] = findViewById(R.id.keypad_4);
		keys[4] = findViewById(R.id.keypad_5);
		keys[5] = findViewById(R.id.keypad_6);
		keys[6] = findViewById(R.id.keypad_7);
		keys[7] = findViewById(R.id.keypad_8);
		keys[8] = findViewById(R.id.keypad_9);
		
	}
	
	/**
	 * 通知ShuduView对象,刷新挣个九宫格显示的数据
	 * @param tile
	 */
	private void returnResult(int tile){
		System.out.println("shuduView: " + shuduView);
		shuduView.setSelectedTile(tile);
		dismiss();//取消对话框的显示
	}
	
	private void setListeners(){
		for(int i = 0 ; i < keys.length ; ++i){
			final int t = i + 1;
			keys[i].setOnClickListener(new View.OnClickListener() {
				
				@Override
				public void onClick(View v) {
					returnResult(t);
				}
			});
		}
	}
}



本项目源码下载:http://download.csdn.net/detail/caihongshijie6/6883785

你可能感兴趣的:(android游戏开发项目实战——数独)