Android拼图游戏的设计逻辑,从切图到交互动画,从关卡到倒计时,实例提高!


群英传的最后一章,我大致的看了一下这个例子,发现鸿洋大神也做过,就参考两个人的设计逻辑,感觉都差不多,就这样实现起来了

一.切图工具类

我们九宫格嘛,肯定要一个切图的工具,把一个图片给切成九张,那具体是怎么实现呢?我们先写一个bean来存储一切的状态

ImagePiece

package com.lgl.ninegame.utils;import android.graphics.Bitmap;/**** Created by LGL on 2016/5/2.*/
public class ImagePiece {private int index;private Bitmap bitmap;//构造方法public ImagePiece() {}//有参构造方法public ImagePiece(int index, Bitmap bitmap) {this.index = index;this.bitmap = bitmap;}public int getIndex() {return index;}public void setIndex(int index) {this.index = index;}public Bitmap getBitmap() {return bitmap;}public void setBitmap(Bitmap bitmap) {this.bitmap = bitmap;}}

然后就可以实现我们的切图工具类了

ImageSplitterUtil

package com.lgl.ninegame.utils;import android.graphics.Bitmap;import java.util.ArrayList;
import java.util.List;/*** 切图工具* Created by LGL on 2016/5/2.*/
public class ImageSplitterUtil {/*** 静态方法* 传递bitmap切成piece*piece块,返回List<ImagePiece>** @param bitmap* @param piece* @return*/public static List<ImagePiece> splitImage(Bitmap bitmap, int piece) {//作为返回值传递List<ImagePiece> imagePieces = new ArrayList<>();//获取图片的宽高int width = bitmap.getWidth();int height = bitmap.getHeight();//根据宽高取最小值达到正方形int pieceWidth = Math.min(width, height) / piece;//进行切割for (int i = 0; i < piece; i++) {for (int j = 0; j < piece; j++) {ImagePiece imagePiece = new ImagePiece();imagePiece.setIndex(j + i * piece);int x = j * pieceWidth;int y = i * pieceWidth;//第一次循环为0,0,imagePiece.setBitmap(Bitmap.createBitmap(bitmap, x, y, pieceWidth, pieceWidth));/*** 保存到list中进行返回*/imagePieces.add(imagePiece);}}return imagePieces;}}

二.自定义容器

工具有了,就需要容器了,我们需要自定义一个,这里我们就继承相对布局,我们这个是一个游戏布局,所以我实现准备好了一张妹子的图片,分辨率是800*80

GameView

package com.lgl.ninegame.view;import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import android.widget.ImageView;
import android.widget.RelativeLayout;import com.lgl.ninegame.R;
import com.lgl.ninegame.utils.ImagePiece;
import com.lgl.ninegame.utils.ImageSplitterUtil;import java.util.Collections;
import java.util.Comparator;
import java.util.List;/*** 自定义容器* Created by LGL on 2016/5/2.*/
public class GameView extends RelativeLayout implements View.OnClickListener {//默认3*3private int mColumn = 3;//容器的内边距private int mPadding;//小图的距离 dpprivate int mMagin = 3;//存储图片的,宽高 都是固定的,所以使用数组private ImageView[] mGameOintuItems;//宽度private int mItemWidth;//图片private Bitmap mBitmap;//切图后存储private List<ImagePiece> mItemBitmaps;//标记private boolean once;//容器的一个宽度private int mWidth;public GameView(Context context) {this(context, null);}public GameView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public GameView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}/*** 初始化*/private void init() {//单位转换——dp-pxmMagin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 3, getResources().getDisplayMetrics());mPadding = min(getPaddingLeft(), getPaddingRight(), getPaddingTop(), getPaddingBottom());}/*** 确定当前布局的大小,我们要设置成正方形** @param widthMeasureSpec* @param heightMeasureSpec*/@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);//拿到容器的高宽最小值mWidth = Math.min(getMeasuredHeight(), getMeasuredWidth());if (!once) {//进行切图和排序initBitmap();//设置imageview(item)的宽高等属性initItem();once = true;}setMeasuredDimension(mWidth, mWidth);}/*** 进行切图和排序*/private void initBitmap() {//判断是否存在这张图片if (mBitmap == null) {mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.img);}//进行裁剪mItemBitmaps = ImageSplitterUtil.splitImage(mBitmap, mColumn);//裁剪玩后需要进行顺序打乱sortCollections.sort(mItemBitmaps, new Comparator<ImagePiece>() {@Overridepublic int compare(ImagePiece lhs, ImagePiece rhs) {//生成随机数,如果》0.5返回1否则返回-1return Math.random() > 0.5 ? 1 : -1;}});}/*** 设置imageview(item)的宽高等属性*/private void initItem() {//( 容器的宽度 - 内边距 * 2  - 间距  ) /  裁剪的数量mItemWidth = (mWidth - mPadding * 2 - mMagin * (mColumn - 1)) / mColumn;//几 * 几mGameOintuItems = new ImageView[mColumn * mColumn];//开始排放for (int i = 0; i < mGameOintuItems.length; i++) {ImageView item = new ImageView(getContext());item.setOnClickListener(this);//设置图片item.setImageBitmap(mItemBitmaps.get(i).getBitmap());//保存mGameOintuItems[i] = item;//设置IDitem.setId(i + 1);//设置Tagitem.setTag(i + "_" + mItemBitmaps.get(i).getIndex());RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(mItemWidth, mItemWidth);//判断不是最后一列if (i + 1 % mColumn != 0) {lp.rightMargin = mMagin;}//判断不是第一列if (i % mColumn != 0) {lp.addRule(RelativeLayout.RIGHT_OF, mGameOintuItems[i - 1].getId());}//判断如果不是第一行if ((i + 1) > mColumn) {lp.topMargin = mMagin;lp.addRule(RelativeLayout.BELOW, mGameOintuItems[i - mColumn].getId());}addView(item, lp);}}/*** 获取多个参数的最小值*/private int min(int... params) {int min = params[0];//遍历for (int param : params) {if (param < min) {min = param;}}return min;}/*** 点击事件** @param v*/@Overridepublic void onClick(View v) {}
}

代码的逻辑十分的简单,这点不假,而且注释也是相对来讲清晰易懂,这样,我们先测试一下,在XML中引用

<?xml version="1.0" encoding="utf-8"?>
<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"android:padding="10dp"><com.lgl.ninegame.view.GameView
        android:layout_width="match_parent"android:layout_height="match_parent"android:layout_centerInParent="true" /></RelativeLayout>

我们运行一下看看现在裁剪后的效果

可以的,那我们继续

三.实现图片交互

这里大家应该都知道,要用我们的点击事件了,我们继续在GameView里面编写

