视频地址: Android美女拼图小游戏

实现功能:

(1)多个难度

第一关3*3。

(2)倒计时

(3)图片切分

(4)图片位置变换

第一步:

在src下创建工具包com.imooc.game.utils,创建两个工具类分别为ImagePiece.java和ImageSplitterUtil.java:

package com.imooc.game.utils;import android.graphics.Bitmap;/*** 用来存储每一张图片。图片碎片* */
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;}/** 这个是每个类里可以重写的。* */@Overridepublic String toString(){return "ImagePiece [index=" + index + ", bitmap=" + bitmap + "]";}}
package com.imooc.game.utils;import java.util.ArrayList;
import java.util.List;import android.graphics.Bitmap;public class ImageSplitterUtil
{/*** 传入bitmap,切成piece*piece块,返回的是这个List列表。* @param bitmap* @param piece               * @return List<ImagePiece>*/public static List<ImagePiece> splitImage(Bitmap bitmap, int piece){List<ImagePiece> imagePieces = new ArrayList<ImagePiece>();// 获取图片宽高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();// i表示行,j表示列,得到的数字就是1,2,3,4,5,6,7。。。imagePiece.setIndex(j + i * piece);int x = j * pieceWidth;int y = i * pieceWidth;// 第二三个参数是顶点坐标,第四五个参数是宽高。imagePiece.setBitmap(Bitmap.createBitmap(bitmap, x, y,pieceWidth, pieceWidth));imagePieces.add(imagePiece);}}return imagePieces;}}

第二步:GamePintuLayout类:

package com.imooc.game.pintu.view;import java.util.Collections;
import java.util.Comparator;
import java.util.List;import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.TypedValue;
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.ImageView;
import android.widget.RelativeLayout;
import android.widget.Toast;import com.imooc.game.pintu.R;
import com.imooc.game.utils.ImagePiece;
import com.imooc.game.utils.ImageSplitterUtil;/** 用RelativeLayout比较容易控制位置。* **/
public class GamePintuLayout extends RelativeLayout implements OnClickListener
{// 默认3*3的碎片private int mColumn = 3;// 容器的内边距private int mPadding;// 每张小图之间的距离,横纵。dpprivate int mMargin = 3;// 每一张小图位置上的那个viewprivate ImageView[] mGamePintuItems;// 每一张小图的宽度。private int mItemWidth;// 游戏的图片。private Bitmap mBitmap;// 存放所有的图片碎片的list。private List<ImagePiece> mItemBitmaps;// 操作一次的标识。private boolean once;// 游戏面板的宽度,容器的宽度private int mWidth;private boolean isGameSuccess;private boolean isGameOver;// 设置接口通知MainActivity进行回调。这三个函数是在MainActivity中进行实现的。这里提供的只是一个接口。public interface GamePintuListener{void nextLevel(int nextLevel);void timechanged(int currentTime);void gameover();}// 接口成员变量。public GamePintuListener mListener;/*** 设置接口回调* * @param mListener*/public void setOnGamePintuListener(GamePintuListener mListener){this.mListener = mListener;}private int mLevel = 1;private static final int TIME_CHANGED = 0x110;private static final int NEXT_LEVEL = 0x111;/** 进行UI操作。* */private Handler mHandler = new Handler(){public void handleMessage(android.os.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--;// 每一秒发送一次减少。mHandler.sendEmptyMessageDelayed(TIME_CHANGED, 1000);break;case NEXT_LEVEL:mLevel = mLevel + 1;// 这个是判断用户是否选择进行下一关。if (mListener != null){mListener.nextLevel(mLevel);} else{nextLevel();}break;}};};private boolean isTimeEnabled = false;private int mTime;/*** 设置是否开启时间* * @param isTimeEnabled*/public void setTimeEnabled(boolean isTimeEnabled){this.isTimeEnabled = isTimeEnabled;}/** 第一个构造方法调用第二个构造方法。* */public GamePintuLayout(Context context){this(context, null);}/** 第二个构造方法调用第三个构造方法。* */public GamePintuLayout(Context context, AttributeSet attrs){this(context, attrs, 0);}/** 第三个构造方法进行初始化。* */public GamePintuLayout(Context context, AttributeSet attrs, int defStyle){super(context, attrs, defStyle);init();}private void init(){/** 可以用TypedValue进行单位的转换,将dp转为px,或者将sp转为px* 这样也就把我们3dp转化为3px的值。* 在布局当中尽可能的所有的字体使用sp,所有的merage使用dp,一般不使用px,因为不同的屏幕上分辨率是不同的。* */mMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,3, getResources().getDisplayMetrics());// 保证上下左右边距相同的,取最小值mPadding = min(getPaddingLeft(), getPaddingRight(), getPaddingTop(),getPaddingBottom());}/** 设置当前布局的大小* */@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){super.onMeasure(widthMeasureSpec, heightMeasureSpec);// 取容器宽高的最小值。mWidth = Math.min(getMeasuredHeight(), getMeasuredWidth());// 如果once没有发生过。if (!once){// 进行切图以及排序。initBitmap();// 设置ImageView(Item)的宽高等属性。initItem();// 判断是否开启时间。checkTimeEnable();once = true;}// 重置它占据的位置是一个正方形。setMeasuredDimension(mWidth, mWidth);}/*** 判断是否开启时间。* */private void checkTimeEnable(){// 如果开启了,if (isTimeEnabled){// 根据关卡设置时间长短。countTimeBaseLevel();// 通知主界面显示时间。mHandler.sendEmptyMessage(TIME_CHANGED);}}// 根据关卡设置时间长短。private void countTimeBaseLevel(){// 用pow可以根据游戏难度增加时间,2的mLevel次,指数增长。mTime = (int) Math.pow(2, mLevel) * 60;}/*** 进行切图,已经排序。*/private void initBitmap(){if (mBitmap == null){// 获取Bitmap图片对象mBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.image);}// 进行切割后返回ImagePiece的List。mItemBitmaps = ImageSplitterUtil.splitImage(mBitmap, mColumn);// 使用sort乱序排序图片。重写compare方法。Collections.sort(mItemBitmaps, new Comparator<ImagePiece>(){@Overridepublic int compare(ImagePiece a, ImagePiece b){// 使用随机数。return Math.random() > 0.5 ? 1 : -1;}});}/*** 设置ImageView(Item)的宽高等属性。*/private void initItem(){// 获取item宽度。(容器宽度-最左右的空隙-中间图片之间的空隙)/ 一行Item的数目。mItemWidth = (mWidth - mPadding * 2 - mMargin * (mColumn - 1))/ mColumn;// 这个大小是随程序的mColumn动态创建的。多个ImageView。mGamePintuItems = new ImageView[mColumn * mColumn];// 生成Item,设置Rule。for (int i = 0; i < mGamePintuItems.length; i++){// 生产一个一个的ImageView。mItemBitmaps是bitmap的ListImageView item = new ImageView(getContext());item.setOnClickListener(this);item.setImageBitmap(mItemBitmaps.get(i).getBitmap());mGamePintuItems[i] = item;item.setId(i + 1);// 在item的tag中存储了index,index存储的是真正的顺序。item.setTag(i + "_" + mItemBitmaps.get(i).getIndex());/*** 排列顺序* 0 1 2 3* 4 5 6 7* 8 9 10 11* */RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(mItemWidth, mItemWidth);// 不是最后一列的列设置右边距,通过rightMarginif ((i + 1) % mColumn != 0){lp.rightMargin = mMargin;}// 不是第一列if (i % mColumn != 0){// 就是设置它是在谁的右边排列,1 rightof 0lp.addRule(RelativeLayout.RIGHT_OF,mGamePintuItems[i - 1].getId());}// 如果不是第一行,就要设置上边距,同时要设置它在谁的下面。if ((i + 1) > mColumn){lp.topMargin = mMargin;lp.addRule(RelativeLayout.BELOW,mGamePintuItems[i - mColumn].getId());}// 给这个item设置它的参数。addView(item, lp);}// for}public void restart(){isGameOver = false;mColumn--;nextLevel();}private boolean isPause ; public void pause(){isPause = true ; mHandler.removeMessages(TIME_CHANGED);}public void resume(){if(isPause){isPause = false ;mHandler.sendEmptyMessage(TIME_CHANGED);}}/*** 下一关要做的事。* */public void nextLevel(){// 取消当前容器中的所有viewthis.removeAllViews();// 取消动画层mAnimLayout = null;// 图片碎片变复杂。mColumn++;isGameSuccess = false;checkTimeEnable();// 重新显示新的图片。initBitmap();initItem();}/*** 取多个参数的最小值。*/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;@Overridepublic void onClick(View v){if (isAniming)return;// 如果两次点击了相同的图片,代表取消第一个点击,也就去掉点中状态。if (mFirst == v){mFirst.setColorFilter(null);mFirst = null;return;}// 如果第一张null,说明此时点的这张图片是第一张图片,否则是第二张图片。if (mFirst == null){mFirst = (ImageView) v;// 点中后有一个被点中的状态,这里设置透明的红色。55代表透明度,后面是八位颜色。mFirst.setColorFilter(Color.parseColor("#55FF0000"));} else{mSecond = (ImageView) v;// 交换item。exchangeView();}}/*** 动画层*/private RelativeLayout mAnimLayout;// 正在动画的时候,就不让用户乱点一通。private boolean isAniming;/*** 交换item*/private void exchangeView(){// 先去掉点中状态。mFirst.setColorFilter(null);// 准备动画层setUpAnimLayout();// 复制我们点中的图片。ImageView first = new ImageView(getContext());// mItemBitmaps是存放所有的图片碎片的list,它是被乱序排过的。final Bitmap firstBitmap = mItemBitmaps.get(getImageIdByTag((String) mFirst.getTag())).getBitmap();first.setImageBitmap(firstBitmap);// 要看看是不是RelativeLayout的LayoutParam,或者就是直接写出Relative前缀来。LayoutParams lp = new LayoutParams(mItemWidth, mItemWidth);// 减去mPadding是因为,对于底下原本的那层,它的左边是有边距的,// 而对于上面的动画层,它的整个大小是没有覆盖下面的边距的,这样它里面的元素也就不需要考虑下面的它说拥有的paddinglp.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。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 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();// 交换bitmap和tagmFirst.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 < mGamePintuItems.length; i++){ImageView imageView = mGamePintuItems[i];if (getImageIndexByTag((String) imageView.getTag()) != i){isSuccess = false;}}if (isSuccess){// 当前一次游戏结束以后,把上一次的Handler动作取消掉。isGameSuccess = true;mHandler.removeMessages(TIME_CHANGED);Toast.makeText(getContext(), "Success,level up !!!",Toast.LENGTH_LONG).show();mHandler.sendEmptyMessage(NEXT_LEVEL);}}/*** 通过tag获取image的id,也就是当初设置的乱排序以后对应的i值。* tag中包含i和index,split[0]就是i。* @param tag* @return*/public int getImageIdByTag(String tag){String[] split = tag.split("_");return Integer.parseInt(split[0]);}/*** 根据tag获取index。* */public int getImageIndexByTag(String tag){String[] split = tag.split("_");return Integer.parseInt(split[1]);}/*** 构造动画层。*/private void setUpAnimLayout(){if (mAnimLayout == null){mAnimLayout = new RelativeLayout(getContext());// 加到面板之中。addView(mAnimLayout);}}}

第三步:

package com.imooc.game.pintu;import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.os.Bundle;
import android.widget.TextView;import com.imooc.game.pintu.view.GamePintuLayout;
import com.imooc.game.pintu.view.GamePintuLayout.GamePintuListener;public class MainActivity extends Activity
{private GamePintuLayout mGamePintuLayout;private TextView mLevel ; private TextView mTime;@Overrideprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mTime = (TextView) findViewById(R.id.id_time);mLevel = (TextView) findViewById(R.id.id_level);mGamePintuLayout = (GamePintuLayout) findViewById(R.id.id_gamepintu);// 这个这个mGamePintuLayout.setTimeEnabled(true);// 哇,这个是我们自己写的。mGamePintuLayout.setOnGamePintuListener(new GamePintuListener(){@Overridepublic void timechanged(int currentTime){// 转为字符串的形式mTime.setText(""+currentTime);}@Overridepublic void nextLevel(final int nextLevel){// 到下一关的时候弹出dialog框让用户选择是否进行下一关。new AlertDialog.Builder(MainActivity.this).setTitle("Game Info").setMessage("LEVEL UP !!!").setPositiveButton("NEXT LEVEL", new OnClickListener(){@Overridepublic void onClick(DialogInterface dialog,int which){mGamePintuLayout.nextLevel();// 转为字符串的形式。mLevel.setText(""+nextLevel);}}).show();}@Overridepublic void gameover(){new AlertDialog.Builder(MainActivity.this).setTitle("Game Info").setMessage("Game over !!!").setPositiveButton("RESTART", new OnClickListener(){@Overridepublic void onClick(DialogInterface dialog,int which){mGamePintuLayout.restart();}}).setNegativeButton("QUIT",new OnClickListener(){@Overridepublic void onClick(DialogInterface dialog, int which){finish();}}).show();}});}@Overrideprotected void onPause(){super.onPause();mGamePintuLayout.pause();}@Overrideprotected void onResume(){super.onResume();mGamePintuLayout.resume();}}

第四步: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" ><com.imooc.game.pintu.view.GamePintuLayoutandroid:id="@+id/id_gamepintu"android:layout_width="fill_parent"android:layout_height="fill_parent"android:layout_centerInParent="true"android:padding="3dp" /><RelativeLayoutandroid:layout_width="fill_parent"android:layout_height="wrap_content"android:layout_above="@id/id_gamepintu"android:layout_margin="3dp" ><TextViewandroid:id="@+id/id_level"android:layout_width="70dp"android:layout_height="70dp"android:background="@drawable/textbg"android:gravity="center"android:padding="4dp"android:text="1"android:textColor="#EA7821"android:textSize="30sp"android:textStyle="bold" /><TextViewandroid:id="@+id/id_time"android:layout_width="70dp"android:layout_height="70dp"android:layout_alignParentRight="true"android:background="@drawable/textbg"android:gravity="center"android:padding="4dp"android:text="120"android:textColor="#EA7821"android:textSize="30sp"android:textStyle="bold" /></RelativeLayout></RelativeLayout>

其中的textbg.xml文件放在drawable文件下:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"android:shape="oval" ><strokeandroid:width="2px"android:color="#1579DB" /><solid android:color="#B4CDE6" /></shape>

Android案例(1)——美女拼图小游戏相关推荐

  1. android 美女拼图小游戏

    经过这十天的学习,感慨有许多 在第一天的实训中,了解到了两种容器:RelativeLayout(相对布局容器)和LinearLayout(线性布局容器),他们之间可以相互放,即容器能够存放容器,不过一 ...

  2. android实现九宫格拼图小游戏

    贴一下效果图 接下来随便用一张图片就好 以下是全代码 自定义View GameView类 import android.content.Context; import android.graphics ...

  3. android移动拼图小游戏的图片,利用ViewDragHelper轻松实现Android拼图游戏

    前言 最近一段时间看了一些介绍ViewDragHelper的博客,感觉这是一个处理手势滑动的神器,看完以后就想做点东西练练手,于是就做了这个Android拼图小游戏. 先上个效果图 demo.gif ...

  4. android自定义游戏闯关图,Android开发之儿时的回忆——拼图小游戏

    会写这篇文章完全是由于巧合,前几天路过天桥下的路边摊发现一个很熟悉的"老朋友",想必大家小时候也玩过这种滑块拼图吧. 儿时的印象--滑块拼图 哈哈,暴露年龄的东西,刚开始觉得很惊喜 ...

  5. Android—拼图小游戏

    拼图小游戏 icon: 选择界面: 两个难度界面: 成功界面: 然后 这个是跟着视频教程写的 也是第一次写关于图片分割和动画的这些 大概步骤是: 1.初始化游戏主界面和方块 2.设置某个方块为缺口方块 ...

  6. jQuery实现拼图小游戏(实操案例)

    先看初始效果图: 方法一: var id = $(this).prop("id"); //得到当前点击的这个td的ID if (parseInt(id) + 3 < 10 & ...

  7. JavaScript版拼图小游戏

    慕课网上准备开个新的jQuery教程,花了3天空闲时间写了一个Javascript版的拼图小游戏,作为新教程配套的分析案例 拼图游戏网上有不少的实现案例了,但是此源码是我自己的实现,所以不做太多的比较 ...

  8. JAVA练习题37:拼图小游戏

    第一个小项目:拼图小游戏 暂不完善,因为没有链接数据库,用户名和密码只是暂存在集合中 资料 图片资料 整个项目源码 界面展示 代码: 一.主程序(入口)App import com.qi.ui.Gam ...

  9. python设计拼图小游戏_教你用Python自制拼图小游戏,轻松搞定熊孩子

    摘要:本文主要为大家详细介绍了python实现拼图小游戏,文中还有示例代码介绍,感兴趣的小伙伴们可以参考一下. 开发工具 Python版本:3.6.4 相关模块: pygame模块: 以及一些Pyth ...

最新文章

  1. C#从服务器下载文件到客户端源码
  2. 1032. 挖掘机技术哪家强(20)
  3. Javascript 解析,格式化日期
  4. 初识 scrapy 框架 - 安装
  5. matlab绘制贝叶斯曲线,Matlab建立SVM,KNN和朴素贝叶斯模型分类绘制ROC曲线
  6. Java异常处理 一
  7. html中元素盒子垂直居中的实现方法
  8. 研华GPIO的功能实现代码
  9. C++中如何读取一个数的位数_初中数学:近似数和平均数知识点总结及练习
  10. Charles(网络封包分析工具)
  11. 微星P55-主板是怎样造出来的
  12. MATLAB r2016a 配置 VLFeat 库教程(超详细,一看必会)
  13. 《软件工程导论》第一章
  14. 3、在hilens_kit安装ros后,运行小车导航
  15. MySQL数据库(15):高级数据操作-新增数据
  16. [Js] Js实现继承的5种方式
  17. notepad打开java乱码_notepad打开中文乱码
  18. 请尝试将 `lib` 编译器选项更改为 es2015 或更高版本
  19. 苹果Mac安全使用教程:如何使用文件保险箱加密Mac数据
  20. 石川:出色不如走运 (III)?

热门文章

  1. 杰里之69 系列规格篇
  2. 史蒂芬柯维经典之作《第三选择》听后感和思维导图
  3. 分角色软件系统流程图
  4. ArcGIS给栅格数据建立属性表
  5. oracle查看sql命中率,关于Oracle检查命中率的SQL
  6. 125%屏幕缩放比,软件很糊解决方法
  7. 调节e18-d80nk的测量距离_经纬仪施工测量详细使用教程,比课本还具体!比老师教的还详细...
  8. 5G通信面临的困难挑战及解决方案有哪些?
  9. LinkedIn技巧-领英怎么只给选择的好友群发消息?
  10. 轻松理解20种最常用的AI算法