Android自定义View实现数独游戏

先说一下数独游戏的规则:

1.在整个横坐标和纵坐标的9个格子上只能填土1-9的数字且不重复
2.在当前3*3 的格子上填入1-9数字且不重复

先给大家看效果图

项目思路

1、UI呈现:这个放在 GameView 类里面
        显示原始数据
        显示当然用户填写的数据
        显示用户当前点击的位置
        显示候选区数据

2、逻辑处理:这个是放在Matrix类里面的
    原始数据:游戏开始的时候就要创建出来的,
    当前数据:用户填写上去的实时数据
    数据判断:判断这个位置可以修改数据吗? 比如,原始数据就是不可以修改的
            判断这个位置可以填入的数据,比如,原始数据这个位置有8了,就不能填8了。

代码 GameView 类

public class GameView extends View {

 private int PhoneWidth; // 手机屏幕的宽度
 private int mGridWidth; // 当前格子的宽度

 private int[] mFalseNumber; // 候选区不能填写的数字

 private Paint mLinePaint; // 白线
 private Paint mDarkPaint; // 浅蓝色的 方格子
 private Paint mOptDarkPaint; // 用户点击 浅绿色的格子
 private Paint numberPaint; // 原始数据 数字
 private Paint changePaint; // 用户填写的数字
 private Paint mOptPaint; // 候选区数字

 private Matrix M; // 用户计算类

 private float tCX;
 private float tCY;
 private int mOptBoard;
 private int mOptNumber;

 public GameView(Context context, AttributeSet attrs, int defStyleAttr) {
 super(context, attrs, defStyleAttr);
 initView();
 }

 public GameView(Context context, AttributeSet attrs) {
 super(context, attrs);
 initView();
 }

 public GameView(Context context) {
 super(context);

 initView();
 }

 private void initView() {

 PhoneWidth = getResources().getDisplayMetrics().widthPixels;
 mGridWidth = (PhoneWidth - 40) / 9;

 tCX = mGridWidth / 2;
 tCY = tCX - tCX / 2;

 mFalseNumber = new int[9];

 for (int i = 0; i < 9; i++) {
  mFalseNumber[i] = i;
 }

 M = new Matrix();
 initPaint();

 invalidate();
 }

 private void initPaint() {
 mLinePaint = new Paint();
 mLinePaint.setColor(Color.WHITE);
 mLinePaint.setStyle(Paint.Style.STROKE);
 mLinePaint.setStrokeWidth(2f);

 mDarkPaint = new Paint();
 mDarkPaint.setColor(Color.parseColor("#52E7CD"));
 mDarkPaint.setStyle(Paint.Style.FILL);

 numberPaint = new Paint();
 numberPaint.setColor(Color.WHITE);
 numberPaint.setTextSize(mGridWidth * 0.65f);
 numberPaint.setTextAlign(Paint.Align.CENTER);
 numberPaint.setShadowLayer(10F, -5F, 8F, Color.parseColor("#999999"));
 numberPaint.setAntiAlias(true);

 mOptPaint = new Paint();
 mOptPaint.setColor(Color.WHITE);
 mOptPaint.setTextSize(mGridWidth * 0.65f+15);
 mOptPaint.setTextAlign(Paint.Align.CENTER);
 mOptPaint.setShadowLayer(10F, -5F, 8F, Color.parseColor("#999999"));
 mOptPaint.setAntiAlias(true);

 changePaint = new Paint();
 changePaint.setColor(Color.parseColor("#FCA454"));
 changePaint.setTextSize(mGridWidth * 0.65f);
 changePaint.setTextAlign(Paint.Align.CENTER);
 changePaint.setShadowLayer(10F, -5F, 8F, Color.parseColor("#999999"));
 changePaint.setAntiAlias(true);

 mOptDarkPaint = new Paint();
 mOptDarkPaint.setColor(Color.parseColor("#52E76E"));
 mOptDarkPaint.setStyle(Paint.Style.FILL);
 }

 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 setMeasuredDimension(PhoneWidth, PhoneWidth + mGridWidth+20);
 }

 @Override
 protected void onDraw(Canvas canvas) {

 drawBoard(canvas);

 int x = mOptBoard / 9;
 int y = mOptBoard % 9;
 // 画出棋盘选择框
 canvas.drawRect(x * mGridWidth+22, y * mGridWidth+22, x * mGridWidth+20 + mGridWidth, y * mGridWidth+20 + mGridWidth, mOptDarkPaint);

 // 当前棋盘数据
 for (int i = 0; i < 9; i++) {
  for (int j = 0; j < 9; j++) {
  int cutData = M.getCutData(i, j);
  if (M.getOnClicked(i,j) && cutData>0) {
   canvas.drawText(Integer.toString(cutData), i * mGridWidth + tCX+20, j*mGridWidth + mGridWidth - tCY+20, changePaint);
  }
  }
 }

 // 候选区文字

 drawTrueText(canvas);

 }

 private void drawTrueText(Canvas canvas) {
 float startY = PhoneWidth + 30;

 // 画平行四边形
 canvas.drawLine(50, startY, PhoneWidth-50, startY, mLinePaint);
 canvas.drawLine(10, startY + mGridWidth - 40, PhoneWidth-10, startY + mGridWidth-40, mLinePaint);
 canvas.drawLine(50, startY, 10, startY + mGridWidth - 40, mLinePaint);
 canvas.drawLine(PhoneWidth-50, startY, PhoneWidth-10, startY + mGridWidth-40, mLinePaint);

 float y = (mGridWidth - 30)/2.0f;

 for (int i = 0; i < 9; i++) {
  if (mFalseNumber[i] == 0) {
  canvas.drawText(Integer.toString(i+1), i * mGridWidth + tCX+ 20, startY + (mGridWidth - tCY) - y, mOptPaint);
  }
 }
 }

 private void drawBoard(Canvas canvas) {

 // 画底色
 for (int i = 0; i < 9; i++) {
  for (int j = 0; j < 9; j++) {
  int x = i / 3;
  int y = j / 3;
  if ((x == 0 || x == 2) && (y == 0 || y == 2)) {
   canvas.drawRect(mGridWidth * j + 20, 20 + mGridWidth * i,
    mGridWidth * j + 20 + mGridWidth, 20 + mGridWidth
     * i + mGridWidth, mDarkPaint);
  } else if (y == 1 && x == 1) {
   canvas.drawRect(mGridWidth * j + 20, 20 + mGridWidth * i,
    mGridWidth * j + 20 + mGridWidth, 20 + mGridWidth
     * i + mGridWidth, mDarkPaint);
  }

  }
 }

 // 画白线
 for (int i = 0; i < 10; i++) {
  canvas.drawLine(20, mGridWidth * i + 1 + 20, 9 * mGridWidth + 20,
   mGridWidth * i + 1 + 20, mLinePaint);
  canvas.drawLine(mGridWidth * i + 1 + 20, 0 + 20, mGridWidth * i + 1
   + 20, 9 * mGridWidth + 20, mLinePaint);
 }

 //画初始数字
 for (int i = 0; i < 9; i++) {
  for (int j = 0; j < 9; j++) {
  if (!M.getOnClicked(i, j)) {
   canvas.drawText(M.getText(i, j), i * mGridWidth +20+tCX, mGridWidth * j + 20+mGridWidth-tCY, numberPaint);

  }
  }
 }
 }

 @Override
 public boolean onTouchEvent(MotionEvent event) {
 if (event.getAction() != MotionEvent.ACTION_DOWN) {
  return super.onTouchEvent(event);
 }

 if (event.getX()<20 || event.getY()<20 || event.getX()>PhoneWidth-20) {
  Log.e("123", "点到边了");
  return super.onTouchEvent(event);
 }

 int choX = (int) ((event.getX()-20) / mGridWidth);
 int choY = (int) ((event.getY()-20) / mGridWidth);

 Log.i("game ", "optX "+choX+" optY "+choY);

 if (event.getY() < PhoneWidth-20) { // 棋盘的点击

  if (M.getOnClicked(choX, choY)) {
  mFalseNumber = new int[9];
  int[] trueData = M.getFalseData(choY, choX);
  mOptBoard = choX * 9 + choY;
  for (int i : trueData) {
   mFalseNumber[i - 1] = 1;
  }

  }

 } else { // 候选区点击
  Log.e("game ","opt Number " + choX + 1);

  System.out.println(Arrays.toString(mFalseNumber));

  if (mFalseNumber[choX] == 0) {
  mOptNumber = choX;
  int x = mOptBoard / 9;
  int y = mOptBoard % 9;
  M.setCutData(x, y, mOptNumber+1);
  }

 }
 invalidate();

 return true;
 }

 // 再来一局
 public void play() {
 initView();
 }
 // 重头开始
 public void repeat(){
 M.initCutData();
 invalidate();
 }

