拼图游戏-从基础到应用玩转手势变化。

      • 小方块相关的类
      • 主界面的布局
      • 打开图片选择图片
      • 拼图的各个小方块的形成过程
      • 小方块的点击事件和手势判断过程
      • 游戏开始打乱方块以及游戏结束时弹出Toast提示的方法

相信大家在小的时候都玩过拼图游戏,现如今,手机普及,能在手机上玩的游戏越来越多,于是乎,重温小时候,编写这个简易拼图游戏,而且也能进一步加深android的一些基础知识。

老规矩,先是效果图~:

这里我把为了演示效果,把图片打乱的很少,在代码里可以更改。

首先,有个默认的图片,可以用来拼图,也可以选择你喜欢的图片进行拼图,拼图的过程会记录移动的步数,并且当游戏胜利的时候会弹出一个笑脸提示,游戏胜利,用了多少步数。

ps:感兴趣的完全可以继续在这上面进行扩展,比如增加游戏难度的选项,可以将图片分成更多的小方块等等。

大体思路:将大图切割成各个小方块,用数组记录每个小方块的信息,用GridLayout来显示各个小方块,并且将某个小方块标记为空白方块(空白方块可以和相邻方块进行交换),在GridLayout上的各个小方块上增加点击事件和在整个屏幕上添加手势事件,每次点击或者有手势时,判断小方块是否能移动,最后在游戏胜利时弹出胜利提示。

话不多说,接下来,就是一步步实现拼图游戏的过程啦~

1.小方块相关的类。

这是小方块的各种变量的item,类,用来管理将大图切割成每个小方块的每个小方块的信息。很简单,就是各种变量和Setter和Getter方法直接上代码~

/**
 * Created by yyh on 2016/10/21.
 */
public class GameItemView{
    /**
     * 每个小方块的信息
     */
    //每个小方块的实际位置x,
    private int x=0;
    //每个小方块的实际位置y,
    private int y=0;
    //每个小方块的图片,
    private Bitmap bm;
    //每个小方块的图片位置x,
    private int p_x=0;
    //每个小方块的图片位置y.
    private int p_y=0;
    public GameItemView(int x, int y, Bitmap bm) {
        super();
        this.x = x;
        this.y = y;
        this.bm = bm;
        this.p_x=x;
        this.p_y=y;
    }
    public int getX() {
        return x;
    }
    public void setX(int x) {
        this.x = x;
    }
    public int getY() {
        return y;
    }

    public Bitmap getBm() {
        return bm;
    }

    public void setBm(Bitmap bm) {
        this.bm = bm;
    }

    public int getP_x() {
        return p_x;
    }

    public void setP_x(int p_x) {
        this.p_x = p_x;
    }

    public int getP_y() {
        return p_y;
    }

    public void setP_y(int p_y) {
        this.p_y = p_y;
    }
    /**
     * 判断每个小方块的位置是否正确
     * @return
     */
    public boolean isTrue(){
        if (x==p_x&&y==p_y){
            return true;
        }
        return false;
    }
}

2.主界面的布局

主界面很简单,一个Button,用来换图片,一个ImageView,用来显示原图,一个GridLayout用来进行拼图游戏,最后,一个TextView,用来显示完成这个拼图用了多少步数。布局如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
   >
    <LinearLayout
        android:id="@+id/ll"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        >
        <Button
            android:id="@+id/bt_choice"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="选择图片"
            android:adjustViewBounds="true"
            />

    </LinearLayout>

    <ImageView
        android:id="@+id/iv"
        android:layout_below="@id/ll"
        android:adjustViewBounds="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/haizei"
        android:layout_marginTop="3dp"
        ></ImageView>

    <!-- 游戏的主界面-->
    <GridLayout
        android:layout_marginTop="3dp"
        android:layout_below="@id/iv"
        android:id="@+id/gl"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:columnCount="5"
        android:rowCount="3"
        android:adjustViewBounds="true"
        >
    </GridLayout>
    <TextView
        android:id="@+id/tv_step"
        android:layout_below="@id/gl"
        android:layout_marginTop="3dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="已用步数:0"
        android:textSize="26sp"
        />

