Android 简单数独开发

简介

这篇文章是我看了 Mars老师 讲的开发数独教程,得到学习笔记和感想。这里面的代码和框架我都改了一下。在这里要感谢一下互联网,和 Mars老师。

数独界面展示



源码

点击获取源码


数独分析

基础知识

手机的坐标系

如上图所示,左上角0点为手机屏幕最左上方。


Paint和Canvas

Paint 是画笔,这个画笔你可以设置为你想要的画笔,比如颜色、大小、形状。本文用到的有:

// 设置颜色
public native void setColor(@ColorInt int color);

// 设置样式
public void setStyle(Style style);

// 设置文字对齐
public void setTextAlign(Align align);

// 设置大小
public native void setTextSize(float textSize);

// 消除锯齿一样的边缘
public native void setAntiAlias(boolean aa);

Canvas 是画布,在这个画布上你可以任意画出你想要的东西。本文用到的方法有:

// 画一个矩形, (left, top) 为左上点、(right, botton) 为右下点。
public void drawRect(float left, float top, float right, float bottom, @NonNull Paint paint);

// 画一条直线, (startX, startY) 为左坐标、(stopX, stopY) 为右坐标。
public void drawLine(float startX, float startY, float stopX, float stopY, @NonNull Paint paint);

// 写字, text 为内容, (x, y) 为首字最左边的坐标,其余字按照直线依次显示。
public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint);

FontMetrics

FontMetrics

上图是字在 FontMetrics 中的各种属性值。等下我们要用这个来调节字体的位置,大家只要记住里面的几个名字就行。
Baseline 是字体的水平线。也就是横轴和手机的坐标系一样。向上为负值,向下为正值。
获取 FontMetrics 对象如下:
//其中 paint 为所用的画笔对象

Paint.FontMetrics fontMetrics = paint.getFontMetrics();


步骤解析

九宫格的生成

这个界面是自定义生成的,我们要把九宫格给画出来。
. 新建一个类继承 View 类,重写里面得 onDraw 方法。

. 先画一个外矩形,将所有的东西包住。

 Paint paint = new Paint();
 // 消除锯齿一样的边缘
 paint.setAntiAlias(true);
 // 先画一个空心的矩形 作为外背景。Paint.Style.STROKE 为设置空心
 paint.setStyle(Paint.Style.STROKE);
 // 加载颜色
 paint.setColor(ContextCompat.getColor(mContext, R.color.top2));
 // getWidth() getHeight() 得到界面的宽和高
 canvas.drawRect(0, 0, getWidth(), getHeight(), paint);

这是我的颜色表
颜色表

. 由数独的界面可知道,横轴和纵轴都有 9 根线来等间隔隔开。因此要先获得界面的宽和高,再除以 9。

@Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        // 得到 九宫格 的高度和宽度
        width = w / 9f;
        height = h / 9f;
        super.onSizeChanged(w, h, oldw, oldh);
    }

. 进行绘画,在横(X)轴上每隔 width 个大小就画一条。在纵(Y)轴上每隔 height 个大小就画一条。

在这里,进行的条纹加深就是间隔隔开一点点,然后再次画线,颜色选深色。这样看起来就有条纹。

因为九宫格为 9 x 9, 而且里面又有 9 个 3 x 3,所以每有 3 个 width 时,横轴就画一个更深颜色的线用来区分。同理纵轴也是一样。

 // 画9条横轴 纵轴 网格
for (int i = 0; i < 9; i++) {
      paint.setColor(ContextCompat.getColor(mContext, R.color.back));
      // 横轴
     canvas.drawLine(0, i * height, getWidth(), i * height, paint);
      // 纵轴
     canvas.drawLine(i * width, 0, i * width, getHeight(), paint);
      // 进行条纹加深
     paint.setColor(ContextCompat.getColor(mContext, R.color.top1));
      // 横轴
     canvas.drawLine(0, i * height + 3, getWidth(), i * height + 3, paint);
      // 纵轴
     canvas.drawLine(i * width + 3, 0, i * width + 3, getHeight(), paint);
      // 将目前的 九宫格 分为 3 块
     if (i % 3 == 0) {
           paint.setColor(ContextCompat.getColor(mContext, R.color.top2));
           // 横轴
           canvas.drawLine(0, i * height + 3, getWidth(), i * height + 3, paint);
           // 纵轴
           canvas.drawLine(i * width + 3, 0, i * width + 3, getHeight(), paint);
      }
}

到这里九宫格已生成。也就是说棋盘已经可以用了。


填入初始数字

要将数字填入九宫格中,为了美观,将设置文字所在的位置。

解析

如图,黑色为设置字的大小。黄色为九宫格中每个格的大小。水平方向上的绿色为Baseline。紫色为格子的中心。
由图知,字的宽度为 width / 2;
字的高度为 height / 2 - (fontMetrics.ascent + fontMetrics.descent) / 2;
height / 2 为水平紫线,要想达到水平的绿线上就必须高度增加。这里要增加的数值是随意。一般来说用 -(fontMetrics.ascent + fontMetrics.descent) / 2 得到的数值来进行增加(ascent 为负值,descent 为正数,括号里面得到负值,加一个负号为正数)。

 paint.setColor(Color.BLACK);
 paint.setStyle(Paint.Style.STROKE);

 // 字体设置水平居中, 也就是写入字的坐标为首字中心
 paint.setTextAlign(Paint.Align.CENTER);
 paint.setTextSize((float) (height * 0.75));
 Paint.FontMetrics fontMetrics = paint.getFontMetrics();
 float x = width / 2;
 float y = height / 2 - (fontMetrics.ascent + fontMetrics.descent) / 2;

