有一段时间没写安卓的代码,慢慢的有点忘了,这个按照视频一步一步来的复原
主要步骤:
1.把一张大图分成多个方块图,GridLayout实现
2.设置某个方块为缺口(拼图是总有一个空白的方块)
3.点击交换缺口与相邻方块的数据
4.获取手势,手势交换缺口与相邻方块的数据
5.打乱顺序
6.判断结束
按钮背景,放在drawable文件夹下,命名bg_btn

<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true"> <!-- 定义当button 处于pressed 状态时的形态。--> <shape> <gradient android:startColor="#8600ff" /> <stroke android:width="2dp" android:color="#000000" /> <corners android:radius="5dp" /> <padding android:left="10dp" android:top="10dp" android:bottom="10dp" android:right="10dp" /> </shape> </item> <item android:state_pressed="false"> <!-- 定义当button 处于pressed 状态时的形态。--> <shape> <gradient android:startColor="#aeaeae" android:endColor="#b0b0b0" /> <stroke android:width="2dp" android:color="#333333" /> <corners android:radius="5dp" /> <padding android:left="10dp" android:top="10dp" android:bottom="10dp" android:right="10dp" /> </shape> </item> </selector>
布局文件activity_main.xml

<RelativeLayout 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" tools:context=".MainActivity" > <GridLayout android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/gl_main_game" android:columnCount="4" android:rowCount="5" android:fillViewport="true" > </GridLayout> <Button android:id="@+id/btn_start" android:layout_width="120px" android:layout_height="60px" android:layout_marginRight="5px" android:layout_marginLeft="5px" android:layout_centerHorizontal="true" android:layout_alignParentBottom="true" android:layout_marginBottom="20px" android:background="@drawable/bg_btn" android:text="Start" /> <Button android:id="@+id/btn_back" android:layout_width="120px" android:layout_height="60px" android:layout_toLeftOf="@+id/btn_start" android:layout_alignParentBottom="true" android:layout_marginBottom="20px" android:background="@drawable/bg_btn" android:text="Back" /> <Button android:id="@+id/btn_select" android:layout_width="120px" android:layout_height="60px" android:layout_toRightOf="@+id/btn_start" android:layout_alignParentBottom="true" android:layout_marginBottom="20px" android:background="@drawable/bg_btn" android:text="Select" /> </RelativeLayout>
主函数实现逻辑