</RelativeLayout>

3.打开图片选择图片

给Button设置点击事件,调用startActivityForResult(Intent intent,int requestCode);方法,来获取图片。

 bt_choice.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent= new Intent("android.intent.action.GET_CONTENT");
                intent.setType("image/*");
                startActivityForResult(intent, CHOICE_PHOTO);//打开相册
            }
        });

在Activity中重写onActivityResult(int requestCode, int resultCode, Intent data)方法来显示选择的图片,以及初始化游戏。(图片选择完以后,就要进行图片的切割,和拼图游戏的开始。)

 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode){
            case CHOICE_PHOTO:
                if (resultCode==RESULT_OK){
                    //判断手机系统版本
                    if (Build.VERSION.SDK_INT>=19){
                        handleImageOnKitKat(data);
                        //得到imageview中的图片
                        BitmapDrawable bitmapDrawable= (BitmapDrawable) photo.getDrawable();
                        bt_tupan=bitmapDrawable.getBitmap();
                        //将原来GridLayout中的小方块移除,
                        removeGameItem();
                        //将新图切割成小方块并加入GridLayout.
                        setGameItem();
                        //开始游戏
                        startGame();
                    }else {
                        handleImageBeforeKitKat(data);
                        //得到imageview中的图片
                        BitmapDrawable bitmapDrawable= (BitmapDrawable) photo.getDrawable();
                        bt_tupan=bitmapDrawable.getBitmap();
                         //将原来GridLayout中的小方块移除,
                        removeGameItem();
                        //将新图切割成小方块并加入GridLayout.
                        setGameItem();
                        //开始游戏
                        startGame();
                    }
                }
        }
    }

然后是选择图片的具体方法的实现函数。注释很清楚,不多说。我们的重点在拼图以及手势变化的具体实现,这里选择图片的方式很多。不多讲,网上有现成的框架。

 //手机不大于19的取数据方法
    private void handleImageBeforeKitKat(Intent data) {
        Uri uri =data.getData();
        String imagePath = getImagePath(uri, null);
        displayImage(imagePath);
    }
    /**
     * 手机大于19的取数据方法
     * @param data
     */
    @TargetApi(Build.VERSION_CODES.KITKAT)
    private void handleImageOnKitKat(Intent data) {
        String imagePath=null;
        Uri uri=data.getData();
        if (DocumentsContract.isDocumentUri(this,uri)){
            //如果是document类型的url,则通过document的id处理。
            String docId=DocumentsContract.getDocumentId(uri);
            if ("com.android.providers.media.documents".equals(uri.getAuthority())){
                String id =docId.split(":")[1];//解析出数字格式的id;
                String selection= MediaStore.Images.Media._ID+"="+id;
                imagePath=getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,selection);
            }else if ("com.android.providers.downloads.documents".equals(uri.getAuthority())){
                Uri contenturi= ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"),Long.valueOf(docId));
                imagePath=getImagePath(contenturi,null);
            }
        }else if ("content".equalsIgnoreCase(uri.getScheme())){
            //如果不是document类型的uri,则使用普通的方式处理。
            imagePath=getImagePath(uri,null);
        }
        displayImage(imagePath);
    }
    /**
     * 显示图片
     * @param imagePath  //图片的路径。
     */
    private void displayImage(String imagePath) {
        if (imagePath != null) {
            Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
            if (isHeigthBigWidth(bitmap)) {
                Bitmap bt = rotaingImageView(bitmap);//将图片旋转90度。
                Bitmap disbitmapt = ajustBitmap(bt);
                photo.setImageBitmap(disbitmapt);
            } else {
                Bitmap disbitmap = ajustBitmap(bitmap);
                photo.setImageBitmap(disbitmap);
            }
        }
    }

    /**
     * 调整图片的方向
     * @param bitmap
     * @return
     */
    private Bitmap rotaingImageView(Bitmap bitmap) {
        //旋转图片 动作
        Matrix matrix = new Matrix();;
        matrix.postRotate(270);
        // 创建新的图片
        Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0,
                bitmap.getWidth(), bitmap.getHeight(), matrix, true);
        return resizedBitmap;
    }
    /**
     * 得到图片的路径
     * @param externalContentUri
     * @param selection
     * @return
     */
    private String getImagePath(Uri externalContentUri, String selection) {
        String path=null;
        Cursor cursor=getContentResolver().query(externalContentUri, null, selection, null, null);
        if (cursor!=null){
            if (cursor.moveToFirst()){
                path=cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
            }
        }
        cursor.close();
        return path;
    }

