Android案例(1)——美女拼图小游戏

来源:互联网 发布:出知益州 时李顺构乱 编辑:程序博客网 时间:2024/05/24 01:37

视频地址: 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.GamePintuLayout        android:id="@+id/id_gamepintu"        android:layout_width="fill_parent"        android:layout_height="fill_parent"        android:layout_centerInParent="true"        android:padding="3dp" />    <RelativeLayout        android:layout_width="fill_parent"        android:layout_height="wrap_content"        android:layout_above="@id/id_gamepintu"        android:layout_margin="3dp" >        <TextView            android: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" />        <TextView            android: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" >    <stroke        android:width="2px"        android:color="#1579DB" />    <solid android:color="#B4CDE6" /></shape>

















0 0
原创粉丝点击