老规矩,先上GIF动态图,看个效果,如果符合你的项目或肯定你要了解的内容,再往下看吧:
看1下代码里面的K1,K2,K3,K4,K5,K6,就能够了解函数运行的顺序,最后在看1下setTileIfValid方法就能够了。
package com.iwanghang.newview; import android.app.Dialog; import android.content.Context; import android.os.Bundle; import android.view.View; // 该类用于实现Dialog,实现自定义的对话框功能 public class KeyDialog extends Dialog { // 用来寄存代表对话框当中按钮的对象 private final View keys [] = new View[9]; private final int used[]; private NewView newView; // 构造函数的第2个参数当中保存着当前单元格已使用过的数字 public KeyDialog(Context context, int [] used){ super(context); this.used = used; } // 构造函数的第2个参数当中保存着当前单元格已使用过的数字 public KeyDialog(Context context,int [] used,NewView newView){ super(context); this.used = used; this.newView = newView; } // 当1个Dialog第1次显示的时候,会调用其onCreate方法 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 设置对话框的标题 setTitle("KeyDialog"); // 用于为该Dialog设置布局文件 setContentView(R.layout.keypad); // findViews(); // 遍历全部used数组 for (int i = 0; i <used.length; i++) { if(used[i] != 0){ System.out.println(used[i]); 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); } // KeyDialog初始化的时候,设置数字按键监听,K1 private void setListeners() { // 遍历全部keys数组 for (int i = 0; i < keys.length; i++) { final int keyPress = i + 1; keys[i].setOnClickListener(new View.OnClickListener() { public void onClick(View v) { returnResult(keyPress); // 返回点击的数字,K2 } }); } } // 通知NewView对象,刷新全部9宫格显示的数据 private void returnResult(int tile) { newView.setSelectedTile(tile); // 调用NewView中的方法,K3 // 取消对话框的显示 dismiss(); } }NewView.java:
package com.iwanghang.newview; import android.app.AlertDialog; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.os.Handler; import android.os.Message; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.widget.TextView; public class NewView extends View{ // 单元格的宽度和高度 private float width; private float height; private int checkPoint = 1; // 当前关卡 private MainActivity mainActivity = (MainActivity) getContext(); //private Number number = new Number(); private Number number = new Number(checkPoint); private int selectedX; private int selectedY; public NewView(Context context, AttributeSet attrs) { super(context, attrs); } public NewView(MainActivity mainActivity) { super(mainActivity); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { this.width = w / 9f; this.height = h / 10f; super.onSizeChanged(w, h, oldw, oldh); } // 当Android系统需要绘制1个View对象时,就会调用该对象的onDraw @Override protected void onDraw(Canvas canvas) { System.out.println("AAA onDraw -----------------"); // 画背景 Paint backgroundPaint = new Paint(); backgroundPaint.setColor(getResources().getColor(R.color.colorBackground)); canvas.drawRect(0,0,getWidth(),getHeight(),backgroundPaint); Paint darkPaint = new Paint(); darkPaint.setColor(getResources().getColor(R.color.colorDark)); Paint hilitePaint = new Paint(); hilitePaint.setColor(getResources().getColor(R.color.colorHilite)); Paint lightPaint = new Paint(); lightPaint.setColor(getResources().getColor(R.color.colorLight)); // 绘制9X9的网络格 // 两条距离为1的直线,视觉上会有割裂的效果 for (int i = 0; i < 9; i++) { 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); } // 绘制3X3的网络格 for (int i = 0; i < 10; 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); canvas.drawLine(i*width, 1, i*width, 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); numberPaint.setAntiAlias(true); // 抗锯齿 /** * 数字居中位置 * x轴居中比较容易计算 * y轴居中的计算,依赖于FontMetrics,大家很容易百度到相干的知识 */ Paint.FontMetrics fm = numberPaint.getFontMetrics(); float x = width / 2; float y = height / 2 - (fm.ascent + fm.descent) / 2; // canvas.drawText("1", 0 * width + x, 0 * height + y, numberPaint); // canvas.drawText("2", 1 * width + x, 1 * height + y, numberPaint); // canvas.drawText("3", 2 * width + x, 2 * height + y, numberPaint); // canvas.drawText("4", 3 * width + x, 3 * height + y, numberPaint); // canvas.drawText("5", 4 * width + x, 4 * height + y, numberPaint); // /** // * 根据Number类中的数组,绘制数字 // */ // for (int i = 0; i < 9; i++) { // for (int j = 0; j < 9; j++) { // canvas.drawText(number.getTileString(i,j), i*width+x, j*height+y, numberPaint); // mainActivity.playSound(); // handler.sendEmptyMessageDelayed(0, 200); // 每次重绘后,等待200毫秒,给handler发消息 // } // } /** * 绘制1个刷新文字 */ canvas.drawText("刷新",4*width+x,9*height+y,numberPaint); /** * 绘制关卡文字 */ Paint strPaint = new Paint(); strPaint.setTextSize(height*0.5f); strPaint.setAntiAlias(true); // 抗锯齿 canvas.drawText("第" + checkPoint + "关",6*width+x,9*height+y,strPaint); //super.onDraw(canvas); // canvas.drawText(number.getTileString(numberX,numberY), numberX*width+x, numberY*height+y, numberPaint); // System.out.println("numberY = " + numberY); // if (numberY < 9){ // if (number.getTileString(numberX,numberY) != ""){ // mainActivity.playSound(); // handler.sendEmptyMessageDelayed(0, 200); // 每次重绘后,等待200毫秒,给handler发消息 // }else { // handler.sendEmptyMessageDelayed(0, 0); // 每次重绘后,等待500毫秒,给handler发消息 // } // } /** * 优先绘制上1次绘制过的数字 */ int numberIndexOld = 0; int ii = 0; int jj = 0; if (numberIndex != 0){ // 首次绘制,没有上1次绘制过的数字,所以不进入这个方法 System.out.println("AAA 优先绘制上1次绘制过的数字 numberIndex = " + numberIndex); outterLoop1: for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { canvas.drawText(number.getTileString(i,j), i*width+x, j*height+y, numberPaint); System.out.println("AAA 优先绘制上1次绘制过的数字 number = " + number.getTileString(i,j) + " | i = " + i + " | j = " + j + " | numberIndexOld = " + numberIndexOld); if (numberIndexOld == (numberIndex⑴)){ ii = i; jj = j; break outterLoop1; } numberIndexOld = numberIndexOld + 1 ; } } jj = jj + 1; if (jj == 9){ jj = 0; ii = ii + 1; } } int getOld = 0; /** * 根据Number类中的数组,绘制数字 */ System.out.println("AAA0 绘制数字 jj = " + jj + " | ii = " + ii); outterLoop2: for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { if (getOld == 0){ // 继续上1次绘制的数字的i和j,继续绘制 i = ii; j = jj; getOld = 1; } canvas.drawText(number.getTileString(i,j), i*width+x, j*height+y, numberPaint); numberIndex = numberIndex + 1 ; System.out.println("AAA0 绘制数字 number = " + number.getTileString(i,j) + " | i = " + i + " | j = " + j); // 绘制进程中 遇到 不为空的数字 , 播放音效 并 延迟300毫秒 System.out.println(); if (number.getTileString(i,j) != "" && numberIndex<81){ mainActivity.playSound(); // 调用MainActivity里的方法,播放音效 handler.sendEmptyMessageDelayed(0, 1); // 等待300毫秒,给handler发消息 break outterLoop2; } } } } /** * 获得按下的数字 */ @Override public boolean onTouchEvent(MotionEvent event) { super.onTouchEvent(event); if (event.getAction() == MotionEvent.ACTION_DOWN){ selectedX = (int)(event.getX() / width); selectedY = (int)(event.getY() / height); StringBuffer sb = new StringBuffer(); if (selectedY < 9){ int used[] = number.getUsedTilesByCoor(selectedX,selectedY); for (int i = 0; i < used.length; i++) { sb.append(used[i]); } //LayoutInflater layoutInflater = LayoutInflater.from(this.getContext()); //View layoutView = layoutInflater.inflate(R.layout.dialog,null); //TextView textView = (TextView) layoutView.findViewById(R.id.usedTextId); //textView.setText("该位置不可用数字 = " + sb.toString()); //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(); } //Toast.makeText(getContext(), "ACTION_DOWN = " + used, Toast.LENGTH_SHORT).show(); if (selectedY == 9){ if (selectedX>=3 && selectedX<=5){ //Toast.makeText(getContext(), "ACTION_DOWN = 刷新",Toast.LENGTH_SHORT).show(); if (checkPoint == 1){ checkPoint = 2; } else if (checkPoint == 2){ checkPoint = 1; } //Toast.makeText(getContext(), "checkPoint = " + checkPoint, Toast.LENGTH_SHORT).show(); number = new Number(checkPoint); numberIndex = 0; invalidate(); // onDraw() } } } return true; } private int numberIndex = 0; // 绘制完成的数字,下标81 private Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); invalidate(); // onDraw(); } }; // KeyDialog当按下1个数字,隐藏KeyDialog后,会调用下面这个方法,K4 public void setSelectedTile(int tile) { System.out.println("BBB = selectedX = " + selectedX + " | selectedY = " + selectedY + " | tile = " + tile); if (number.setTileIfValid(selectedX, selectedY, tile)) { // 设置按下的数字到数组中,并判断是不是为真,K5 invalidate(); // onDraw(),K6 } } }Number.java:
package com.iwanghang.newview; public class Number { // 定义81个数字 // str1、str2 各代表1个关卡 private final String str1 = "600040800084005032000090700563180400009027000708604391" + "392476518150900200047512063"; private final String str2 = "102004007000080000006000000050000380000000000000902000" + "000000070000000102089030000"; // 实例化1个下标81的数组 private int number[] = new int[9*9]; // 存储不可用数据 private int used[][][] = new int[9][9][]; public Number(){ number = fromPuzzleString(str1); } public Number(int str){ if (str == 1){ number = fromPuzzleString(str1); }else if (str == 2){ number = fromPuzzleString(str2); } calculateAllUsedTiles(); // 获得当前关卡不可用数字数组 } // 把81个数字设置到数组中,为0的设置为空 private int[] fromPuzzleString(String str) { int[] number = new int[str.length()]; for (int i = 0; i < number.length; i++) { number[i] = str.charAt(i) - '0'; } return number; } // 根据数字在第几行第几个,获得其在数组中的下表 private int getTile(int x, int y){ //return number[y * 9 + x]; if (y * 9 + x < 81){ return number[y * 9 + x]; }else { return 0; } } // 获得数字的字符串类型 public String getTileString(int x, int y){ int v = getTile(x,y); if (v == 0){ return ""; }else return String.valueOf(v); } public int[] getUsedTilesByCoor(int x,int y){ return used[x][y]; } public void calculateAllUsedTiles() { for (int x = 0; x < 9; x++) { for (int y = 0; y < 9; y++) { used[x][y] = calculateUsedTiles(x, y); } } } /** * 获得当前关卡不可用数字数组 */ public int[] calculateUsedTiles(int x, int y) { int c[] = new int[9]; 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; } } // compress 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; } protected boolean setTileIfValid(int x, int y, int value) { System.out.println("Number BBB = x = " + x + " | y = " + y + " | value = " + value); int tiles[] = getUsedTiles(x, y); if (value != 0) { for (int tile : tiles) { if (tile == value) return false; } } setTile(x, y, value); calculateAllUsedTiles(); return true; } protected int[] getUsedTiles(int x, int y) { return used[x][y]; } private void setTile(int x, int y, int value) { number[y * 9 + x] = value; } }MainActivity.java:
package com.iwanghang.newview; import android.media.AudioManager; import android.media.SoundPool; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; public class MainActivity extends AppCompatActivity { private SoundPool soundPool; private static int soundId; int resId = R.raw.doo; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getSupportActionBar().hide(); //setContentView(R.layout.activity_main); NewView newView = new NewView(this); setContentView(newView); initSoundPool(resId); } public void initSoundPool(int resId) { // 初始化声音池 soundPool = new SoundPool( 3, // maxStreams参数,该参数为设置同时能够播放多少音效 AudioManager.STREAM_MUSIC, // streamType参数,该参数设置音频类型,在游戏中通常设置为:STREAM_MUSIC 1 // srcQuality参数,该参数设置音频文件的质量,目前还没有效果,设置为0为默许值。 ); soundId = soundPool.load(this, resId, 1); } public void playSound() { // 播放声音,参数sound是播放音效的id,参数number是播放音效的次数 soundPool.play( soundId, // 播放的音乐id 1, // 左声道音量 1, // 右声道音量 1, // 优先级,0为最低 0, // 循环次数,0无不循环,⑴无永久循环 1 // 回放速度 ,该值在0.5⑵.0之间,1为正常速度 ); } public void stopSound() { // 播放声音,参数sound是播放音效的id,参数number是播放音效的次数 if (soundPool != null && soundId > 0) { soundPool.stop(soundId); soundPool.release(); } } }keypad.xml:
<?xml version="1.0" encoding="utf⑻"?> <!-- ! Excerpted from "Hello, Android!", ! published by The Pragmatic Bookshelf. ! Copyrights apply to this code. It may not be used to create training material, ! courses, books, articles, and the like. Contact us if you are in doubt. ! We make no guarantees that this code is fit for any purpose. ! Visit http://www.pragmaticprogrammer.com/titles/eband for more book information. --> <TableLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/keypad" android:orientation="vertical" android:layout_width="wrap_content" android:layout_height="wrap_content" android:stretchColumns="*"> <TableRow> <Button android:id="@+id/keypad_1" android:text="1"> </Button> <Button android:id="@+id/keypad_2" android:text="2"> </Button> <Button android:id="@+id/keypad_3" android:text="3"> </Button> </TableRow> <TableRow> <Button android:id="@+id/keypad_4" android:text="4"> </Button> <Button android:id="@+id/keypad_5" android:text="5"> </Button> <Button android:id="@+id/keypad_6" android:text="6"> </Button> </TableRow> <TableRow> <Button android:id="@+id/keypad_7" android:text="7"> </Button> <Button android:id="@+id/keypad_8" android:text="8"> </Button> <Button android:id="@+id/keypad_9" android:text="9"> </Button> </TableRow> </TableLayout>