4.拼图的各个小方块的形成过程。

看着各个小方块,我们用GridLayout来实现是最为方便的。所以,用一个GridLayout来显示大图切割后的各个小方块,用一个ImageView数组来保存各个小方块的信息,并且,我们默认把最后一个小方块设置为空白方块。

首先是各种需要的变量。注释很清楚。

 /**
     * 利用二维数组创建若干个游戏小方框
     */
    private ImageView [][] iv_game_arr=new ImageView[3][5];
    /**
     *游戏主界面
     *
     */
    private GridLayout gl_game_layout;
    //小方块的行和列
    private int i;
    private int j;
    /**空方块的全局变量*/
    private ImageView iv_null_imagview;

接着是从Imageview获取图片,并且将图片按照一定的行和列进行切割(这里将拼图设置为3行5列)。将切割后的各个小方块的信息保存在一个ImageView数组中。给每个小方块设置Tag,和点击监听。

 private void setGameItem() {
        //调整图片的尺寸
        Bitmap abitmap=ajustBitmap(bt_tupan);
        int ivWidth=getWindowManager().getDefaultDisplay().getWidth()/5;//每个游戏小方块的宽和高。切成正方形
        int tuWidth=abitmap.getWidth()/5;
        for (int i=0;i<iv_game_arr.length;i++){
            for (int j=0;j<iv_game_arr[0].length;j++){
                //将大图切成小方块
                Bitmap bm=Bitmap.createBitmap(abitmap,j*tuWidth,i*tuWidth,tuWidth,tuWidth);
                iv_game_arr[i][j]=new ImageView(this);
                iv_game_arr[i][j].setImageBitmap(bm);//设置每一个小方块的图案
                iv_game_arr[i][j].setLayoutParams(new RelativeLayout.LayoutParams(ivWidth, ivWidth));
                //设置方块之间的间距
                iv_game_arr[i][j].setPadding(2, 2, 2, 2);
                iv_game_arr[i][j].setTag(new GameItemView(i, j, bm)); //绑定自定义数据
                iv_game_arr[i][j].setOnClickListener(new View.OnClickListener() {
                .......

);

当然,我们选择的图片不可能都是符合标准的大小的,所以,在切割图片之前我们要对图片进行调整。将图片调整为5:3的比例。(这样切割成3行5列的小方块才能正确切割)这里对于width,我把每个小方块的间隔事先也算到里面去。

 //调整图片的大小
    private Bitmap ajustBitmap(Bitmap bitmap) {
        int width=getWindowManager().getDefaultDisplay().getWidth()-(iv_game_arr[0].length-1)*2;
        int heigth=width/5*3;
        Bitmap scaledBitmap=Bitmap.createScaledBitmap(bitmap, width, heigth, true);
        return scaledBitmap;
    }

将每个小方格放入到GridLayout中。

 /**
     * 将小方格放入GridLayout
     */
    private void startGame() {
        tv_step.setText("已用步数:0");
        for (i = 0; i <iv_game_arr.length; i++){
            for (j = 0; j <iv_game_arr[0].length; j++){
                gl_game_layout.addView(iv_game_arr[i][j]);
            }
        }
        //将最后一个方块设置为设置空方块。
        setNullImageView(iv_game_arr[i-1][j-1]);

5.小方块的点击事件和手势判断过程。

这里是拼图游戏的核心,弄懂了小方块的移动变化规律,也就弄懂了拼图游戏。

对于点击事件,首先拿到被点击的小方块的各种信息(位置、图案)和空白小方块的位置信息,判断被点击的小方块是否和空白小方块相邻,如果相邻,就移动交换数据(用TranslateAnimation来实现移动动画),如果不相邻则无操作。

a.判断点击方块和空白方块是否相邻的方法

/**
     *   判断当前点击的方块,是否和空方块相邻。
     * @param imageView 当前点击的方块
     * @return true:相邻。 false:不相邻。
     */
    public boolean isAdjacentNullImageView(ImageView imageView){
        //获取当前空方块的位置与点击方块的位置
        GameItemView null_gameItemView= (GameItemView) iv_null_imagview.getTag();
        GameItemView now_gameItem_view = (GameItemView) imageView.getTag();
     if(null_gameItemView.getY()==now_gameItem_view.getY()&&now_gameItem_view.getX()+1==null_gameItemView.getX()){//当前点击的方块在空方块的上面
           return true;
       }else if(null_gameItemView.getY()==now_gameItem_view.getY()&&now_gameItem_view.getX()==null_gameItemView.getX()+1){//当前点击的方块在空方块的下面
           return true;
       }else if(null_gameItemView.getY()==now_gameItem_view.getY()+1&&now_gameItem_view.getX()==null_gameItemView.getX()){//当前点击的方块在空方块的左面
           return true;
       }else if(null_gameItemView.getY()+1==now_gameItem_view.getY()&&now_gameItem_view.getX()==null_gameItemView.getX()){ ////当前点击的方块在空方块的右面
           return true;
       }
        return false;
    }

b.接着是如果相邻就进入方块数据交换的方法

这里有个方法重载,是否需要动画效果,没有动画效果的数据交换是为初始化游戏时打乱拼图做准备的。这里将核心交换代码列出。每次交换后还要判断是否游戏胜利(即是否拼图完成~)。



            //得到点击方块绑定的数据
            GameItemView gameItemView = (GameItemView) itemimageView.getTag();
            //将空方块的图案设置为点击方块
            iv_null_imagview.setImageBitmap(gameItemView.getBm());
            //得到空方块绑定的数据
            GameItemView null_gameItemView = (GameItemView) iv_null_imagview.getTag();
            //交换数据(将点击方块的数据传入空方块)
            null_gameItemView.setBm(gameItemView.getBm());
            null_gameItemView.setP_x(gameItemView.getP_x());
            null_gameItemView.setP_y(gameItemView.getP_y());
            //设置当前点击的方块为空方块。
            setNullImageView(itemimageView);
            if (isStart){
                isGameWin();//成功时,会弹一个吐司。
            }

c.交换时的动画设置

交换设置动画时,首先判断移动的方向,根据方向设置不同的移动动画,然后再监听动画完成后,进行数据交换操作。即上面b.接着是如果相邻就进入方块数据交换的方法.最后执行动画。

 //1.创建一个动画,设置方向,移动的距离
//判断方向,设置动画
        if (itemimageView.getX()>iv_null_imagview.getX()){//当前点击的方块在空方块的上面
            //下移
            translateAnimation = new TranslateAnimation(0.1f,-itemimageView.getWidth(),0.1f,0.1f);
        }else if (itemimageView.getX()<iv_null_imagview.getX()){//当前点击的方块在空方块的下面
            //上移
            boolean f=itemimageView.getX()<iv_null_imagview.getX();
            //Log.i("点击方块","sssssssssssssssssssssssss"+f);
            translateAnimation = new TranslateAnimation(0.1f,itemimageView.getWidth(),0.1f,0.1f);
        }else if (itemimageView.getY()>iv_null_imagview.getY()){//当前点击的方块在空方块的左面
            //右移
            translateAnimation=new TranslateAnimation(0.1f,0.1f,0.1f,-itemimageView.getWidth());
        }else if(itemimageView.getY()<iv_null_imagview.getY()){//当前点击的方块在空方块的右面
            //左移
            translateAnimation=new TranslateAnimation(0.1f,0.1f,0.1f,itemimageView.getWidth());
        }
        //2.设置动画的各种参数
        translateAnimation.setDuration(80);
        translateAnimation.setFillAfter(true);
        //3.设置动画的监听
        translateAnimation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                isAminMove=true;
            }
            @Override
            public void onAnimationEnd(Animation animation) {
                //动画结束,交换数据
               ......
            }

 //动画执行
        itemimageView.startAnimation(translateAnimation);

点击事件的流程就完了,接下来是手势判断的事件。即不仅可以通过点击小方块进行移动,也可以通过手势移动小方块。

One.创建手势对象

在onFling方法中完成手势相关的操作。

 //创建手势对象
        gestureDetector =new GestureDetector(this, new GestureDetector.OnGestureListener() {
            @Override
            public boolean onDown(MotionEvent e) {
                return false;
            }
            @Override
            public void onShowPress(MotionEvent e) {
            }
            @Override
            public boolean onSingleTapUp(MotionEvent e) {
                return false;
            }
            @Override
            public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
                return false;
            }
            @Override
            public void onLongPress(MotionEvent e) {
            }
            @Override
            public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            //手势相关的操作
            ......
    }

接着我们onFling方法中做具体操作

Two.判断手势移动的方向

根据返回值的不同得到不同的移动方向。

 /**
     * 增加手势滑动,根据手势判断是上下左右滑动
     * @param start_x 手势起始点x
     * @param start_y 手势起始点y
     * @param end_x 手势终止点 x
     * @param end_y 手势终止点y
     * @return 1:上 2:下 3:左 4:右
     */
    public int getDirctionByGesure(float start_x,float start_y,float end_x,float end_y){
        boolean isLeftOrRight =(Math.abs(end_x-start_x)>Math.abs(end_y-start_y))?true:false; //是否是左右
        if(isLeftOrRight){//左右
            boolean isLeft=(end_x-start_x)>0?false:true;
            if(isLeft){
                return 3;
            }else {
                return 4;
            }
        }else{//上下
            boolean isUp=(end_y-start_y)>0?false:true;
            if (isUp){
                return 1;
            }else {
                return 2;
            }
        }
    }

Three.根据空方块和移动的方向,判断能否移动以及进行移动操作。

因为是手势,移动的肯定是空方块周围的方块,所以重点就是要判断空方块在要移动的方块的那个方向,再根据方向判断能否移动,进行移动操作。(其中changeDateByImageView()中的方法就是具体的方块交换数据及移动的操作。就是点击事件的那个方法。)

/**重载changeByDirGes(int type)方法;
     * 根据手势的方向,对空方块相邻位置的方块进行移动。
     * @param type 方向的返回值  1:上 2:下 3:左 5:右
     * @param isAnim 是否有动画 true:有动画,false:无动画
     */
    public void changeByDirGes(int type,boolean isAnim){
        //1.获取当前空方块的位置。
        GameItemView null_gameItemView= (GameItemView) iv_null_imagview.getTag();
        int new_x=null_gameItemView.getX();
        int new_y=null_gameItemView.getY();
        //2.根据方向,设置相应相邻的位置坐标。
        if (type==1){//说明空方块在要移动的方块的上面。
            new_x++;
        }else if (type==2){//空方块在要移动的方块的下面
            new_x--;
        }else if (type==3){//空方块在要移动的方块的左面
            new_y++;
        }else if (type==4){//空方块在要移动的方块的右面
            new_y--;
        }
        //3.判断这个新坐标是否存在
        if(new_x>=0&&new_x<iv_game_arr.length&&new_y>=0&&new_y<iv_game_arr[0].length){
            //存在,可以移动交换数据
            if(isAnim){//有动画
                changeDateByImageView(iv_game_arr[new_x][new_y]);
            }else{
                changeDateByImageView(iv_game_arr[new_x][new_y],isAnim);
            }
        }else{
            //什么也不做
        }
    }

好了,手势事件也就大功告成了~

当然这里有两个要注意的地方。1.首先要对当前的Activity设置onTouchEvent()方法,将onTouch事件交由手势去处理,其次也要设置dispatchTouchEvent()方法,在里面也要向下分发给手势事件,如果不设置向下分发给手势判断,那么在GridLayout里,就只能触发点击事件而手势事件就不会起作用了。2.要增加一个是否在移动过程中的flag,如果在移动过程中,就什么也不做,要不然每次点击小方块即使在移动过程中,也会触发点击事件从而又进行动画移动,造成不好的用户体验。

 @Override
    public boolean onTouchEvent(MotionEvent event) {
        return gestureDetector.onTouchEvent(event);
    }
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        gestureDetector.onTouchEvent(ev);
        return super.dispatchTouchEvent(ev);
    }

6.游戏开始打乱方块以及游戏结束时弹出Toast提示的方法。

代码很简单,直接上代码,其中,弹出的Toast是一个带有自定义View动画的Toast.

 //随机打乱图片的顺序
    public void randomOrder(){
        //打乱的次数,为了测试方便,设置很小。
        for (int i=0;i<5;i++){
            //根据手势,交换数据,无动画。
            int type = (int) (Math.random()*4)+1;
           // Log.i("sssssssssfdfdfd","交换次数"+i+"type的值"+type);
           changeByDirGes(type, false);
        }
    }
    /**
     * 判断游戏结束的方法
     */
    public void isGameWin(){
        //游戏胜利标志
        boolean isGameWin =true;
        //遍历每个小方块
        for (i = 0; i <iv_game_arr.length; i++){
            for (j = 0; j <iv_game_arr[0].length; j++){
                //为空的方块不判断 跳过
                if (iv_game_arr[i][j]==iv_null_imagview){
                    continue;
                }
                GameItemView gameItemView= (GameItemView) iv_game_arr[i][j].getTag();
                if (!gameItemView.isTrue()){
                    isGameWin=false;
                    //跳出内层循环
                    break;
                }
            }
            if (!isGameWin){
                //跳出外层循环
                break;
            }
        }
        //根据一个开关变量觉得游戏是否结束,结束时给提示。
        if (isGameWin){
           // Toast.makeText(this,"游戏胜利",Toast.LENGTH_SHORT).show();
            ToastUtil.makeText(this,"恭喜你,游戏胜利,用了"+step+"步",ToastUtil.LENGTH_SHORT,ToastUtil.SUCCESS);
            step=0;

        }
    }

好了,重要的部分都已经完了,这里还有个自定义View的Toast,关于Toast的详解,将在下篇文章,这里先简单说明下自定义Toast的实现过程。

首先,新建一个SuccessToast类,(笑脸包括左眼,有眼,笑脸弧)。我们将核心的过程给出。利用动画,实现动态笑脸画制过程。

 @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mPaint.setStyle(Paint.Style.STROKE);
        //话笑脸弧
        canvas.drawArc(rectF, 180, endAngle, false, mPaint);
        mPaint.setStyle(Paint.Style.FILL);
        if (isSmileLeft) {
        //如果是左眼,画左眼
            canvas.drawCircle(mPadding + mEyeWidth + mEyeWidth / 2, mWidth / 3, mEyeWidth, mPaint);
        }
        if (isSmileRight) {
        //如果是有眼,画右眼。
            canvas.drawCircle(mWidth - mPadding - mEyeWidth - mEyeWidth / 2, mWidth / 3, mEyeWidth, mPaint);
        }
    }

 /**
     * 开始动画的方法
     * @param startF 起始值
     * @param endF   结束值
     * @param time  动画的时间
     * @return
     */
    private ValueAnimator startViewAnim(float startF, final float endF, long time) {
        //设置valueAnimator 的起始值和结束值。
        valueAnimator = ValueAnimator.ofFloat(startF, endF);
        //设置动画时间
        valueAnimator.setDuration(time);
        //设置补间器。控制动画的变化速率
        valueAnimator.setInterpolator(new LinearInterpolator());
        //设置监听器。监听动画值的变化,做出相应方式。
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {

                mAnimatedValue = (float) valueAnimator.getAnimatedValue();
                //如果value的值小于0.5
                if (mAnimatedValue < 0.5) {
                    isSmileLeft = false;
                    isSmileRight = false;
                    endAngle = -360 * (mAnimatedValue);
                    //如果value的值在0.55和0.7之间
                } else if (mAnimatedValue > 0.55 && mAnimatedValue < 0.7) {
                    endAngle = -180;
                    isSmileLeft = true;
                    isSmileRight = false;
                    //其他
                } else {
                    endAngle = -180;
                    isSmileLeft = true;
                    isSmileRight = true;
                }
                //重绘
                postInvalidate();
            }
        });

        if (!valueAnimator.isRunning()) {
            valueAnimator.start();
        }
        return valueAnimator;
    }

