文章目录
- 一、项目概述
- 二、开发环境
- 三、详细设计
- 3.1 界面设计
- 3.2 逻辑设计
- 四、运行演示
一、项目概述
数独是一种逻辑解谜游戏,它规则稍复杂,解题过程富有挑战性。本次安卓数独小游戏,主页面有继续游戏、新游戏、关于和退出四个功能,下拉菜单有设置选项。新游戏可以选择简单、中等和困难3个模式,进入游戏后可以随时退出,然后点继续游戏就能回到上一次的游戏状态。关于是对数独游戏的规则介绍,在设置中可以开启或者关闭背景音乐,开启或关闭游戏提示。游戏界面,每个3x3的方块区域都被实线给分隔开,方便玩家观察填入数字。
涉及知识点:AlertDialog、KeyEvent、MediaPlayer、Canvas、Paint、Parcelable、Fragment、Menu、PreferenceManager、Bundle
二、开发环境
开发环境依旧是3.6.1,属于2020年的版本,基本上大家都满足版本要求。
三、详细设计
3.1 界面设计
1、关于界面,非常简单,描述游戏规则只需要TextView即可。
2、键盘界面,一共3行,每行3列。用到3个TableRow控件,每个TableRow包含3个Button,在输入数字时候会作为对话框弹出。核心代码是button的style。
// 设置主界面的button的样式
<style name="number_button">
<item name="android:layout_width">0dp</item>
<item name="android:layout_weight">1.0</item>
<item name="android:layout_height">40dp</item>
<item name="android:layout_margin">3dp</item>
<item name="android:textSize">30sp</item>
<item name="android:background">@drawable/select_number_click</item>
<item name="android:textColor">#fff</item>
</style>
3、主界面,其实设计也不难,主要是四个垂直排列的Button,主要内容还是在background中,solid的颜色为渐变色,stroke的宽度为1dp,color是灰色,radius是50dp,弧形半径非常明显,padding为2dp,有内边距视觉效果更好,最后根据state_pressed变色。
3.2 逻辑设计
1、背景音乐,直接定义一个类,定义play方法,创建MediaPlayer对象,播放传参resource,setLooping设为true就是循环播放。代码如下:
public class Music {
private static MediaPlayer mp = null;
// stop old song and start a new song
public static void play(Context context, int resource){
stop(context);
if(Settings.getMusic(context)){
mp = MediaPlayer.create(context, resource);
mp.setLooping(true);
mp.start();
}
}
// stop the music
public static void stop(Context context) {
if(mp != null){
mp.stop();
mp.release();
mp = null;
}
}
}
在应用启动时开始播放音乐,退出时停止播放。
@Override
protected void onResume() {
super.onResume();
// 程序开始时,调用music activity开始播放音乐
Music.play(this, R.raw.nothing_to_lose);
}
@Override
protected void onPause() {
super.onPause();
// 程序暂停或退出时,停止播放音乐
Music.stop(this);
}
2、小键盘,绑定每个数字按钮,设置监听器,在显示键盘前会进行逻辑判断,只显示可以填入该方块内的数字,其余数字不显示。
// 为小键盘设置监听函数
private void setListeners(){
for(int i = 0; i < keys.length; i++){
final int t = i + 1;
keys[i].setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View arg0) {
returnResult(t);
}
});
}
keypad.setOnClickListener(new View.OnClickListener() {
public void onClick(View arg0) {
returnResult(0);
}
});
}
// 返回结果函数
private void returnResult(int tile) {
// 绘制用户选择的数字
puzzleView.setSelectedTile(tile);
dismiss();
}
在PuzzleView类中,有显示软键盘和手机屏幕点击事件的处理方法,该方法处理三种情况的事件:屏幕被按下时,屏幕被抬起时,屏幕中拖动,根据触摸位置的坐标选中对应区域的按钮。
@Override
public boolean onTouchEvent(MotionEvent event) {
// 参数event为手机屏幕触摸事件封装类的对象,其中封装了该事件的所有信息,
// 例如触摸的位置、触摸的类型以及触摸的时间等。该对象会在用户触摸手机屏幕时被创建。
// 该方法处理三种情况的事件:屏幕被按下时,屏幕被抬起时,屏幕中拖动
if(event.getAction() != MotionEvent.ACTION_DOWN)//如果屏幕没有被按下
return super.onTouchEvent(event);
select((int)(event.getX()/width),(int)(event.getY()/height));
game.showKeypadOrError(selX, selY);
Log.d(TAG, "onTouchEvent:x"+selX+",y"+selY);
return true;
}
3、PuzzleView是用来绘制游戏界面,当前活动主窗口大小改变时调用onSizeChanged,获取width和height,然后分成一共9x9个小方块。
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
// 计算单元格的宽和高(横向是宽度,纵向是高度)
width = w / 9f;
height = h / 9f;
getRect(selX, selY, selRect);
Log.d(TAG, "onSizeChanged:width" + width + ",height" + height);
super.onSizeChanged(w, h, oldw, oldh);
}
创建画笔对象,绘制数字,设置字体颜色,设置字体大小和对齐方式,定义了字体规格对象,该对象封装了有关在特定屏幕上呈现特定字体的信息。再创建选择的对象的画笔,按下变色的颜色,根据每个单元格可填的数目给出不同颜色的提示。
// draw numbers 绘制数字
Paint foreground = new Paint(Paint.ANTI_ALIAS_FLAG);// 生成画数字的画笔
foreground.setColor(getResources().getColor(R.color.puzzle_foreground));// 设置字体颜色
foreground.setStyle(Style.FILL);
foreground.setTextSize(height * 0.75f);
foreground.setTextScaleX(width / height);
foreground.setTextAlign(Paint.Align.CENTER);// 设置数字在框内的布局 居中对齐
// draw num in the center of the tile
FontMetrics fm = foreground.getFontMetrics();// 定义了字体规格对象,该对象封装了有关在特定屏幕上呈现特定字体的信息。
float x = width / 2;
float y = height / 2 - (fm.ascent + fm.descent) / 2;// 横竖都居中
// Ascent: 字符顶部到baseLine的距离。
// Descent: 字符底部到baseLine的距离。
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
canvas.drawText(this.game.getTitleString(i, j), i * width + x,
j * height + y, foreground);// 画数字
}
}
// draw the selection
Log.e(TAG, "selRect=" + selRect);
Paint selected = new Paint();
// 生成选择的对象的画笔 按下变色的颜色
selected.setColor(getResources().getColor(R.color.puzzle_selected));
canvas.drawRect(selRect, selected);
// draw the hints pick a hint color based on moves left
// 根据每个单元格可填的数目给出不同颜色的提示
if(Settings.getHints(getContext())){
Paint hint=new Paint();
int c[] = {
getResources().getColor(R.color.puzzle_hint_0),// 表示目前填入不合适 需要从新填入
getResources().getColor(R.color.puzzle_hint_1),// 表示只有一个数字可以填入
getResources().getColor(R.color.puzzle_hint_2),// 不同颜色的提醒 表示有两个数字可以填入
};
Rect r = new Rect();
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
int movesleft = 9 - game.getUsedTiles(i, j).length;
if(movesleft < c.length){
getRect(i, j, r);
hint.setColor(c[movesleft]);
canvas.drawRect(r, hint);
}
}
}
}
4、Game是游戏类,主要实现难度的选择,数字的绘制规则,一共三种游戏模式,每一种都是定义好的字符串,0表示当前没有填充数字,0之外表示有,然后遍历字符串,在PuzzleView中调用画笔绘制9x9的网格,并在每个方块中绘制数字,这样数独的界面就绘制好了。然后选中某一个方块,就会调用小键盘进行输入。
// 获取难度 difficulty easy = 0 默认为简单
int diff = getIntent().getIntExtra(KEY_DIFFICULTY, DIFFICULTY_EASY);
puzzle = getPuzzle(diff);//获取当前数据
calculateUsedTiles();//获取当前关的不可用数字数组
puzzleView = new PuzzleView(this);//获取画图的对象
setContentView(puzzleView);//设置内容画图
puzzleView.requestFocus();//画的图,获取焦点
// if the activity is restarted ,do a continue next time
getIntent().putExtra(KEY_DIFFICULTY, DIFFICULTY_CONTINUE);
5、游戏状态保存依靠Bundle,实例状态存储在bundle中,保存当前游戏状态,parcelable是序列化的接口,方便处理窗口保存事件。
@Override
protected Parcelable onSaveInstanceState() {//
//用户打包需要传输的数据,然后在Binder中传输,用于跨进程传输数据
Parcelable p = super.onSaveInstanceState();// 打包状态
Log.d(TAG, "onSavedInstanceState");
Bundle bundle = new Bundle();
bundle.putInt(SELX, selX);
bundle.putInt(SELY, selY);// 保存当前的坐标
bundle.putParcelable(VIEW_STATE, p);// 保存当前的状态,用bundle传输
return bundle;
}
恢复已经保存的信息,只需要从Bundle里面取出来保存的坐标即可。
@Override
protected void onRestoreInstanceState(Parcelable state) {// 处理窗口还原事件
Log.d(TAG, "onRestoreInstanceState");
Bundle bundle = (Bundle) state;
select(bundle.getInt(SELX), bundle.getInt(SELY));// 通过bundle对象获取 保存的坐标
super.onRestoreInstanceState(bundle.getParcelable(VIEW_STATE));// 通过bundle对象获取存放的状态信息
return;
}
四、运行演示
1、启动应用,进入主界面,有继续、新游戏、关于和退出四大选项,标题栏显示“数独游戏”,右侧是菜单项。
2、点击新游戏,选择游戏难度,分为简单、中等和困难。
3、进入游戏后,可以看到有的小方块已经填有数字,剩下的小方块就是你需要填入数字的。玩家选中的方块是浅绿色,比如左上角第一个方块。游戏提示开启时,当某一个方块能填的数字只有1~2时,方块颜色不同于默认颜色,可以看到下图左上角第一块区域数字8的左右各有1个提示方块。
4、点击任意方块,将弹出键盘,选择填入的数字,当点击下一个方块时,刷新界面。
5、菜单栏进入设置界面,有背景音乐和提示选项,背景音乐如果开启会循环播放。
6、我们关闭提示,则方块没有颜色提示,显示的颜色一致,这是困难模式,没有提示太难了。
7、关于界面介绍了游戏规则。
🚀这有你错过的精彩内容
-
❤️Android Studio实现学生信息系统❤️
-
❤️Android Studio实现学生选课系统❤️
-
❤️Android Stduio实现天气预报APP❤️
-
❤️Android Studio实现仓库管理系统❤️
-
❤️Android Studio实现校园二手交易系统❤️
从追求效率的角度看,只有真正有价值的事情,才值得你这样耗用自己宝贵的时间。