距离上次发博客好久了,主要是最近太忙,一直再忙着学习各种高级的控件知识以及找工作之类的,所以十分抱歉,刚好抽今天周末,把后面的补上。废话不多说,咱们开始进入正题。
前面两个帖子吧登陆界面和开始界面介绍完了,现在开始写咱们的主要功能实现代码,因为我所用的思路在“简单”,“一般”,“困难”三个不同关卡都差不多,只是其中的参数发生了一些改变,所以这里直接拿”简单“关卡来做介绍
首先呢,我想获取一个效果,就是界面上面的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);
接下来就是添加整体布局了,对于游戏的界面,我使用的线性布局,将布局先简单的画出来,然后再去搭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(),重新生成初始化的排列,然后继续判断,直到倒置数之和为偶数