package com.example.jigsaw; import android.os.Bundle; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.animation.Animation; import android.view.animation.Animation.AnimationListener; import android.view.animation.TranslateAnimation; import android.widget.Button; import android.widget.GridLayout; import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.Toast; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.view.GestureDetector.OnGestureListener;//手势监听包,不提示导入?? public class MainActivity extends Activity { /**当前移动动画是否正在执行*/ private boolean isAnimRun = false; /** 利用二维数组创建若干小方块 **/ private ImageView[][] iv_game_arr = new ImageView[5][4]; /** 游戏主界面 **/ private GridLayout gl_main_game; /** 当前空方块的实例保存 **/ private ImageView iv_null_ImageView; /** 当前手势 */ private GestureDetector mDetector; /**判断游戏是否开始,默认没开始*/ private boolean isGameStart = false; @Override public boolean onTouchEvent(MotionEvent event) { // TODO Auto-generated method stub return mDetector.onTouchEvent(event);// 把界面监听换成手势监听 } @Override public boolean dispatchTouchEvent(MotionEvent ev) { // TODO Auto-generated method stub mDetector.onTouchEvent(ev); return super.dispatchTouchEvent(ev); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mDetector = new GestureDetector(MainActivity.this, new OnGestureListener() { @Override public boolean onSingleTapUp(MotionEvent arg0) { // TODO Auto-generated method stub return false; } @Override public void onShowPress(MotionEvent arg0) { // TODO Auto-generated method stub } @Override public boolean onScroll(MotionEvent arg0, MotionEvent arg1, float arg2, float arg3) { // TODO Auto-generated method stub return false; } @Override public void onLongPress(MotionEvent arg0) { // TODO Auto-generated method stub } @Override public boolean onFling(MotionEvent arg0, MotionEvent arg1, float arg2, float arg3) { int type = getDirByGes(arg0.getX(), arg0.getY(), arg1.getX(), arg1.getY());// 判断手势返回int类型 // Toast.makeText(MainActivity.this, "" + // type,Toast.LENGTH_SHORT).show(); changeByDir(type);// 移动换图 return false; } @Override public boolean onDown(MotionEvent arg0) { // TODO Auto-generated method stub return false; } }); setContentView(R.layout.activity_main); /** 初始化游戏的若干小方块 */ // 获取一张大图片 Bitmap bigBm = ((BitmapDrawable) getResources().getDrawable( R.drawable.kenan1)).getBitmap(); int tuWandW = bigBm.getWidth() / (iv_game_arr.length);// 每个游戏块的宽和高 int tvWandH= this.getWindowManager().getDefaultDisplay().getWidth()/ (iv_game_arr.length-1);//小方块的宽度应为屏幕的平均值 for (int i = 0; i < iv_game_arr.length; i++) { for (int j = 0; j < iv_game_arr[i].length; j++) { Bitmap bm = Bitmap.createBitmap(bigBm, j * tuWandW, i * tuWandW, tuWandW, tuWandW);// 根据行和列切成若干个游戏小图片 iv_game_arr[i][j] = new ImageView(this); iv_game_arr[i][j].setImageBitmap(bm);// 设置每一个小方块的图片 iv_game_arr[i][j].setLayoutParams(new RelativeLayout.LayoutParams(tvWandH, tvWandH));//设置宽高 iv_game_arr[i][j].setPadding(2, 2, 2, 2);// 设置方块的间距 iv_game_arr[i][j].setTag(new GameData(i, j, bm));// 绑定自定义数据 // 设置点击监听,判断位置关系 iv_game_arr[i][j].setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub boolean flag = isHasByNullImageView((ImageView) v); // Toast.makeText(MainActivity.this, "位置关系是否存在:"+flag, // Toast.LENGTH_SHORT).show(); if (flag) { changeDataByImageView((ImageView) v); } } }); } } // 初始化游戏界面,并添加若干小方块 gl_main_game = (GridLayout) findViewById(R.id.gl_main_game); for (int i = 0; i < iv_game_arr.length; i++) { for (int j = 0; j < iv_game_arr[i].length; j++) { gl_main_game.addView(iv_game_arr[i][j]); } } /** 设置第一个方块为空 */ setNullIamgeView(iv_game_arr[0][0]); Button btnStart=(Button) findViewById(R.id.btn_start);//button点击之后打乱 btnStart.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { // TODO Auto-generated method stub /** 初始化随机打乱顺序方块 */ randomMove(); isGameStart = true;//开始状态 } }); } /** * 根据手势的方向,获取空方块相应的相邻位置如果存在方块,那么进行数据交换 * * @param type * 1:上,2:下,3:左,4:右 */ public void changeByDir(int type) { changeByDir(type, true); } /** * 根据手势的方向,获取空方块相应的相邻位置如果存在方块,那么进行数据交换 * * @param type * 1:上,2:下,3:左,4:右 * @param isAnim * ture:有动画,false:无动画 */ public void changeByDir(int type, boolean isAnim) { // 获取当前空方块的位置 GameData mNullGameData = (GameData) iv_null_ImageView.getTag(); // 根据方向,设置相应的相邻的位置坐标 int new_x = mNullGameData.x; int new_y = mNullGameData.y; if (type == 1) { new_x++; } else if (type == 2) { new_x--; } else if (type == 3) { new_y++; } else if (type == 4) { new_y--; } // 判断这个新坐标,是否存在 if (new_x >= 0 && new_x < iv_game_arr.length && new_y >= 0 && new_y < iv_game_arr[0].length) { // 存在的话,开始移动 if (isAnim) {// 有动画 changeDataByImageView(iv_game_arr[new_x][new_y]); } else {// 无动画 changeDataByImageView(iv_game_arr[new_x][new_y], isAnim); } } else { // 什么也不做 } } /** * 判断游戏结束的方法 */ public void isGameOver(){ boolean isGameOver = true; //要遍历每个游戏小方块 for (int i = 0; i < iv_game_arr.length; i++) { for (int j = 0; j < iv_game_arr[0].length; j++) { //为空的方块数据不判断 if(iv_game_arr[i][j]==iv_null_ImageView){ continue; } GameData mGameData = (GameData) iv_game_arr[i][j].getTag(); if (!mGameData.isTrue()) { isGameOver=false; break; } } } //根据一个开关变量决定游戏是否结束,结束时给提 if (isGameOver) { Toast.makeText(MainActivity.this, "游戏结束", Toast.LENGTH_SHORT).show(); } } /** * 手势判断,是向左,还是右 * * @param start_x * 手势的起始点x * @param start_y * 手势的起始点y * @param end_x * 手势的终止点x * @param end_y * 手势的终止点y * @return 1:上,2:下,3:左,4:右 */ public int getDirByGes(float start_x, float start_y, float end_x, float end_y) { boolean isLeftOrRight = (Math.abs(start_x - end_x) > Math.abs(start_y - end_y)) ? true : false;// 是否是左右 if (isLeftOrRight) {// 左右 boolean isLeft = start_x - end_x > 0 ? true : false;// 判断左右 if (isLeft) { return 3; } else { return 4; } } else {// 上下 boolean isUp = start_y - end_y > 0 ? true : false;// if (isUp) { return 1; } else { return 2; } } } // 随机打乱顺序 public void randomMove() { // 打乱的次数 for (int i = 0; i < 100; i++) { // 根据手势开始交换,无动画 int type = (int) (Math.random() * 4) + 1;// 随机数得到[0,4)之间的数,加上一就是移动的顺序 changeByDir(type, false); } } /** * 利用动画结束之后,交换两个方块的数据 * * @param mImageView * 点击的方块 */ public void changeDataByImageView(final ImageView mImageView) { changeDataByImageView(mImageView, true); } /** * 利用动画结束之后,交换两个方块的数据 * * @param mImageView * 点击的方块 * @param isAnim * true 有动画,false 无动画 */ public void changeDataByImageView(final ImageView mImageView, boolean isAnim) { if (isAnimRun) {//如果动画正在执行,不进行操作 return; } if (!isAnim) {//判断是否有动画 GameData mGameData = (GameData) mImageView.getTag(); iv_null_ImageView.setImageBitmap(mGameData.bm);// 设置空方块为交换的图片 GameData mNullGameData = (GameData) iv_null_ImageView.getTag(); mNullGameData.bm = mGameData.bm; mNullGameData.p_x = mGameData.p_x; mNullGameData.p_y = mGameData.p_y; setNullIamgeView(mImageView);// 设置当前点击的为空模块 if (isGameStart) {//开始后每一次移动都要判断是否结束 isGameOver();// 成功时会弹一个toast } return; } // 创建一个动画,设置好方向,移动距离 TranslateAnimation translateAnimation = null; if (mImageView.getX() > iv_null_ImageView.getX()) {// 当前点击的方块在空方块的下面 // 往上移 translateAnimation = new TranslateAnimation(0.1f, -mImageView.getWidth(), 0.1f, 0.1f); } else if (mImageView.getX() < iv_null_ImageView.getX()) { // 往下移 translateAnimation = new TranslateAnimation(0.1f, mImageView.getWidth(), 0.1f, 0.1f); } else if (mImageView.getY() > iv_null_ImageView.getY()) { // 往左移 translateAnimation = new TranslateAnimation(0.1f, 0.1f, 0.1f, -mImageView.getWidth()); } else if (mImageView.getY() < iv_null_ImageView.getY()) { // 往右移 translateAnimation = new TranslateAnimation(0.1f, 0.1f, 0.1f, mImageView.getWidth()); } // 设置动画的的时长 translateAnimation.setDuration(70); // 设置动画结束之后时候是否停留 translateAnimation.setFillAfter(true); // 设置动画结束之后要真正的把数据交换了 translateAnimation.setAnimationListener(new AnimationListener() { @Override public void onAnimationStart(Animation arg0) { // TODO Auto-generated method stub isAnimRun = true; } @Override public void onAnimationRepeat(Animation arg0) { // TODO Auto-generated method stub } @Override public void onAnimationEnd(Animation arg0) { // TODO Auto-generated method stub isAnimRun = false; mImageView.clearAnimation(); GameData mGameData = (GameData) mImageView.getTag(); iv_null_ImageView.setImageBitmap(mGameData.bm);// 设置空方块为交换的图片 GameData mNullGameData = (GameData) iv_null_ImageView.getTag(); mNullGameData.bm = mGameData.bm; mNullGameData.p_x = mGameData.p_x; mNullGameData.p_y = mGameData.p_y; setNullIamgeView(mImageView);// 设置当前点击的为空模块 if (isGameStart) {//开始后每一次移动都要判断是否结束 isGameOver();// 成功时会弹一个toast } } }); // 执行动画 mImageView.startAnimation(translateAnimation); } /** * 设置某个方块为空 * * @param mImageView * 当前要设置为空的方块实例 */ public void setNullIamgeView(ImageView mImageView) { mImageView.setImageBitmap(null); // mImageView.setBackgroundColor(10);//设置空的背景色 iv_null_ImageView = mImageView; } /** * 判断当前点击的方块,是否与空方块的位置关系是否相邻 * * @param mImageView * 点击的方块实例 * @return true:相邻,false:不相邻 */ public boolean isHasByNullImageView(ImageView mImageView) { // 分别获取当前空方块的位置与点击方块的位置,通过x,y两边都差一的方式判断 GameData mNullGameData = (GameData) iv_null_ImageView.getTag(); GameData mGameData = (GameData) mImageView.getTag(); if (mNullGameData.y == mGameData.y && mGameData.x + 1 == mNullGameData.x) {// 当前点击的方块在空方块的上边 return true; } else if (mNullGameData.y == mGameData.y && mGameData.x - 1 == mNullGameData.x) {// 当前点击的方块在空方块的下边 return true; } else if (mNullGameData.y == mGameData.y + 1 && mGameData.x == mNullGameData.x) {// 当前点击的方块在空方块的左边 return true; } else if (mNullGameData.y == mGameData.y - 1 && mGameData.x == mNullGameData.x) {// 当前点击的方块在空方块的右边 return true; } return false; } /** * 每个游戏小方块上要绑定的数据 */ class GameData { /** 每个小方块的实际位置x */ public int x = 0; /** 每个小方块的实际位置y */ public int y = 0; /** 每个小方块的图片 */ public Bitmap bm; /** 每个小方块的图片的位置x */ public int p_x = 0; /** 每个小方块的图片的位置y */ public int p_y = 0; public GameData(int x, int y, Bitmap bm) { super(); this.x = x; this.y = y; this.bm = bm; this.p_x = x; this.p_y = y; } /** * 每个小方块的位置是否正确 * @return true:正确,false:不正确 */ public boolean isTrue(){ if (x==p_x && y==p_y) { return true; } return false; } } }
效果如下: