一、程式運行
二、代碼實現
1、main.xml
2、dialog.xml
3、keypad1.xml
4、MainActivity
package com.njupt.shudu;import android.os.Bundle;import android.app.Activity;import android.view.Menu;public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(new ShuduView(this));}@Overridepublic 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的高度 * */@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {//計算目前的儲存格的寬度和高度this.width = w / 9f;this.height = h / 9f;super.onSizeChanged(w, h, oldw, oldh);}@Overrideprotected 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);}@Overridepublic 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方法 */@Overrideprotected 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() {@Overridepublic void onClick(View v) {returnResult(t);}});}}}