然后新建一个success_toast_layout.xml,完成toast的布局。布局是左右(左边笑脸view,右边TextView的提示。)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/root_layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#00000000"
    android:orientation="vertical">
    <LinearLayout
        android:id="@+id/base_layout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="25dp"
        android:layout_marginLeft="30dp"
        android:layout_marginRight="30dp"
        android:layout_marginTop="25dp"
        android:background="@drawable/background_toast"
        android:orientation="horizontal">
        <LinearLayout
            android:id="@+id/linearLayout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center">
            <com.example.yyh.puzzlepicture.activity.Util.SuccessToast
                android:id="@+id/successView"
                android:layout_width="50dp"
                android:layout_height="50dp"
                android:layout_gravity="center_vertical|left"
                android:layout_margin="10px"
                android:gravity="center_vertical|left" />
        </LinearLayout>
        <TextView
            android:id="@+id/toastMessage"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_vertical"
            android:padding="10dp"
            android:text="New Text" />
    </LinearLayout>
</LinearLayout>

最后新建一个ToastUtil类,管理自定义的Toast.

/**
 * Created by yyh on 2016/10/25.
 */
public class ToastUtil {
    public static final int LENGTH_SHORT = 0;
    public static final int LENGTH_LONG = 1;

    public static final int SUCCESS = 1;

