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*3    private int mColumn = 3;    //容器的内边距    private int mPadding;    //小图的距离 dp    private 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-px        mMagin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 3, getResources().getDisplayMetrics());        mPadding = min(getPaddingLeft(), getPaddingRight(), getPaddingTop(), getPaddingBottom());    }    /** * 确定当前布局的大小,我们要设置成正方形 * * @param widthMeasureSpec * @param heightMeasureSpec */    @Override    protected 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);        //裁剪玩后需要进行顺序打乱sort        Collections.sort(mItemBitmaps, new Comparator<ImagePiece>() {            @Override            public int compare(ImagePiece lhs, ImagePiece rhs) {                //生成随机数,如果》0.5返回1否则返回-1                return 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;            //设置ID            item.setId(i + 1);            //设置Tag            item.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 */    @Override    public 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 */    @Override    public 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没改,我们在回到最初始的状态之前加上以下代码:

//改变tag mFirst.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()        {            @Override            public void onAnimationStart(Animation animation)            {                mFirst.setVisibility(View.INVISIBLE);                mSecond.setVisibility(View.INVISIBLE);            }            @Override            public void onAnimationRepeat(Animation animation)            {                // TODO Auto-generated method stub            }            @Override            public 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() {        @Override        public 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() {            @Override            public void nextLevel(int nextLevel) {                new AlertDialog.Builder(MainActivity.this).setTitle("拼图完成").setMessage("美女抱回家").setPositiveButton("下一关", new DialogInterface.OnClickListener() {                    @Override                    public void onClick(DialogInterface dialog, int which) {                        gameview.nextLevel();                    }                }).show();            }            @Override            public void timechanged(int time) {            }            @Override            public 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逻辑可以这样下

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

时间

          @Override            public 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);        }    }

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

    @Override    protected void onPause() {        super.onPause();        gameview.pauseGame();    }    @Override    protected 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;    @Override    protected 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() {            @Override            public void nextLevel(final int nextLevel) {                new AlertDialog.Builder(MainActivity.this).setTitle("拼图完成").setMessage("美女抱回家").setPositiveButton("下一关", new DialogInterface.OnClickListener() {                    @Override                    public void onClick(DialogInterface dialog, int which) {                        gameview.nextLevel();                        tv_level.setText("当前关卡" + nextLevel);                    }                }).show();            }            @Override            public void timechanged(int time) {                //设置时间                tv_time.setText("倒计时:" + time);            }            @Override            public void gameOver() {                new AlertDialog.Builder(MainActivity.this).setTitle("游戏结束").setMessage("很遗憾没有成功抱到美女!").setPositiveButton("重新开始", new DialogInterface.OnClickListener() {                    @Override                    public void onClick(DialogInterface dialog, int which) {                        gameview.restartGame();                    }                }).setNegativeButton("退出", new DialogInterface.OnClickListener() {                    @Override                    public void onClick(DialogInterface dialog, int which) {                        finish();                    }                }).show();            }        });    }    @Override    protected void onPause() {        super.onPause();        gameview.pauseGame();    }    @Override    protected 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*3    private int mColumn = 3;    //容器的内边距    private int mPadding;    //小图的距离 dp    private 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() {        @Override        public 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-px        mMagin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 3, getResources().getDisplayMetrics());        mPadding = min(getPaddingLeft(), getPaddingRight(), getPaddingTop(), getPaddingBottom());    }    /** * 确定当前布局的大小,我们要设置成正方形 * * @param widthMeasureSpec * @param heightMeasureSpec */    @Override    protected 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);        //裁剪玩后需要进行顺序打乱sort        Collections.sort(mItemBitmaps, new Comparator<ImagePiece>() {            @Override            public int compare(ImagePiece lhs, ImagePiece rhs) {                //生成随机数,如果》0.5返回1否则返回-1                return 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;            //设置ID            item.setId(i + 1);            //设置Tag            item.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 */    @Override    public 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() {            @Override            public void onAnimationStart(Animation animation) {                mFirst.setVisibility(View.INVISIBLE);                mSecond.setVisibility(View.INVISIBLE);                isAniming = true;            }            @Override            public void onAnimationRepeat(Animation animation) {                // TODO Auto-generated method stub            }            @Override            public 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,欢迎大家进来交流技术!

更多相关文章

  1. android的照相图片压缩
  2. Android:如何设置底部控件view随着软键盘的弹出而上移——诺诺"涂
  3. Android中读取系统图库(包含相册)中的图片,显示图片与图片的路径
  4. Android跳转系统界面_大全集
  5. 【移动开发】Android图片异步加载之Android-Universal-Image-Loa
  6. android 帧动画的替代方案
  7. android处理拍照旋转问题及带来的对内存占用的思考
  8. Android(安卓)酷炫来袭:制作属于你自己的音频播放器(综合运用Media
  9. Android用AsyncTask来下载图片及用AsyncTask的好处

随机推荐

  1. Drawable分类
  2. adb 无线调试
  3. Android的广播机制——Broadcast Reciver
  4. Android实现视频播放的3种实现方式
  5. 使用 Android 实现联网
  6. android中常用布局
  7. Android 秒级编译方案-Freeline安装使用
  8. Android(安卓)adapter中调用activity中的
  9. android基于tcpdump的数据包捕获完整解决
  10. Android适配(屏幕适配、国际化适配)