Android自定义View的数独游戏

2023-11-12

Android自定义View的数独游戏

先说一下数独游戏的规则
在整个横坐标和纵坐标的9个格子上只能填土1-9的数字且不重复
在当前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>
然后运行起来就是这个样子的了

这里写图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android自定义View的数独游戏 的相关文章

随机推荐

  • K8S安装部署Nacos集群

    1 在k8s中部署的难点 在k8s中部署nacos集群和在裸机器上直接部署nacos机器其实差别不大 最主要的区别是k8s中部署的服务没有固定的ip地址 而nacos集群部署需要配置所有实例的ip 2 解决 在k8s中通过StatefulS
  • day06 Spring AOP

    DI注解 作用 解决使用xml配置繁琐的问题 该注解和使用配置文件一样分成两类进行注入 字段注入或属性注入 注入bean 取代xml中的ref 1 value注解 使用value注解给属性进行赋值 只能使用于八大基本类型和常量类型 Stri
  • Woedpress分类目录绑定二级域名

    实现分类目录和二级域名绑定需要使用 WordPress 的多站点功能 Multisite 以下是一个基本的步骤和示例代码来实现这个功能 代码实现方法 步骤 将 WordPress 安装为多站点模式 在 WordPress 安装目录下的 wp
  • verdi使用linux命令,vcs和verdi的调试及联合仿真案例

    环境配置 首先搭建好vcs和Verdi都能工作的环境 主要有license问题 环境变量的设置 在220实验室的服务器上所有软件的运行环境都是csh 所以 所写的脚本也都是csh的语法 生成波形文件 Testbench的编写 若想用Verd
  • 机器学习实战8-基于XGBoost和LSTM的台风强度预测模型训练与应用

    大家好 我是微学AI 今天给大家介绍一下机器学习实战8 基于XGBoost和LSTM的台风强度预测模型训练与应用 今年夏天已经来了 南方的夏天经常会有台风登陆 给人们生活带来巨大的影响 本文主要基于XGBoost模型和长短期记忆 LSTM
  • 淘宝滑动验证码研究

    引言 悠闲的时候 总会去找些事做做 前些天在登录淘宝的时候 发现了滑动验证码 虽然已经不是什么新事物 但还是产生了很大的兴趣 传统的字符输入验证码 变为了滑动验证码 这一看就是产品大师的手笔啊 不知道申请专利没有 这种 情感化 的验证码设计
  • C语言深入学习--checklist7:链接、运行时数据结构、申明

    1 你知道段的概念吗 段是二进制文件中的简单区域 里面保存了某种特定的类型 如符号表条目 相关的所有信息 1 可执行程序分为几个段 每个段保存什么内容 可执行程序分为三个段 BSS段 数据段 文本段 BSS段 Block Started b
  • 矩阵迹运算介绍及C++/OpenCV/Eigen的三种实现

    矩阵迹运算返回的是矩阵对角元素的和 迹运算因为很多原因而有用 若不使用求和符号 有些矩阵运算很难描述 而通过矩阵乘法和迹运算符号 可以清楚地表示 例如 迹运算提供了另一种描述矩阵Frobenius范数的方式 用迹运算表示表达式 我们可以使用
  • 2021斯坦福CS224N课程笔记~2

    2 Neural Classifiers 2 1本篇内容覆盖 word2vec与词向量回顾 算法优化基础 计数与共现矩阵 GloVe模型 词向量评估 word senses 2 2 回顾 word2vec 的主要思想 2 2 1 主要步骤
  • Node Sass does not yet support your current environment: Windows 64-bit with Unsupported runtime

    报错 在进行编译的时候运行到下面的错误 Node Sass does not yet support your current environment Windows 64 bit with Unsupported runtime 88 这
  • 芯片电源引脚的电容选择

    主要内容 参考如下 DC 100K 10uF以上的钽电容或铝电解 100K 10M 100nF 0 1uF 陶瓷电容 10M 100M 10nF 0 01uF 陶瓷电容 100M以上 1nF 0 001uF 陶瓷电容和PCB的地平面与电源平
  • ggplot2技巧书《R数据可视化手册》读书笔记:第二章 快速探索数据

    2 1绘制散点图 基础绘图 plot data x data y ggplot2 qplot data x data y 提前安装 加载ggplot2 qplot x y data 等价于 ggplot data aes x y geom
  • 医学图像分割--Stacked fully convolutional networks with multi-channel learning

    Stacked fully convolutional networks with multi channel learning application to medical image segmentation https link sp
  • c++基本使用(类的静态成员)

    c 基本使用 类的静态成员 静态成员属性 静态成员方法 类的静态成员包括 静态成员变量 静态成员函数 静态成员属性 用 static 关键字把类的成员变量声明为静态 表示它在程序中 不仅是对象 是共享的 静态成员使用类名加范围解析运算符 就
  • 【Dash搭建可视化网站】项目1:使用Dash创建简单网页

    项目1 使用Dash创建简单网页 项目1 使用Dash创建简单网页 1 1 官网示例 1 2 绘制简单网页的基本步骤 1 3 创建一个稍微有意思的页面 手动反爬虫 禁止转载 原博地址 https blog csdn net lys 828
  • Ansible 企业级自动化运维平台开发实战

    一 运维开发 普通的运维方式 使用Xshell或者脚本去操作服务器 运维开发的方式 可以实现把运维的工作Web化 运维开发优点 可以把运维工作简单化 运维工作规划化 运维开发 负责具体的产品的运维工作 同时也需要进行基本的开发 了解业务的痛
  • WPF之层级数据模板HierarchicalDataTemplate的使用

    WPF之层级数据模板HierarchicalDataTemplate的使用 1 HierarchicalDataTemplate List 2 HierarchicalDataTemplate XML 3 TreeView Hierarch
  • 基于Yolov5目标检测的物体分类识别及定位(一) -- 数据集原图获取与标注

    从本篇博客正式开始深度学习项目的记录 实例代码只会放通用的代码 数据集和训练数据也是不会全部放出 系列文章 基于Yolov5目标检测的物体分类识别及定位 一 数据集原图获取与标注 基于Yolov5目标检测的物体分类识别及定位 二 yolov
  • 一道简单的PV操作题

    这是川大操作系统的一道期末考试题 There is an cage and only one animal can be put into this cage The hunters can put tiger into the cage
  • Android自定义View的数独游戏

    Android自定义View的数独游戏 先说一下数独游戏的规则 在整个横坐标和纵坐标的9个格子上只能填土1 9的数字且不重复 在当前3 3 的格子上填入1 9数字且不重复 先给大家看效果图 项目思路 1 UI呈现 这个放在 GameView