    static SuccessToast successToastView;

    public static void makeText(Context context, String msg, int length, int type) {
        Toast toast = new Toast(context);
        switch (type) {
            case 1: {
                View layout = LayoutInflater.from(context).inflate(R.layout.success_toast_layout, null, false);
                TextView text = (TextView) layout.findViewById(R.id.toastMessage);
                text.setText(msg);
                successToastView = (SuccessToast) layout.findViewById(R.id.successView);
                successToastView.startAnim();
                text.setBackgroundResource(R.drawable.success_toast);
                text.setTextColor(Color.parseColor("#FFFFFF"));
                toast.setView(layout);
                break;
            }

        }
        toast.setDuration(length);
        toast.show();
    }

}

这样就可以在ManiActivity中调用这个自定义Toast了。

好了,完结。

游戏源码:csdn:拼图游戏的实现过程

gitHub:拼图游戏。

时间: 2024-10-11 11:09:57

拼图游戏-从基础到应用玩转手势变化。的相关文章

Java GUI 基础 Eight Puzzle (拼图游戏)

很简单使用java GUI 制作一个简单的拼图游戏 // main package HW1; import java.io.IOException; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JLabel; public class HW1 extends JFrame{ /** * */ public HW1_0586(String s

逆序数-拼图游戏必备知识