代码 Matrix类

public class Matrix {

 private int [][]mData ; // 原始数据
 private int [][]mCutData; // 当前数据

 public Matrix() {

 int i = (int)(Math.random()*5);

 switch (i) {
 case 1:
  mData = GAMEDATA1;
  break;
 case 2:
  mData = GAMEDATA2;
  break;
 case 3:
  mData = GAMEDATA3;
  break;
 case 4:
  mData = GAMEDATA4;
  break;
 case 0:
  mData = GAMEDATA2;
  break;

 }
 initCutData();
 Log.e("Matrix", "random :"+i);

 }

 /** 得到当前坐标上的文字 */
 public String getText(int x, int y){

 String index = mData[x][y]+"";

 if ("0".equals(index)) {
  index = "";
 }

 return index;

 }

 /** 判断该坐标是否可以点击 */
 public boolean getOnClicked(int x, int y){

 if (mData[x][y] == 0) {
  return true;
 }
 return false;

 }

 /** 判断该坐标有哪些数不可用 */
 public int[] getFalseData(int x, int y){
 Set<Integer> set = new TreeSet<Integer>();

 // 检查X 轴有哪些不能点

 for (int i = 0; i < 9; i++) {
  int d = mData[y][i];

  if (d!=0) {
  set.add(d);
//  LogUtils.e("x: "+d);
  }
 }
 // 检查 y 轴有哪些不能点
 for (int i = 0; i < 9; i++) {
  int d = mData[i][x];
  if (d!=0) {
  set.add(d);
//  LogUtils.e("Y: "+d);
  }
 }

 // 检查 3*3 方格哪些不能点

 x = x/3*3;
 y = y/3*3;

// LogUtils.e(" x "+x+" Y "+y);

 for (int i = x; i < x+3; i++) {
  for (int j = y; j < y+3; j++) {
  int d = mData[j][i];
  if (d!=0) {
   set.add(d);
//   LogUtils.e("i "+i+"j "+j+" xy: "+d);
  }
  }
 }

 Integer[] arr2 = set.toArray(new Integer[0]);
 // 数组的包装类型不能转 只能自己转;吧Integer转为为int数组;
 int[] result = new int[arr2.length];
 for (int i = 0; i < result.length; i++) {
  result[i] = arr2[i];
 }
 System.out.println("false Number : "+Arrays.toString(result));

 return result;
 }

 /** 当前棋盘数据 */
 public void initCutData(){

 mCutData = new int[9][9];

 for (int i = 0; i < mData.length; i++) {
  for (int j = 0; j < mData[i].length; j++) {
   mCutData[i][j] = mData[i][j];
  }
 }

 for (int i = 0; i < mCutData.length; i++) {
  System.out.println(Arrays.toString(mCutData[i]));
 }

 }