 /*** 点击的第一张图和第二张图,他们进行交换*/private ImageView mFirst;private ImageView mSecond;/*** 点击事件** @param v*/@Overridepublic void onClick(View v) {//重复点击if (mFirst == v) {//去掉颜色mFirst.setColorFilter(null);mFirst = null;return;}if (mFirst == null) {mFirst = (ImageView) v;//设置选中效果mFirst.setColorFilter(Color.parseColor("#55FF0000"));//第二次点击} else {mSecond = (ImageView) v;//交换exchangeView();}}

这里我们需要去写一个图片交换的方法

 /*** 图片交换*/private void exchangeView() {//先去掉颜色mFirst.setColorFilter(null);String firstTag = (String) mFirst.getTag();String secondTag = (String) mSecond.getTag();String[] firstParams = firstTag.split("_");String[] scendParams = secondTag.split("_");//获取到bitmap并且替换mSecond.setImageBitmap(mItemBitmaps.get(Integer.parseInt(firstParams[0])).getBitmap());mFirst.setImageBitmap(mItemBitmaps.get(Integer.parseInt(scendParams[0])).getBitmap());//回到最初始的状态mFirst = mSecond = null;}

这样,我们就可以运行了

但是,大家有没有发现,他的小bitmap排序是错误的,这就需要我们去处理了

实际上是因为我们虽然交换了,但是tag没改,我们在回到最初始的状态之前加上以下代码:

//改变tagmFirst.setTag(secondTag);mSecond.setTag(firstTag);

四.交互动画

既然我们卡片已经实现了,那么接下来,我们怎么的也要去加点动画呀,开搞,我们先把公用的东西抽取出来

 /*** 获取tag** @param tag* @return*/public int getImageIdByTag(String tag) {String[] split = tag.split("_");return Integer.parseInt(split[0]);}/*** 获取图片的tag** @param tag* @return*/public int getImageIndex(String tag) {String[] split = tag.split("_");return Integer.parseInt(split[1]);}/*** 动画层,覆盖在viewGroup中*/private RelativeLayout mAnimLayout;/*** 交互动画*/private void setUpAnimLayout() {if (mAnimLayout == null) {mAnimLayout = new RelativeLayout(getContext());//添加到整体addView(mAnimLayout);}}

接着,我们需要修改以下图片交换的方法,添加一层动画层

/*** 图片交换*/private void exchangeView() {mFirst.setColorFilter(null);// 构造我们的动画层setUpAnimLayout();ImageView first = new ImageView(getContext());final Bitmap firstBitmap = mItemBitmaps.get(getImageIdByTag((String) mFirst.getTag())).getBitmap();first.setImageBitmap(firstBitmap);LayoutParams lp = new LayoutParams(mItemWidth, mItemWidth);lp.leftMargin = mFirst.getLeft() - mPadding;lp.topMargin = mFirst.getTop() - mPadding;first.setLayoutParams(lp);mAnimLayout.addView(first);ImageView second = new ImageView(getContext());final Bitmap secondBitmap = mItemBitmaps.get(getImageIdByTag((String) mSecond.getTag())).getBitmap();second.setImageBitmap(secondBitmap);LayoutParams lp2 = new LayoutParams(mItemWidth, mItemWidth);lp2.leftMargin = mSecond.getLeft() - mPadding;lp2.topMargin = mSecond.getTop() - mPadding;second.setLayoutParams(lp2);mAnimLayout.addView(second);// 设置动画TranslateAnimation anim = new TranslateAnimation(0, mSecond.getLeft()- mFirst.getLeft(), 0, mSecond.getTop() - mFirst.getTop());anim.setDuration(300);anim.setFillAfter(true);first.startAnimation(anim);TranslateAnimation animSecond = new TranslateAnimation(0,-mSecond.getLeft() + mFirst.getLeft(), 0, -mSecond.getTop()+ mFirst.getTop());animSecond.setDuration(300);animSecond.setFillAfter(true);second.startAnimation(animSecond);// 监听动画anim.setAnimationListener(new Animation.AnimationListener(){@Overridepublic void onAnimationStart(Animation animation){mFirst.setVisibility(View.INVISIBLE);mSecond.setVisibility(View.INVISIBLE);}@Overridepublic void onAnimationRepeat(Animation animation){// TODO Auto-generated method stub}@Overridepublic void onAnimationEnd(Animation animation){String firstTag = (String) mFirst.getTag();String secondTag = (String) mSecond.getTag();mFirst.setImageBitmap(secondBitmap);mSecond.setImageBitmap(firstBitmap);mFirst.setTag(secondTag);mSecond.setTag(firstTag);mFirst.setVisibility(View.VISIBLE);mSecond.setVisibility(View.VISIBLE);mFirst = mSecond = null;mAnimLayout.removeAllViews();}});}

这样,我们就可以有动画效果了,我们来运行一下

到现在,我们就已经可以玩了,但是这样子就不叫游戏了,我们应该这样添加一些过关的逻辑

五.过关逻辑

其实过关的逻辑很简单的,只要我们在每次移动之后去判断是不是过关了就行,如下代码

 /*** 判断是否过关*/private void checkSuccess() {boolean isSuccess = true;for (int i = 0; i < mGameOintuItems.length; i++) {//拿到所有的图片ImageView imageView  = mGameOintuItems[i];if(getImageIndex((String) imageView.getTag()) != i){isSuccess = false;}}if(isSuccess){Log.i("tag","成功");Toast.makeText(getContext(),"成功,进入下一关!",Toast.LENGTH_LONG).show();}}

OK,但是这个不是重点,重点是我们要下一关,而且要把相应的数据传递给MainActivity,这就要实现接口了

 private static final int TIME_CHANGED = 10;private static final int NEXT_LEVEL = 11;/*** 设置接口回调** @param mListener*/public void setOnGamemListener(GamePintuListener mListener) {this.mListener = mListener;}public GamePintuListener mListener;/*** 关数*/private int level = 1;/*** 设置开启时间** @param timeEnabled*/public void setTimeEnabled(boolean timeEnabled) {isTimeEnabled = timeEnabled;}//接口private interface GamePintuListener {//关卡void nextLevel(int nextLevel);//时间void timechanged(int time);//游戏结束void gameOver();}

同时要实现下一关的方法

 /*** 下一关*/public void nextLevel() {this.removeAllViews();mAnimLayout = null;mColumn++;isGameSuccess = false;initBitmap();initItem();}

这样,我们就可以在handler中操作了

//子线程操作private Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case TIME_CHANGED:break;case NEXT_LEVEL:level = level + 1;if(mListener!=null){mListener.nextLevel(level);}else{nextLevel();}break;}super.handleMessage(msg);}};

这边的逻辑OK了之后,我们需要回到MainActivity去操作显示UI

gameview = (GameView) findViewById(R.id.gameview);gameview.setOnGamemListener(new GameView.GamePintuListener() {@Overridepublic void nextLevel(int nextLevel) {new AlertDialog.Builder(MainActivity.this).setTitle("拼图完成").setMessage("美女抱回家").setPositiveButton("下一关", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {gameview.nextLevel();}}).show();}@Overridepublic void timechanged(int time) {}@Overridepublic void gameOver() {}});

这样,我们再来演示一遍

六.记录时间

开发也到了最后了,我们把时间记录给实现了,我们还是的创建几个方法

/*** 是否显示时间*/private void checkTimeEnable() {//如果我们开启了if (isTimeEnabled) {countTimeBaseLevel();handler.sendEmptyMessage(TIME_CHANGED);}}/*** 根据当前等级设置时间*/private void countTimeBaseLevel() {mTime = (int) Math.pow(2, level) * 60;}

然后发送handler

                 case TIME_CHANGED:if (isGameSuccess || isGameOver) {return;}if (mListener != null) {mListener.timechanged(mTime);if (mTime == 0) {isGameOver = true;mListener.gameOver();return;}}mTime--;handler.sendEmptyMessageDelayed(TIME_CHANGED, 1000);break;

现在我们可以去MainActivityity实现逻辑了,这里的gameover逻辑可以这样下

@Overridepublic void gameOver() {new AlertDialog.Builder(MainActivity.this).setTitle("游戏结束").setMessage("很遗憾没有成功抱到美女!").setPositiveButton("重新开始", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {//先不考虑}}).setNegativeButton("退出", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {finish();}}).show();}});

时间

          @Overridepublic void timechanged(int time) {//设置时间tv_time.setText("倒计时:"+time);}

这里要记住设置显示效果

 //显示时间gameview.setTimeEnabled(true);

现在就可以倒计时了,同时,也可以监听到结束了

七.最后的补充

我们gameover以后也是需要操作的,我们有一个重新开始,我们只要写一个初始化的方法就可以了

    /*** 重新开始*/public void restartGame() {isGameOver = false;mColumn--;nextLevel();}

这样就可以了

当然,我们游戏一般都是有暂停的,这个我们也加上,我们在GameView中新建方法

 /*** 暂停*/public void pauseGame() {isPause = true;handler.removeMessages(TIME_CHANGED);}/*** 恢复*/public void resumeGame() {if (isPause) {isPause = false;handler.sendEmptyMessage(TIME_CHANGED);}}

不过我们的目的是他后台时=不记录时间,所以只要和生命周期绑定就可以了

@Overrideprotected void onPause() {super.onPause();gameview.pauseGame();}@Overrideprotected void onResume() {super.onResume();gameview.resumeGame();}

到这里,整个游戏算是正式的开发完整了,贴上完整的代码

MainActivity

package com.lgl.ninegame;import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;import com.lgl.ninegame.view.GameView;public class MainActivity extends AppCompatActivity {private GameView gameview;private TextView tv_level, tv_time;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);tv_level = (TextView) findViewById(R.id.tv_level);tv_time = (TextView) findViewById(R.id.tv_time);gameview = (GameView) findViewById(R.id.gameview);//显示时间gameview.setTimeEnabled(true);gameview.setOnGamemListener(new GameView.GamePintuListener() {@Overridepublic void nextLevel(final int nextLevel) {new AlertDialog.Builder(MainActivity.this).setTitle("拼图完成").setMessage("美女抱回家").setPositiveButton("下一关", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {gameview.nextLevel();tv_level.setText("当前关卡" + nextLevel);}}).show();}@Overridepublic void timechanged(int time) {//设置时间tv_time.setText("倒计时:" + time);}@Overridepublic void gameOver() {new AlertDialog.Builder(MainActivity.this).setTitle("游戏结束").setMessage("很遗憾没有成功抱到美女!").setPositiveButton("重新开始", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {gameview.restartGame();}}).setNegativeButton("退出", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {finish();}}).show();}});}@Overrideprotected void onPause() {super.onPause();gameview.pauseGame();}@Overrideprotected void onResume() {super.onResume();gameview.resumeGame();}
}

GameView

package com.lgl.ninegame.view;import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.Toast;import com.lgl.ninegame.R;
import com.lgl.ninegame.utils.ImagePiece;
import com.lgl.ninegame.utils.ImageSplitterUtil;import java.util.Collections;
import java.util.Comparator;
import java.util.List;/*** 自定义容器* Created by LGL on 2016/5/2.*/
public class GameView extends RelativeLayout implements View.OnClickListener {//默认3*3private int mColumn = 3;//容器的内边距private int mPadding;//小图的距离 dpprivate int mMagin = 3;//存储图片的,宽高 都是固定的,所以使用数组private ImageView[] mGameOintuItems;//宽度private int mItemWidth;//图片private Bitmap mBitmap;//切图后存储private List<ImagePiece> mItemBitmaps;//标记private boolean once;//记录时间private int mTime;//容器的一个宽度private int mWidth;//判断游戏是否成功private boolean isGameSuccess;//是否显示时间private boolean isTimeEnabled = false;/*** 动画层,覆盖在viewGroup中*/private RelativeLayout mAnimLayout;private boolean isGameOver;/*** 动画限制*/private boolean isAniming;private static final int TIME_CHANGED = 10;private static final int NEXT_LEVEL = 11;/*** 设置接口回调** @param mListener*/public void setOnGamemListener(GamePintuListener mListener) {this.mListener = mListener;}public GamePintuListener mListener;/*** 关数*/private int level = 1;/*** 设置开启时间** @param timeEnabled*/public void setTimeEnabled(boolean timeEnabled) {isTimeEnabled = timeEnabled;}//接口public interface GamePintuListener {//关卡void nextLevel(int nextLevel);//时间void timechanged(int time);//游戏结束void gameOver();}//子线程操作private Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case TIME_CHANGED:if (isGameSuccess || isGameOver || isPause) {return;}if (mListener != null) {mListener.timechanged(mTime);if (mTime == 0) {isGameOver = true;mListener.gameOver();return;}}mTime--;handler.sendEmptyMessageDelayed(TIME_CHANGED, 1000);break;case NEXT_LEVEL:level = level + 1;if (mListener != null) {mListener.nextLevel(level);} else {nextLevel();}break;}super.handleMessage(msg);}};public GameView(Context context) {this(context, null);}public GameView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public GameView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}/*** 初始化*/private void init() {//单位转换——dp-pxmMagin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 3, getResources().getDisplayMetrics());mPadding = min(getPaddingLeft(), getPaddingRight(), getPaddingTop(), getPaddingBottom());}/*** 确定当前布局的大小,我们要设置成正方形** @param widthMeasureSpec* @param heightMeasureSpec*/@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);//拿到容器的高宽最小值mWidth = Math.min(getMeasuredHeight(), getMeasuredWidth());if (!once) {//进行切图和排序initBitmap();//设置imageview(item)的宽高等属性initItem();//根据关卡设置时间checkTimeEnable();once = true;}setMeasuredDimension(mWidth, mWidth);}/*** 是否显示时间*/private void checkTimeEnable() {//如果我们开启了if (isTimeEnabled) {countTimeBaseLevel();handler.sendEmptyMessage(TIME_CHANGED);}}/*** 根据当前等级设置时间*/private void countTimeBaseLevel() {mTime = (int) Math.pow(2, level) * 60;}/*** 进行切图和排序*/private void initBitmap() {//判断是否存在这张图片if (mBitmap == null) {mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.img);}//进行裁剪mItemBitmaps = ImageSplitterUtil.splitImage(mBitmap, mColumn);//裁剪玩后需要进行顺序打乱sortCollections.sort(mItemBitmaps, new Comparator<ImagePiece>() {@Overridepublic int compare(ImagePiece lhs, ImagePiece rhs) {//生成随机数,如果》0.5返回1否则返回-1return Math.random() > 0.5 ? 1 : -1;}});}/*** 设置imageview(item)的宽高等属性*/private void initItem() {//( 容器的宽度 - 内边距 * 2  - 间距  ) /  裁剪的数量mItemWidth = (mWidth - mPadding * 2 - mMagin * (mColumn - 1)) / mColumn;//几 * 几mGameOintuItems = new ImageView[mColumn * mColumn];//开始排放for (int i = 0; i < mGameOintuItems.length; i++) {ImageView item = new ImageView(getContext());item.setOnClickListener(this);//设置图片item.setImageBitmap(mItemBitmaps.get(i).getBitmap());//保存mGameOintuItems[i] = item;//设置IDitem.setId(i + 1);//设置Tagitem.setTag(i + "_" + mItemBitmaps.get(i).getIndex());RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(mItemWidth, mItemWidth);//判断不是最后一列if (i + 1 % mColumn != 0) {lp.rightMargin = mMagin;}//判断不是第一列if (i % mColumn != 0) {lp.addRule(RelativeLayout.RIGHT_OF, mGameOintuItems[i - 1].getId());}//判断如果不是第一行if ((i + 1) > mColumn) {lp.topMargin = mMagin;lp.addRule(RelativeLayout.BELOW, mGameOintuItems[i - mColumn].getId());}addView(item, lp);}}/*** 获取多个参数的最小值*/private int min(int... params) {int min = params[0];//遍历for (int param : params) {if (param < min) {min = param;}}return min;}/*** 点击的第一张图和第二张图,他们进行交换*/private ImageView mFirst;private ImageView mSecond;/*** 点击事件** @param v*/@Overridepublic void onClick(View v) {//如果点击了一次,你还点击,则无效if (isAniming) {return;}//重复点击if (mFirst == v) {//去掉颜色mFirst.setColorFilter(null);mFirst = null;return;}if (mFirst == null) {mFirst = (ImageView) v;//设置选中效果mFirst.setColorFilter(Color.parseColor("#55FF0000"));//第二次点击} else {mSecond = (ImageView) v;//交换exchangeView();}}/*** 图片交换*/private void exchangeView() {mFirst.setColorFilter(null);// 构造我们的动画层setUpAnimLayout();ImageView first = new ImageView(getContext());final Bitmap firstBitmap = mItemBitmaps.get(getImageIdByTag((String) mFirst.getTag())).getBitmap();first.setImageBitmap(firstBitmap);LayoutParams lp = new LayoutParams(mItemWidth, mItemWidth);lp.leftMargin = mFirst.getLeft() - mPadding;lp.topMargin = mFirst.getTop() - mPadding;first.setLayoutParams(lp);mAnimLayout.addView(first);ImageView second = new ImageView(getContext());final Bitmap secondBitmap = mItemBitmaps.get(getImageIdByTag((String) mSecond.getTag())).getBitmap();second.setImageBitmap(secondBitmap);LayoutParams lp2 = new LayoutParams(mItemWidth, mItemWidth);lp2.leftMargin = mSecond.getLeft() - mPadding;lp2.topMargin = mSecond.getTop() - mPadding;second.setLayoutParams(lp2);mAnimLayout.addView(second);// 设置动画TranslateAnimation anim = new TranslateAnimation(0, mSecond.getLeft()- mFirst.getLeft(), 0, mSecond.getTop() - mFirst.getTop());anim.setDuration(300);anim.setFillAfter(true);first.startAnimation(anim);TranslateAnimation animSecond = new TranslateAnimation(0,-mSecond.getLeft() + mFirst.getLeft(), 0, -mSecond.getTop()+ mFirst.getTop());animSecond.setDuration(300);animSecond.setFillAfter(true);second.startAnimation(animSecond);// 监听动画anim.setAnimationListener(new Animation.AnimationListener() {@Overridepublic void onAnimationStart(Animation animation) {mFirst.setVisibility(View.INVISIBLE);mSecond.setVisibility(View.INVISIBLE);isAniming = true;}@Overridepublic void onAnimationRepeat(Animation animation) {// TODO Auto-generated method stub}@Overridepublic void onAnimationEnd(Animation animation) {String firstTag = (String) mFirst.getTag();String secondTag = (String) mSecond.getTag();mFirst.setImageBitmap(secondBitmap);mSecond.setImageBitmap(firstBitmap);mFirst.setTag(secondTag);mSecond.setTag(firstTag);mFirst.setVisibility(View.VISIBLE);mSecond.setVisibility(View.VISIBLE);mFirst = mSecond = null;mAnimLayout.removeAllViews();//每次移动完成判断是否过关checkSuccess();isAniming = false;}});}/*** 判断是否过关*/private void checkSuccess() {boolean isSuccess = true;for (int i = 0; i < mGameOintuItems.length; i++) {//拿到所有的图片ImageView imageView = mGameOintuItems[i];if (getImageIndex((String) imageView.getTag()) != i) {isSuccess = false;}}if (isSuccess) {isGameSuccess = true;handler.removeMessages(TIME_CHANGED);Log.i("tag", "成功");Toast.makeText(getContext(), "成功,进入下一关!", Toast.LENGTH_LONG).show();handler.sendEmptyMessage(NEXT_LEVEL);}}/*** 获取tag** @param tag* @return*/public int getImageIdByTag(String tag) {String[] split = tag.split("_");return Integer.parseInt(split[0]);}/*** 获取图片的tag** @param tag* @return*/public int getImageIndex(String tag) {String[] split = tag.split("_");return Integer.parseInt(split[1]);}/*** 交互动画*/private void setUpAnimLayout() {if (mAnimLayout == null) {mAnimLayout = new RelativeLayout(getContext());//添加到整体addView(mAnimLayout);}}/*** 下一关*/public void nextLevel() {this.removeAllViews();mAnimLayout = null;mColumn++;isGameSuccess = false;checkTimeEnable();initBitmap();initItem();}/*** 重新开始*/public void restartGame() {isGameOver = false;mColumn--;nextLevel();}//暂停状态private boolean isPause;/*** 暂停*/public void pauseGame() {isPause = true;handler.removeMessages(TIME_CHANGED);}/*** 恢复*/public void resumeGame() {if (isPause) {isPause = false;handler.sendEmptyMessage(TIME_CHANGED);}}
}

我们最终的运行结果

笔记下载地址:http://pan.baidu.com/s/1c0U7k2W 密码:9v0g

Demo下载:http://download.csdn.net/detail/qq_26787115/9509305

我的群,通往Android的神奇之旅 :555974449,欢迎大家进来交流技术!

Android拼图游戏的设计逻辑,从切图到交互动画,从关卡到倒计时,实例提高!相关推荐

  1. Android拼图游戏设计(包括游戏算法及数据库设计)

    拼图游戏的设计可以分为如下几个部分:① UI设计:② 事件监听,事件处理:③ 游戏逻辑. 用户首先进入登陆注册界面,在登录注册模块可以实现注册登录功能,同时实现修改密码和注销用户的功能,这一功能模块需 ...

  2. 基于Java实现的Android拼图游戏设计

    资源下载地址:https://download.csdn.net/download/sheziqiong/85638665 基于Java实现的Android拼图游戏设计 游戏效果 一.Android ...

  3. 拼图游戏的设计与实现

    拼图游戏的设计与实现 功能要求: 使用HTML5画布技术制作一款拼图小游戏,要求将图像划分为3*3的9块方块并打乱排序,用户可以移动方块拼成完整图片. 实现效果图: 一.界面设计 1.整体布局设计 1 ...

  4. java课程设计拼图_java拼图游戏课程设计报告

    java拼图游戏课程设计报告 砾寸椒涩藕矾糯陋捕炬洁困喘港划舟逃豺涌锤芳喜胺递龚乏埔跺摩实阿信颊立蹲稿船纽臃瘪自康嘱脖究绢术拱虑犹犀棉宜炙转鸦半甘哨疗墓暑蛊渤幽峭咀豺虫拘召饭莽畜穗篷姿钟逻捞跨瀑拿丈土 ...

  5. 微信小程序(游戏)----拼图游戏(设计思路)

    微信小程序(游戏)----拼图游戏(设计思路) 设计思路 1.将一张海报等分成 N*N 的矩阵 方法一:利用两个组件循环完成,view组件和image组件,view组件作为盒子规定大小--超出部分不显 ...

  6. Android 拼图游戏知识点小结

    前几天在 慕课网 学习了 Android 拼图游戏小项目.对于我这种初学者来说,想完全搞懂,还是有不少困难的,但通过此项目还是学到了挺多东西.http://blog.csdn.net/lmj62356 ...

  7. java课程设计拼图_基于Java拼图游戏的设计与实现(含录像)

    基于拼图游戏的设计与实现(含录像) 摘    要 本拼图游戏是基于J2SE平台开发的,它是一个Application,它的游戏规则和诺亚舟里的拼图游戏是一样的.这个游戏将一张大图切割成N张小图,然后在 ...

  8. android 拼图游戏2(可从手机选择任意一张图片)

    在原有的基础上添加可选择手机任意一张图片. 1.在MainActivity.java代码中添加多了一个按钮可实现选择手机照片确认返回.在onActivityResult里面添加 gamePintuLa ...

  9. Java拼图游戏总结,Java拼图游戏课程设计报告

    Java拼图游戏课程设计报告 JavaJava 程序设计与应用开发 课程设计报告程序设计与应用开发 课程设计报告 设计题目 拼图大作战 学生姓名 学生班级 学生学号 指导教师 完成时间2016 年 0 ...

最新文章

  1. Python使用numpy中trim_zeros函数去除首尾0值的语法
  2. 未来5年网络安全支出将达到1万亿
  3. Windows启动过程
  4. idea怎么提交到dev分支_IDEA设置git提交分支
  5. convert.todatetime指定日期格式_SQL基础知识V2——常用日期函数
  6. 防止Linux库so中的接口冲突
  7. gpt efi win7 linux,科学网—UEFI+GPTSSD+HDD 成功安装win10+Centos linux7 过程 - 陈长云的博文...
  8. 建筑建模学习笔记2——3DMax房屋框架建模
  9. vscode php插件_「PHP从入门到颈椎病康复」基础篇——HelloWorld
  10. java word转pdf 在linux转pdf乱码解决方法
  11. java防止SQL注入
  12. Android修炼之道—自定义控件 (300喵喵币)
  13. java 区号_区号查询示例代码
  14. 手机无线投屏到linux电脑,scrcpy - 手机无线投屏到电脑
  15. 苹果电脑系统重装 —— U盘操作
  16. 自动驾驶路径规划——A*(Astar)算法
  17. 二维码制作生成器有哪些?分享几个二维码制作生成器
  18. 用大白菜装centos7_大白菜安装centos7 踩坑记
  19. 如何让网站在浏览器网址前面显示小图标?ico图标怎么放?
  20. 美国各州格言,大家可以看看什么词最多

热门文章

  1. 【Pygame实战】好“球”推荐——“足”出精彩,追“球”健康,足不出户在家也能“踢足球”啦~
  2. 街篮服务器维护比赛没打完,约战《街篮》争霸赛 季前删测不日开启
  3. 用广告配音器如何给广告进行配音
  4. zabbix 监控服务器CPU
  5. 保研经验分享 || 西北高考照顾省大学生如何大一绩点崩掉最后专业第七保研上岸C9、华五
  6. css图片居中(水平居中和垂直居中)
  7. python win32con键位表_关于pywin32的完整键位码表
  8. quotename用法
  9. SpringCloud进阶-Hystrix的服务降级
  10. 一加手机将与 OPPO 全面融合