近两天准备出个拼图游戏的教程,准备时候遇到一些问题,收集保存分享下来,一来自己用,二来涨点知识,三来有需要的小伙伴刚好也看看. 事情起因很简单,比如下面这个拼图(矩阵): 1 2 3 空 这样一个2*2矩阵,是标准原始矩阵,但是变一下: 3 1 2 空 这样是可还原的(空和附近的可以换位置,空2,空3,空1,空2即可). 但下面这个呢? 1 3 2 空 你还能还原吗? 这个不是怪你,是真的没法还原. 当时脑子里面过了一下,就发现不行,然后查了查资料,真的有前辈搞过相关的问题,并且有了靠谱的答案.

一款html拼图游戏详解

本文是爱编程原创翻译,转载请看清文末的转载要求,谢谢合作! 游戏介绍 这篇文章是献给web游戏开发者用简单的开发工具开发一款游戏.此文介绍了用html.css.javascript只需简单和几个步骤开发一款2d游戏.在这里,我要呈现给大家如何创建一款拼图游戏,在这个游戏中你可以拖动小图块来拼成完整的大图. 点击这里查看游戏效果. 游戏规则 游戏规则非常简单,你只要拖动被切碎的图片块来组成完整的大图.需要用正确的方法才能最终拼成完整的图片.在游戏中拖动小图片的次数将被统计下来.所以.应该尽量用最少