 public void setCutData(int x, int y, int data){
 if (getOnClicked(x, y)) {
  mCutData[x][y] = data;
 }

 }

 public int getCutData(int x, int y){
 return mCutData[x][y];
 }
}

代码 MainActivity 类

public class MainActivity extends Activity {

 private GameView gV;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);

 gV = (GameView) findViewById(R.id.game);
 }

 public void rePay(View v){
 gV.repeat();
 }
 public void newPay(View v){
 gV.play();

 }

acitivity_main.xml 文件

<LinearLayout 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:background="#0EC5A5"
 android:orientation="vertical" >

 <TextView
 android:layout_width="match_parent"
 android:layout_height="52dp"
 android:layout_marginTop="10dp"
 android:gravity="center"
 android:text="SUDOKU"
 android:textColor="@android:color/white"
 android:textSize="30sp" />

 <com.xuan.sudokugame.GameView
 android:id="@+id/game"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content" />

 <LinearLayout
 android:layout_width="match_parent"
 android:layout_height="60dp"
 android:padding="10dp"
 android:orientation="horizontal" >

 <Button
  android:layout_width="0dp"
  android:onClick="rePay"
  android:layout_height="match_parent"
  android:layout_weight="1"
  android:layout_marginRight="10dp"
  android:text="重新开始"
  android:textColor="@android:color/white"
  android:background="@drawable/radius_border_gray"/>
 <Button
  android:onClick="newPay"
  android:layout_width="0dp"
  android:layout_height="match_parent"
  android:layout_weight="1"
  android:text="再来一局"
  android:textColor="@android:color/white"
  android:background="@drawable/radius_border_gray"/>
 </LinearLayout>

</LinearLayout>

然后运行起来就是这个样子的了。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

您可能感兴趣的文章:

  • Android游戏之数独游戏开发
  • 基于Android实现数独游戏
  • Android应用实践之数独游戏开发
  • 简单实现Android数独游戏
  • 简单实现java数独游戏
  • java版数独游戏核心算法(一)
  • java版数独游戏界面实现(二)
  • java数独游戏完整版分享
(0)

