English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Androidパズルゲームで基本から応用までのジェスチャー変化を楽しむ

皆さんは小さい頃にパズルゲームを遊んだことがあるでしょう。今や携帯電話が普及し、その上で遊べるゲームもどんどん増えています。そこで、昔のことを思い出して、簡単なパズルゲームを作成しました。さらに、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="26
    />
</

3


 
      @Override
      
        
        intent.setType("image", "第2段": "Intent intent= new Intent("android.intent.action.GET_CONTENT");", "第3段": "public void onClick(View v) {", "第4段": "bt_choice.setOnClickListener(new View.OnClickListener() {", "第5段": "Buttonにクリックイベントを設定し、startActivityForResult(Intent intent,int requestCode);メソッドを呼び出して画像を取得します。", "第6段": ".画像を選択する", "第7段": "RelativeLayout>/*
        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) {
          //スマートフォンOSのバージョンを判断
          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に配置します。

 /**
   * 小グリッドに小ブロックを配置します
   */
  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 現在クリックされたブロック
   * public boolean isAdjacentNullImageView(ImageView imageView){
   */
  現在の空ブロックの場所とクリックブロックの場所を取得します。
    //GameItemView now_gameItem_view = (GameItemView) imageView.getTag();
    GameItemView null_gameItemView = (GameItemView) iv_null_imagview.getTag();
    if(null_gameItemView.getY()==now_gameItem_view.getY()&&now_gameItem_view.getX()
   ==null_gameItemView.getX()){+1else if(null_gameItemView.getY()==now_gameItem_view.getY()&&now_gameItem_view.getX()==null_gameItemView.getX()//現在クリックされているブロックは空のブロックの上側にあります
      return true;
    }+1)//現在クリックされているブロックは空のブロックの下側にあります
      return true;
    &&now_gameItem_view.getX()==null_gameItemView.getX(){+1else if(null_gameItemView.getY()//現在クリックされているブロックは空のブロックの左側にあります
      return true;
    }+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());
    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 e2float 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{
      //何もしない
    }
  }

手势イベントは大成功です。

もちろん、ここには注意すべき2つのポイントがあります。1まず、現在のActivityにonTouchEvent()メソッドを設定し、タッチイベントをジェスチャーに委譲します。次に、dispatchTouchEvent()メソッドも設定し、そこでもジェスチャーイベントを下位に委譲します。そうしないと、GridLayout内ではクリックイベントがトリガーされるだけで、ジェスチャーイベントは機能しません。2移動中のフラグを追加します。移動中であれば何もしないようにします。それでも移動中に小さなブロックをクリックすると、タップイベントがトリガーされ、アニメーションが再び実行され、悪いユーザーエクスペリエンスが生じます。

 @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のレイアウトを完了します。レイアウトは左右(左側の笑顔ビュー、右側の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を呼び出せるようになります。
もう、終わり。

ゲームソースコード:パズルゲームの実現プロセス
gitHub:パズルゲーム

これで本文のすべてが終わりです。皆様の学習に役立つことを願っています。また、ナイアラ教程を多くのサポートをお願いします。

声明:本文の内容はインターネットから提供され、著作権者は所有者であり、インターネットユーザーが自発的に貢献し、自己でアップロードしました。本サイトは所有権を持ちません。また、人工的な編集は行われていません。著作権侵害が疑われる内容が見つかった場合は、以下のメールアドレスにご連絡ください:notice#oldtoolbag.com(メールを送信する際、#を@に変更してください。申し訳ありませんが、関連する証拠を提供していただき、一旦確認次第、本サイトは侵害される内容をすぐに削除します。)

おすすめ