Vue.js实现拼图游戏

Vue.js实现拼图游戏 之前写过一篇<基于Vue.js的表格分页组件>的文章,主要介绍了Vue组件的编写方法,有兴趣的可以访问这里进行阅读:http://www.cnblogs.com/luozhihao/p/5516065.html 前言 为了进一步让大家了解Vue.js的神奇魅力,了解Vue.js的一种以数据为驱动的理念,本文主要利用Vue实现了一个数字拼图游戏,其原理并不是很复杂,效果图如下: demo展示地址为:https://luozhihao.github.io/vue-puzz

程序设计 之 C#实现《拼图游戏》 (下) 原理篇

前言:在 http://www.cnblogs.com/labixiaohei/p/6698887.html 程序设计 之 C#实现<拼图游戏>(上),上传了各模块代码,而在本文中将详细剖析原理,使读者更容易理解并学习,程序有诸多问题,欢迎指出,共同学习成长! 正文: 拼图是一个非常经典的游戏,基本每个人都知道他的玩法,他的开始,运行,结束.那么,当我们想要做拼图的时候如何入手呢?答案是:从现实出发,去描述需求(尽量描述为文档),当我们拥有了全面的需求,就能够提供可靠的策略,从而在代码中实现,

拼图游戏---需求分析

各位领导/投资人/用户/合作伙伴:我们的产品--趣拼图是为了游戏中的暴力因素以及无聊.,我们有独特的办法制作一个游戏,它能给用户带来更多好处,比如生活知识的普及,智力的开发等,同时我们有更高效率的一传十,十传百的方法,能让更多用户知道我们的产品,并进一步的传播. 在经过众多假设以及实践,我们组决定做一个开发智力的拼图游戏.我发现现在大多数游戏都是按部就班,玩家需要按照游戏制定的规则来活动,那这样,游戏除了娱乐也就没有其他的意义.经过调查,发现现在人,喜欢玩一些格斗[比如拳皇].跑酷[比如神庙逃亡

游戏开发基础1:游戏指标

基础 * 免费模式/F2P 免费提供游戏,通常是通过提供访问高级功能的能力,或相关产品和服务来收费. * 虚拟商品 游戏内置道具或游戏相关服务,例如能量提升道具,付费内容,或者增进/加强玩法的临时订阅服务. F2P游戏的关键绩效指标 这些将有助于你跟着自己游戏的盈利战略. * 每获取成本(CPA)/ 每安装成本(CPI) 即获取一名新用户的成本.可以将广告活动成本除以新应用安装量来计算其数值: CPA=广告成本/新安装量 * 每参与成本(CPE) 通过一则参与广告获取新用户的成本.在这种广告中,

基于unity3d和leap motion的拼图游戏

最近用unity3d引擎做了一个拼图游戏,会分几次写完,以此作为总结.本文基本查找了网上能查到的所有资料作为参考.也算是大家节省了时间. 目前只完成了拼图部分,leap motion手势控制部分会在后续完成,不过说实话不太看好LM. 项目资源来自 栋笃神探 http://blog.csdn.net/cube454517408/article/details/7907247,不过玩法不同,玩法与小夭 http://game.ceeger.com/forum/read.php?tid=2852相同.

方格拼图游戏2(javascript以类的形式实现)+增加批量移动

今天又在原来的基础上,增加了新的功能:当空白方格A 与 鼠标点击的方格 B在同一行,或者同一列,而且当他们不是紧挨着的情况,自动将B到A间的方格们,顺次移动填补空白方格. var game ={ wid_num:3, cotainerid:"game_cotainer", init:function(_num){ console.log('init'); game.wid_num = _num || 3; console.log( game.wid_num ); var max_num