这里要填入的数据源为:

private String STR = "360000000004230800000004200"
            + "070460003820000014500013020" + "001900000007048300000000045";

当 getValue(x, y) 返回的值不为0时,就说明当前的 (x, y)上有数字。

for (int i = 0; i < 9; i++) {
      for (int j = 0; j < 9; j++) {
            // getValue(x,y)获得对应的数字
            int point = getValue(j, i);
            if (point != 0) {
                 canvas.drawText(String.valueOf(point), j * width + x, i * height + y, paint);
            }
      }
}

得到点击的坐标

在这里重写触摸事件
event.getX() 得到当前点击到的横坐标, 除以 width 时就可以得到横轴上是第几个格子(从零开始数)。同理 event.getY() 也是一样。

  @Override
  public boolean onTouchEvent(MotionEvent event) {
      float x = 0;
      float y = 0;
      if (event.getAction() == MotionEvent.ACTION_DOWN) {
            x = (int) (event.getX() / width);
            y = (int) (event.getY() / height);
       }
      return super.onTouchEvent(event);
   }

进行可填数字判断

这里传入的 (x, y) 为点击到的点。
used数值为存储已存在的数。
进行横轴判断。y 不变,i 从0到9

// 查找横轴 (X)
for (int i = 0; i < 9; i++) {
     int point = getValue(i, y);
     if (point != 0) {
          used[point - 1] = point;
     }
}

进行纵轴判断。x 不变,i 从0到9

// 查找纵轴 (Y)
for (int i = 0; i < 9; i++) {
      int point = getValue(x, i);
      if (point != 0) {
            used[point - 1] = point;
       }
 }

进行小网格(3 x 3)判断。
3x3
int xStart = (x / 3) * 3;
int yStart = (y / 3) * 3;
xStartyStart 就是得到(x, y)所在的 3 x 3 的最初点。也就是上图中红圈所在位置。

// 查找网格
// 计算出 x y 在网格中的最初位置
 int xStart = (x / 3) * 3;
 int yStart = (y / 3) * 3;

  for (int i = xStart; i < xStart + 3; i++) {
        for (int j = yStart; j < yStart + 3; j++) {
             int point = getValue(i, j);
              if (point != 0) {
                  used[point -1] = point;
              }
          }
   }

这里 used 数组就是在(x, y)坐标中横轴、纵轴和 3 x 3 网格中不能填入的数字。


弹出可选数字的界面

这里我用的是 Alertdialog 自定义样式。

我的 XML 布局文件生成的界面如下:
XML
bt 数组为所有 Button 的集合。
更改数据 setSTR(x, y, t);
重写绘制 reDraw();


// 出现对话框,提供用户可添选数据
final AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
final AlertDialog dialog = builder.create();
View item = LayoutInflater.from(mContext).inflate(R.layout.item_choose_layout, null);
// 找到所有的 Button 
findAllBt(item);

// 将对应的 x y 上已经出现的数字进行屏蔽
for (int i = 0; i < 9; i++) {
      if (used[i] != 0) {
      bt[i].setVisibility(View.INVISIBLE);
       }
}
// 设置点击事件
for (int i = 0; i < 9; i++) {
// 得到 ascii 值
final char t = (char) (i + 1 + '0');
bt[i].setOnClickListener(new OnClickListener() {
     @Override
     public void onClick(View v) {
     // 更改数据
     setSTR(x, y, t);
     // 重写绘制
     reDraw();
     dialog.dismiss();
     }
});
}

dialog.setView(item);
dialog.setTitle("请选择数字");
dialog.setCancelable(true);
dialog.show();

更改数据

获得 (x, y) 和要修改的数据。

 // 进行修改
  public void setSTR(int x, int y, char c) {
  // 得到 x y 的长度
     int index = y * 9 + x;
     String reSTR = STR.substring(0, index);
     reSTR += c;
     reSTR += STR.substring(index + 1, STR.length());
     STR = reSTR;
    }

重写绘制

这里就调用了 getUsedArray() 进行不能填入的数字刷新。
invalidate() 就是请求重新draw()。

// 进行重新绘图
 public void reDraw() {
 // 进行数据刷新
 getUsedArray();
 invalidate();
 } 

到这里,简单的数独游戏就完成了!


感想

其实一开始,我不打算写这个数独的。因为我感觉逻辑很简单,剩下的就是代码编写。但不知道是什么原因我开始写了,估计是好奇!
不过,有些东西看起来简单,真正写起来。还是有点问题,不过还好。我还是把这个数独给写了。
通过这次数独的编写,我觉得我还是应该要脚踏实地,每一行代码都应该去实现一下。这次的数独就是一个警告,看起来是一回事,但是敲起来又是另外一件事了。既然选择了代码,就应该要下手敲!


猜你喜欢

转载自blog.csdn.net/zwt520123/article/details/53047008
今日推荐