相关推荐

  • Android应用实践之数独游戏开发

    数独游戏是一种源自18世纪末的瑞士的游戏,后在美国发展.并在日本得以发扬光大的数学智力拼图游戏.拼图是九宫格(即3格宽×3格高)的正方形状,每一格又细分为一个九宫格.在每一个小九宫格中,分别填上1至9的数字,让整个大九宫格每一列.每一行的数字都不重复. 数独的玩法逻辑简单,数字排列方式千变万化.不少教育者认为数独是锻炼脑筋的好方法,上外语阅读课的时候外教老师就很喜欢带我们玩这个,乐此不疲,老外的教学方式还是很受欢迎的.但是每次玩这个游戏的时候都要发一张数独游戏卡,嫌麻烦,就想着写一个demo放自

  • java版数独游戏核心算法(一)

    之前学习javascript时用javascript写过一个数独游戏,最近看了一点java的内容,于是就心血来潮想搞一个java版的数独游戏. 现在将全部代码分享出来和大家学习交流,当然代码中有着各种各样的问题和不足之处,望各位朋友批评指点. 以下是生成数独地图的核心算法,算法不是很好,也是之前参考过网上的一些思想: package hlc.shudu.src; /* * 数独的帮助类,里面提供数据所需的所有算法 */ public class ShuduHelper { //数独地图数组 pr

  • 简单实现Android数独游戏

    本文实例为大家分享了Android数独游戏的具体代码,供大家参考,具体内容如下 实现了点击了相关的单元格之后会显示出对话框提示可选数字. 原始的自定义对话框仍旧不能满足我们的要求,原始的自定义对话框只能够生成Bulider对象  然后通过LayoutInflater获取相应的View 对象 (其实就是Layout 布局文件) 其实也是可以的,只是我们不能再次进行一些其他的操作了,比如说我们即使设置了TableLayout但是我们不能够在上面完成任何操作,因为并不允许使用 自定义方法设置相关功能,

  • java数独游戏完整版分享

    本文实例为大家分享了java数独游戏的具体代码,供大家参考,具体内容如下 自己写的数独游戏,共9关,代码如下: 1.DoShudu类用于产生数独数组 import java.util.Random; public class DoShudu { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub int[][] cells=newshudu(); //ce

  • 基于Android实现数独游戏

    本文实例为大家分享了Android实现数独游戏的具体代码,供大家参考,具体内容如下 1.在src中有4个Java类: 其中代码分别是: Game.java: package com.example.test1; import android.R.integer; public class Game { public final String str="360000000004230800000004200" +"070460003820000014500013020"

  • 简单实现java数独游戏

    本文实例为大家分享了java数独游戏的具体代码,供大家参考,具体内容如下 打算把javaFx需要的组件装好以后直接用javaFx的,但似乎eclipse的版本不对,安装了也不能用... 数独代码是在之前寒假受命写的,学了一个月java的成果,现在看来有些不足但毕竟是第一个程序,就直接放上来,数独终盘的实现直接用了暴力,时间复杂度有点高,懒得改了直接放代码 终盘实现: import java.util.Random; public class SudokuPuzzleGenerator { pri

  • Android游戏之数独游戏开发

    数独游戏是一种源自18世纪末的瑞士的游戏,后在美国发展.并在日本得以发扬光大的数学智力拼图游戏.在每一个小九宫格中,分别填上1至9的数字,让整个大九宫格每一列.每一行的数字都不重复. 数独的玩法逻辑简单,数字排列方式千变万化,是锻炼脑筋的好方法. 本文实现简单的数独游戏,通过mars的视频教程完成编程 1.自定义View: package com.example.administrator.shudugame; /** * Created by Administrator on 2016/9/1

  • java版数独游戏界面实现(二)

    本文实例为大家分享了java版数独游戏界面实现的具体代码,供大家参考,具体内容如下 实现效果图: 这里写图片描述 主函数用于启动程序: package hlc.shudu.app; import hlc.shudu.src.ShuduHelper; import hlc.shudu.ui.ShuduMainFrame; public class AppStart { public static void main(String[] args) { ShuduMainFrame mainFrame

  • Android自定义View实现数独游戏

    先说一下数独游戏的规则: 1.在整个横坐标和纵坐标的9个格子上只能填土1-9的数字且不重复 2.在当前3*3 的格子上填入1-9数字且不重复 先给大家看效果图 项目思路 1.UI呈现:这个放在 GameView 类里面         显示原始数据         显示当然用户填写的数据         显示用户当前点击的位置         显示候选区数据 2.逻辑处理:这个是放在Matrix类里面的     原始数据:游戏开始的时候就要创建出来的,     当前数据:用户填写上去的实时数据

  • Android自定义View实现五子棋游戏

    本文实例为大家分享了Android五子棋游戏的具体代码,供大家参考,具体内容如下 1.效果图: 2.GobangPanel棋盘面板: public class GobangPanel extends View { private int mPanelWidth;//棋盘的宽度 private float mLineHeight;//行,高要为float private int MAX_LINE = 15;//棋盘行数 private int MAX_COUNT_IN_LINE = 5;//设置赢

  • Android自定义View实现游戏摇杆键盘的方法示例

    前言 本文主要给大家介绍的是关于Android自定义View实现游戏摇杆键盘的相关内容,为什么会有这篇文章呢?因为在之前的一个项目,操作方向的方式为上下左右,左上需要同时按住左键和右键的方式进行操作. 如下图: 近来需要升级项目,操作方式改为类似王者荣耀的摇杆操作. 如下图: 好了,下面话不多说了,跟着小编来一起看看是如何实现的吧. 绘制背景 实现遥感按钮,需要绘制背景,绘制中心的遥感按钮.绘制遥感背景,需要创建一个RemoteViewBg类,存储背景图,减少重复创建bitmap. Remote

  • Android自定义view实现阻尼效果的加载动画

    效果: 需要知识: 1. 二次贝塞尔曲线 2. 动画知识 3. 基础自定义view知识 先来解释下什么叫阻尼运动 阻尼振动是指,由于振动系统受到摩擦和介质阻力或其他能耗而使振幅随时间逐渐衰减的振动,又称减幅振动.衰减振动.[1] 不论是弹簧振子还是单摆由于外界的摩擦和介质阻力总是存在,在振动过程中要不断克服外界阻力做功,消耗能量,振幅就会逐渐减小,经过一段时间,振动就会完全停下来.这种振幅随时间减小的振动称为阻尼振动.因为振幅与振动的能量有关,阻尼振动也就是能量不断减少的振动.阻尼振动是非简谐运

  • Android 自定义View 密码框实例代码

    暴露您view中所有影响可见外观的属性或者行为. •通过XML添加和设置样式 •通过元素的属性来控制其外观和行为,支持和重要事件交流的事件监听器 详细步骤见:Android 自定义View步骤 效果图展示: 支持的样式 可以通过XML定义影响外边和行为的属性如下 边框圆角值,边框颜色,分割线颜色,边框宽度,密码长度,密码大小,密码颜色 <declare-styleable name="PasswordInputView"> <attr name="borde

  • Android自定义View详解

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/24252901 很多的Android入门程序猿来说对于Android自定义View,可能都是比较恐惧的,但是这又是高手进阶的必经之路,所有准备在自定义View上面花一些功夫,多写一些文章.先总结下自定义View的步骤: 1.自定义View的属性 2.在View的构造方法中获得我们自定义的属性 [ 3.重写onMesure ] 4.重写onDraw 我把3用[]标出了,所以说3不一

  • Android自定义View中attrs.xml的实例详解

    Android自定义View中attrs.xml的实例详解 我们在自定义View的时候通常需要先完成attrs.xml文件 在values中定义一个attrs.xml 然后添加相关属性 这一篇先详细介绍一下attrs.xml的属性. <?xml version="1.0" encoding="utf-8"?> <resources> //自定义属性名,定义公共属性 <attr name="titleText" for

  • Android自定义View 仿QQ侧滑菜单的实现代码

    先看看QQ的侧滑效果 分析一下 先上原理图(不知道能否表达的清楚 ==) -首先这里使用了 Android 的HorizontalScrollView 水平滑动布局作为容器,当然我们需要继承它自定义一个侧滑视图 - 这个容器里面有一个父布局(一般用LinerLayout,本demo用的是),这个父布局里面有且只有两个子控件(布局),初始状态菜单页的位置在Y轴上存在偏移这样可以就可以形成主页叠在菜单页的上方的视觉效果:然后在滑动的过程程中 逐渐修正偏移,最后菜单页和主页并排排列.原理搞清了实现起来

  • Android自定义View绘制随机生成图片验证码

    本篇文章讲的是Android自定义View之随机生成图片验证码,开发中我们会经常需要随机生成图片验证码,但是这个是其次,主要还是想总结一些自定义View的开发过程以及一些需要注意的地方. 按照惯例先看看效果图: 一.先总结下自定义View的步骤: 1.自定义View的属性 2.在View的构造方法中获得我们自定义的属性 3.重写onMesure 4.重写onDraw 其中onMesure方法不一定要重写,但大部分情况下还是需要重写的 二.View 的几个构造函数 1.public CustomV

  • Android自定义View绘制的方法及过程(二)

    上一篇<Android 自定义View(一) Paint.Rect.Canvas介绍>讲了最基础的如何自定义一个View,以及View用到的一些工具类.下面讲下View绘制的方法及过程 public class MyView extends View { private String TAG = "--------MyView"; private int width, height; public MyView(Context context, AttributeSet a

随机推荐