距离上次发博客好久了,主要是最近太忙,一直再忙着学习各种高级的控件知识以及找工作之类的,所以十分抱歉,刚好抽今天周末,把后面的补上。废话不多说,咱们开始进入正题。
前面两个帖子吧登陆界面和开始界面介绍完了,现在开始写咱们的主要功能实现代码,因为我所用的思路在“简单”,“一般”,“困难”三个不同关卡都差不多,只是其中的参数发生了一些改变,所以这里直接拿”简单“关卡来做介绍
首先呢,我想获取一个效果,就是界面上面的ActionBar是透明的,注意,这里并不是说用的是NoActionBar,而是将ActionBar的背景换成透明的
/**
* Actionbar设置
*/
private void initActionBar() {
// TODO Auto-generated method stub
// 获取ActionBar
actionBar = getActionBar();
Resources r = getResources();
// 将ActionBar的背景更换
Drawable myDrawable = r.getDrawable(R.drawable.actionbar);
actionBar.setBackgroundDrawable(myDrawable);
// 设置主键按钮能否被点击
actionBar.setHomeButtonEnabled(true);
// 返回的图标是否显示
actionBar.setDisplayHomeAsUpEnabled(true);
}
其中的actionbar是一张透明的图片,所以说,会使用简单的PS来作图对于变成学习还是很有用到的
当然,仅仅这样还是不行的,还需要在添加布局文件之前添上这样一行代码,即:
// 将ActionBar悬浮在布局之上
<span style="white-space:pre"> </span>getWindow().requestFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
<span style="white-space:pre"> </span>setContentView(R.layout.activity_easy);
<span style="white-space:pre"> </span>initActionBar();
文中也注释到了,
getWindow().requestFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
这句话的作用是将ActionBar悬浮在布局之上
接下来就是添加整体布局了,对于游戏的界面,我使用的线性布局,将布局先简单的画出来,然后再去搭UI,虽然可能步骤觉得繁琐但是在搭UI的时候,会发现有了一个简单的草图,UI搭起来很方便
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/ll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/background13"
android:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="48dp"
android:gravity="bottom" >
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:src="@drawable/partition" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="20dp"
android:orientation="horizontal" >
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center_vertical"
android:orientation="horizontal" >
<TextView
android:id="@+id/easy_tv1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center_vertical|right"
android:orientation="horizontal" >
<TextView
android:id="@+id/easy_tv2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:text="@string/hello" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginLeft="15sp"
android:layout_marginRight="15dp"
android:layout_marginTop="0dp"
android:layout_weight="8"
android:orientation="horizontal" >
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="horizontal" >
<ImageView
android:id="@+id/easy_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="2dp"
android:scaleType="fitXY"
android:src="@drawable/img1" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="horizontal" >
<ImageView
android:id="@+id/easy_4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="2dp"
android:scaleType="fitXY"
android:src="@drawable/img4" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="horizontal" >
<ImageView
android:id="@+id/easy_7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="2dp"
android:scaleType="fitXY"
android:src="@drawable/img7" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="horizontal" >
<ImageView
android:id="@+id/easy_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="2dp"
android:scaleType="fitXY"
android:src="@drawable/img2" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="horizontal" >
<ImageView
android:id="@+id/easy_5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="2dp"
android:scaleType="fitXY"
android:src="@drawable/img5" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="horizontal" >
<ImageView
android:id="@+id/easy_8"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="2dp"
android:scaleType="fitXY"
android:src="@drawable/img8" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="horizontal" >
<ImageView
android:id="@+id/easy_3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="2dp"
android:scaleType="fitXY"
android:src="@drawable/img3" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="horizontal" >
<ImageView
android:id="@+id/easy_6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="2dp"
android:scaleType="fitXY"
android:src="@drawable/img6" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="horizontal" >
<ImageView
android:id="@+id/easy_9"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="2dp"
android:scaleType="fitXY"
android:src="@drawable/img9" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="2"
android:gravity="center" >
<Button
android:id="@+id/easy_bt1"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:background="@drawable/select"
android:gravity="center"
android:onClick="onclick"
android:text="重新开始" />
</LinearLayout>
</LinearLayout>
搭出来的效果就是这样
其中的每一个ImageView都是一张小图,我是把一张完整的大图切出来的,当然,这是一个大工程,不过如果你会使用Ps,使用里面的批量切图,会节省很多精力
布局搭好之后,就可以获取控件了
/**
* 通过findViewById找到控件位置
*
*/
public void findId() {
action_showimage = (ImageView) findViewById(R.id.action_showimage);
ll = (LinearLayout) findViewById(R.id.ll);
tv1 = (TextView) findViewById(R.id.easy_tv1);
tv2 = (TextView) findViewById(R.id.easy_tv2);
iv_group = new ImageView[3][3];
iv_group[0][0] = (ImageView) findViewById(R.id.easy_1);
iv_group[0][1] = (ImageView) findViewById(R.id.easy_2);
iv_group[0][2] = (ImageView) findViewById(R.id.easy_3);
iv_group[1][0] = (ImageView) findViewById(R.id.easy_4);
iv_group[1][1] = (ImageView) findViewById(R.id.easy_5);
iv_group[1][2] = (ImageView) findViewById(R.id.easy_6);
iv_group[2][0] = (ImageView) findViewById(R.id.easy_7);
iv_group[2][1] = (ImageView) findViewById(R.id.easy_8);
iv_group[2][2] = (ImageView) findViewById(R.id.easy_9);
}
然后就可以把图片添加到布局上了
<span style="font-size:18px;">/**
*
* 将图片添到布局
*/
public void initGame() {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (j + i == 4) {
iv_group[i][j].setImageResource(number[8]);
} else {
iv_group[i][j].setImageResource(number[i * 3 + j]);
}
}
}
}</span>
接下来就可以初始化界面了,这里尤其有一点要特别注意,就是做拼图的时候,会涉及一个很经典的数学逻辑问题,需要在初始化的时候做判断,不然的话,你的程序完成之后,总会有百分之五十的概率是最后两张图片无法完成,这就是经典的15puzzle算法问题。
这里会涉及几个概念:
1,行数
2,空白控件位于从底部往上数第几个控件
3,倒置数之和
首先,第一个行数就不用解释了吧,像我们的这个简单关卡是3*3的模式,所以行数就是3
第二个空白控件,值得就是像我们3*3的模式中,游戏中,只会显示出来8张图片,有一个空白的是用来移动的,这就是那个空白控件
第三个是倒置数,我们举个例子说明一下,我们是3*3的游戏模式,会出现8张图片,正确排列的时候是
012
345
67
这时它们的顺序就是0,1,2,3,4,5,6,7
但是我们初始化的时候肯定会把顺序打乱,这个可以是随机的那么它的顺序可能是这样的
461
270
35
那么这是它们的顺序就是4,6,1,2,7,0,3,5
那么他们的倒置数就是:4,5,1,1,3,
这里的倒置数就是指一个数组,先拿第一个数跟它后面的数比较,有A个比这个数小的数,那么这组数的第一个倒置数就是A,接着拿第二个数跟第二个数后面的数比较下去,依次下去,如果一个数后面的数都比这个数大,则记为0
相信这样解释大家都明白倒置数是什么了吧,至于倒置数之和,指的就是这些倒置数相加的和
接着咱们就需要来了解这个判断规则了:
1,当行数是偶数时,无论怎样,拼图最后都完成,不会出现最后两张图片异位的现象(这个是我之前找到博友的一篇帖子上提到的,之前并没有怎么验证,但是,当我仔细验证的时候,发现就算行数是偶数的时候,依旧会出现图片异位现象,所以,这句应该是不正确的,也希望广大博友们都试试,大家一起讨论出结果)
2,当行数是奇数时:
a,当空白控件距离底部的距离有奇数个控件时,倒置数之和为奇数,不会出现最后两张图片异位的现象
b,当空白控件距离底部的距离有偶数个控件时,倒置数之和为偶数,不会出现最后两张图片异位的现象
这点可能和我之前的一篇专门讲图片异位问题的博客描述有所不同,是因为之前并没有仔细的去验证,所以并没有发现问题所在,这里跟大家说声抱歉
咱们还是拿例子来说明一下,3*3和5*5肯定有百分之五十的概率出现图片异位的现象,而且4*4也可能出现这种情况,所以我们就需要判断了,因为我们指定的空白控件在出现的时候是出现在最右下角位置,那么空白控件距离底部的距离为0,则空白控件距离底部的距离有偶数个控件,我们只需判定导致数之和是否为偶数即可
代码如下
/**
* 初始化图片
*/
public void initView() {
// 获取图片
for (int i = 0; i < number.length; i++) {
number[i] = gameover[i];
}
// 将图片随机化
for (int i = 0; i < number.length - 4; i++) {
int temp = number[i];
int index = (int) (Math.random() * (number.length - 5));
number[i] = number[index];
number[index] = temp;
}
// 将图片添加到布局
initGame();
// 设置步数
socer = 0;
tv2.setText("当前移动了 " + String.valueOf(socer) + " 步");
// 获取随机化图片之后图片的顺序
getlist();
// 判断生成是否有解
if (canSolve(data)) {
return;
} else {
initView();
}
}
/**
*
* 将图片添到布局
*/
public void initGame() {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (j + i == 4) {
iv_group[i][j].setImageResource(number[8]);
} else {
iv_group[i][j].setImageResource(number[i * 3 + j]);
}
}
}
}
/**
* 获取data
*
*/
public void getlist() {
data = new ArrayList<Integer>();
for (int i = 0; i < number.length - 4; i++) {
for (int j = 0; j < number.length - 4; j++) {
if (number[i] == gameover[j]) {
data.add(j);
}
}
}
System.out.println("============" + data.toString());
}
/**
* 该数据是否有解 因为是奇数,而且设置的空白格的初始位置是最下面一行, 所以只需要判断倒置数 getInversions(data)是否为偶数
*
* @param data
* @return 该数据是否有解
*/
public boolean canSolve(List<Integer> data) {
// 可行性原则
return getInversions(data) % 2 == 0;
}
/**
* 计算倒置和算法
*
* @param data
* @return 该序列的倒置和 倒置数:每个数与后面的数比较,看有几个比该数小的数,有一个便加一,以此类推
*
*/
public static int getInversions(List<Integer> data) {
int inversions = 0;
int inversionCount;
for (int i = 0; i < data.size(); i++) {
inversionCount = 0;
for (int j = i + 1; j < data.size(); j++) {
int index = data.get(i);
if (data.get(j) < index) {
inversionCount++;
}
}
inversions += inversionCount;
inversionCount = 0;
}
System.out.println("******" + data.toString() + inversions);
return inversions;
}
这样在初始化的时候,会进行判断,如果倒置数是偶数,怎会赋值成功,如果倒置数不是偶数,那么会重新调用initView(),重新生成初始化的排列,然后继续判断,直到倒置数之和为偶数