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

Android Puzzle-Spiel - von den Grundlagen bis zur Anwendung der Gestenänderungen

Glauben Sie, dass die meisten von uns in der Kindheit Puzzlespiele gespielt haben. Heute, da Handys weit verbreitet sind und immer mehr Spiele auf dem Handy gespielt werden können, haben wir beschlossen, ein einfaches Puzzle-Spiel zu schreiben, um unsere Kindheit zu Revue passieren zu lassen und gleichzeitig einige Grundkenntnisse von Android zu vertiefen.
Wie immer, zuerst das Screenshot:

Hier habe ich das Bild nur wenig durcheinander gewürfelt, was im Code geändert werden kann.

Zunächst einmal gibt es ein Standardbild, das zum Puzzeln verwendet werden kann, oder Sie können ein Bild Ihrer Wahl auswählen, um das Puzzle zu lösen. Der Puzzler verzeichnet den Anzahl der Schritte und zeigt eine lachende Nachricht an, wenn das Spiel gewonnen wird, zusammen mit der Anzahl der Schritte.

ps: Interessierte können hier weiter erweitern, z.B. Optionen zur Erhöhung der Schwierigkeit hinzufügen, das Bild in mehr kleine Quadrate teilen usw.

Allgemeine Idee: Das große Bild in verschiedene kleine Quadrate schneiden, die Informationen jedes kleinen Quadrats in einem Array aufzeichnen, die kleinen Quadrate mit GridLayout anzeigen und ein leeres Quadrat markieren (das leere Quadrat kann mit angrenzenden Quadern ausgetauscht werden), auf den kleinen Quadrate im GridLayout Click-Events hinzufügen und im gesamten Bildschirm Gesten-Events hinzufügen. Bei jedem Klick oder Gesten wird überprüft, ob das Quadrat verschoben werden kann, und schließlich wird eine Siegennachricht angezeigt, wenn das Spiel gewonnen wird.
Ohne viel Zögern, hier ist der nächste Schritt zur Implementierung des Puzzlespiels.

1.Klasse für kleine Quadrate.

Dies sind die verschiedenen Variablen des kleinen Quadrats, Klasse, um die Informationen jedes kleinen Quadrats zu verwalten, die aus dem großen Bild geschnitten werden. Es ist sehr einfach, nur verschiedene Variablen und Setter und Getter Methoden, direkt auf den Code.

/**
 * Created by yyh on 2016/10/21.
 */
public class GameItemView{
  /**
   * Jede kleine Quadrate Informationen
   */
  //Jede kleine Quadrate tatsächliche Position x,
  private int x=0;
  //Jede kleine Quadrate tatsächliche Position y,
  private int y=0;
  //Jede kleine Quadrate Bild,
  private Bitmap bm;
  //Jede kleine Quadrate Bildposition x,
  private int p_x=0;
  //Jede kleine Quadrate Bildposition 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;
  }
  /**
   * Überprüfen Sie, ob die Position jedes kleinen Quadrats korrekt ist
   * @return
   */
  public boolean isTrue(){
    if (x==p_x&&y==p_y){
      return true;
    }
    return false;
  }
}

2. Layout der Hauptoberfläche

Die Hauptoberfläche ist einfach, ein Button, um das Bild zu wechseln, ein ImageView, um das Originalbild anzuzeigen, ein GridLayout, um das Puzzlespiel durchzuführen, und schließlich ein TextView, um die Anzahl der Schritte anzuzeigen, die für das Beenden des Puzzles benötigt wurden. Die Anordnung ist wie folgt:

<?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="Bild auswählen"
      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>
  !-- Hauptmenü des Spiels-->
  <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="Bisherige Schritte: 0"
    android:textSize="26sp"
    />
</RelativeLayout>

3. Bildauswahl öffnen

Button auf einen Klickereignis setzen, um das startActivityForResult(Intent intent,int requestCode);-Verfahren aufzurufen, um ein Bild zu erhalten.

 bt_choice.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        
        intent.setType("image", "第2段": "Intent intent= new Intent("android.intent.action.GET_CONTENT");/*"
        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)){
      //Wird die URL als Dokumententyp erkannt, wird durch die ID des Dokuments behandelt.
      String docId=DocumentsContract.getDocumentId(uri);
      if ("com.android.providers.media.documents".equals(uri.getAuthority())) {
        String id =docId.split(":")[1];//Ein numerischer ID-Wert wird extrahiert;
        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())) {
      //Wird der URI nicht als Dokumententyp erkannt, wird auf die normale Weise behandelt.
      imagePath=getImagePath(uri,null);
    }
    displayImage(imagePath);
  }
  /**
   * Bild anzeigen
   * @param imagePath //Bildpfad.
   */
  private void displayImage(String imagePath) {
    if (imagePath != null) {
      Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
      if (isHeigthBigWidth(bitmap)) {
        Bitmap bt = rotaingImageView(bitmap);//Drehen Sie das Bild90 Grad.
        Bitmap disbitmapt = ajustBitmap(bt);
        photo.setImageBitmap(disbitmapt);
      } else {
        Bitmap disbitmap = ajustBitmap(bitmap);
        photo.setImageBitmap(disbitmap);
      }
    }
  }
  /**
   * Anpassen Sie den Bildrichtung
   * @param bitmap
   * @return
   */
  private Bitmap rotaingImageView(Bitmap bitmap) {
    //Bewegung zur Drehung des Bildes
    Matrix matrix = new Matrix();;
    matrix.postRotate(270);
    // Erstellen Sie ein neues Bild
    Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0,
        bitmap.getWidth(), bitmap.getHeight(), matrix, true);
    return resizedBitmap;
  }
  /**
   * Erhalten Sie den Pfad des Bildes
   * @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. Bildungsvorgang der verschiedenen kleinen Quadrate des Puzzles.}}

Schauen wir uns die kleinen Quadrate an, mit GridLayout zu implementieren ist am bequemsten. Daher verwenden wir ein GridLayout, um die kleinen Quadrate nach dem Schneiden des großen Bildes anzuzeigen, und ein ImageView-Array, um die Informationen der kleinen Quadrate zu speichern, und wir setzen standardmäßig das letzte kleine Quadrat als leeres Quadrat.

Zuerst sind alle notwendigen Variablen. Die Kommentare sind sehr klar.

 /**
   * Erstellen Sie durch ein zweidimensionales Array mehrere kleine Spielequadrat
   */
  private ImageView[][] iv_game_arr = new ImageView[3][5];
  /**
   *Hauptinterface des Spiels
   *
   */
  private GridLayout gl_game_layout;
  //Zeile und Spalte des kleinen Quadrats
  private int i;
  private int j;
  /**Globale Variable für den leeren Quadrat*/
  private ImageView iv_null_imagview;

Dann geht es darum, das Bild aus ImageView abzurufen und es in bestimmten Zeilen und Spalten zu schneiden (hier wird das Puzzle so eingerichtet3Zeile5Kolonne). Die Informationen der zerschnittenen kleinen Quadrate werden in einem ImageView-Array gespeichert. Jedes kleine Quadrat wird mit einem Tag und einem Klick-Listener versehen.

 private void setGameItem() {
    //Die Größe des Bildes anpassen
    Bitmap abitmap = ajustBitmap(bt_tupan);
    int ivWidth = getWindowManager().getDefaultDisplay().getWidth()/5;//Die Breite und Höhe jedes kleinen Spielquadrats. In Quadraten schneiden
    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++{
        //Das große Bild in kleine Quadrate schneiden
        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);//Jedes kleine Quadrat mit einem Muster einrichten
        iv_game_arr[i][j].setLayoutParams(new RelativeLayout.LayoutParams(ivWidth, ivWidth));
        //Setzen Sie den Abstand zwischen den Blöcken
        iv_game_arr[i][j].setPadding(2, 2, 2, 2);
        iv_game_arr[i][j].setTag(new GameItemView(i, j, bm)); //Datenbindung
        iv_game_arr[i][j].setOnClickListener(new View.OnClickListener() {
        .......
);

Natürlich können wir nicht davon ausgehen, dass alle ausgewählten Bilder standardmäßige Größen haben, daher müssen wir das Bild anpassen, bevor wir das Bild schneiden. Passen Sie das Bild an, um5:3im Verhältnis. (Dadurch werden3Zeile5Die Spalten der kleinen Blöcke müssen korrekt zugeschnitten werden. Hier habe ich den Abstand zwischen den kleinen Blöcken bereits vorher berücksichtigt.

 //Vergrößern oder verkleinern Sie das Bild
  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;
  }

Setzen Sie jeden kleinen Quadrat in das GridLayout.

 /**
   * Setzen Sie den kleinen Quadrat in das GridLayout
   */
  private void startGame() {
    tv_step.setText("Bisher verwendete Schritte: 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]);
      }
    }
    //Setzen Sie den letzten Block auf den leeren Block.
    setNullImageView(iv_game_arr[i-1-1]

5. Klickereignis und Gestenprüfungsprozess der kleinen Blöcke

Das ist das Herzstück des Puzzlespiels. Wenn Sie die Bewegungsregeln der kleinen Blöcke verstehen, dann verstehen Sie auch das Puzzlespiel.

Für den Klickereignis, erhalten Sie zunächst verschiedene Informationen des geklickten kleinen Blocks (Position, Muster) und die Positionsinformationen des leeren kleinen Blocks, prüfen Sie, ob der geklickte kleine Block neben dem leeren kleinen Block liegt. Wenn ja, bewegen und tauschen Sie die Daten (durch TranslateAnimation die Bewegungsanimation ausführen), wenn nein, keine Aktion ausführen.
a. Methode zum Prüfen, ob der geklickte Block neben einem leeren Block liegt

/**
   *  Prüfen Sie, ob der aktuell geklickte Block neben einem leeren Block liegt.
   * @param imageView Der aktuell geklickte Block
   * @return true: Adjacent. false: Nicht adjazent.
   */
  public boolean isAdjacentNullImageView(ImageView imageView){
    //Position des aktuellen leeren Blocks und Position des geklickten Blocks erhalten
    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()){//Der aktuelle geklickte Block ist über dem leeren Block
      return true;
    }else if(null_gameItemView.getY()==now_gameItem_view.getY()&&now_gameItem_view.getX()==null_gameItemView.getX()+1{//Der aktuelle geklickte Block ist unter dem leeren Block
      return true;
    }else if(null_gameItemView.getY()==now_gameItem_view.getY()+1&&now_gameItem_view.getX()==null_gameItemView.getX()){//Der aktuelle geklickte Block ist links vom leeren Block
      return true;
    }else if(null_gameItemView.getY()+1==now_gameItem_view.getY()&&now_gameItem_view.getX()==null_gameItemView.getX(){ ////Der aktuelle geklickte Block ist rechts vom leeren Block
      return true;
    }
    return false;
  }

b. Weiterhin wird der Methode zum Datenaustausch bei angrenzenden Blöcken übergeben
Hier gibt es eine Methode mit Überschreibung, ob ein Animationseffekt erforderlich ist, der Datenaustausch ohne Animation ist zur Vorbereitung auf das Durcheinanderlegen des Puzzles beim Initialisieren des Spiels vorgesehen. Hier wird der Kerncode des Datenaustauschs aufgeführt. Nach jedem Austausch muss auch überprüft werden, ob das Spiel gewonnen wurde (d.h. ob das Puzzle fertig ist).

    //Daten des geklickten Blocks abrufen
      GameItemView gameItemView = (GameItemView) itemimageView.getTag();
      //Das Muster des leeren Blocks auf den geklickten Block setzen
      iv_null_imagview.setImageBitmap(gameItemView.getBm());
      //Daten des leeren Blocks abrufen
      GameItemView null_gameItemView = (GameItemView) iv_null_imagview.getTag();
      //Daten austauschen (Die Daten des geklickten Blocks werden in den leeren Block übernommen)}
      null_gameItemView.setBm(gameItemView.getBm());
      null_gameItemView.setP_x(gameItemView.getP_x());
      null_gameItemView.setP_y(gameItemView.getP_y());
      //Setze den aktuellen geklickten Block als leeren Block.
      setNullImageView(itemimageView);
      if (isStart) {
        isGameWin();//Bei Erfolg wird ein Toast-Bildschirm angezeigt.
      }

c. Einstellungen der Animation beim Wechsel
Beim Wechsel der Einstellungen der Animation wird zunächst die Richtung überprüft, und je nach Richtung wird eine unterschiedliche Bewegungsanimation eingestellt. Danach wird auf den Abschluss der Animation gehört und die Daten werden ausgetauscht. Das bedeutet, dass oben b. weitergeht, wenn nebeneinander liegend, in die Methode zur Datenvertauschung des Blocks eingerückt wird. Schließlich wird die Animation ausgeführt.

 //1. Erstelle eine Animation, setze die Richtung und die Entfernung des Verschiebens
//Bestimme die Richtung, setze die Animation
    if (itemimageView.getX() > iv_null_imagview.getX()) {//Der aktuelle geklickte Block ist über dem leeren Block
      //nach unten bewegen
      translateAnimation = new TranslateAnimation(0.1f,-itemimageView.getWidth(), 0.1f, 0.1f);
    } else if (itemimageView.getX() < iv_null_imagview.getX()) {//Der aktuelle geklickte Block ist unter dem leeren Block
      //nach oben bewegen
      boolean f = itemimageView.getX() < iv_null_imagview.getX();
      //Log.i("Klick auf Block", "sssssssssssssssssssssssss"+f);
      translateAnimation = new TranslateAnimation(0.1f, itemimageView.getWidth(), 0.1f, 0.1f);
    } else if (itemimageView.getY() > iv_null_imagview.getY()) {//Der aktuelle geklickte Block ist links vom leeren Block
      //rechts bewegen
      translateAnimation = new TranslateAnimation(0.1f, 0.1f, 0.1f,-itemimageView.getWidth());
    } else if (itemimageView.getY() < iv_null_imagview.getY()) {//Der aktuelle geklickte Block ist rechts vom leeren Block
      //Nach links bewegen
      translateAnimation = new TranslateAnimation(0.1f, 0.1f, 0.1f, itemimageView.getWidth());
    }
    //2.Setzen Sie verschiedene Parameter der Animation
    translateAnimation.setDuration(80);
    translateAnimation.setFillAfter(true);
    //3.Animation der Animation einstellen
    translateAnimation.setAnimationListener(new Animation.AnimationListener() {
      @Override
      public void onAnimationStart(Animation animation) {
        isAminMove = true;
      }
      @Override
      public void onAnimationEnd(Animation animation) {
        //Animation endet, Daten werden ausgetauscht
        ......
      }
 //Animation wird ausgeführt
    itemimageView.startAnimation(translateAnimation);

Der Prozess des Klickens ist abgeschlossen, der nächste ist das Ereignis der Gestenbeurteilung. Das bedeutet, dass man nicht nur durch Klicken auf den kleinen Quadrate bewegen kann, sondern auch durch Gesten.

Erstellt ein Gestenobjekt
Geste-Vorgänge im onFling-Methodenabschnitt durchführen.

 //Objekt für Gesten erstellen
    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 e1MotionEvent e2, float distanceX, float distanceY) {
        return false;
      }
      @Override
      public void onLongPress(MotionEvent e) {
      }
      @Override
      public boolean onFling(MotionEvent e1MotionEvent e2float velocityX, float velocityY) {
      //Gestebezogene Operationen
      ......
  }

Dann führen wir im onFling-Methodenaufruf spezifische Operationen durch

Two. Bestimme die Richtung der Gestenbewegung
Erhalte verschiedene Bewegungsrichtungen basierend auf den verschiedenen Rückgabewerten.

 /**
   * Erhöhe die Gestenschiebewege, entscheide basierend auf dem Gesten, ob es sich um eine horizontale oder vertikale Gestenschiebewege handelt
   * @param start_x Der Startpunkt der Gestenbewegung x
   * @param start_y Der Startpunkt der Gestenbewegung y
   * @param end_x Der Endpunkt der Gestenbewegung x
   * @param end_y Der Endpunkt der Gestenbewegung y
   * @return 1:Oben 2:Unten 3:Links 4:Rechts
   */
  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; //Ob es sich um Links und Rechts handelt
    if(isLeftOrRight) {//Links und Rechts
      boolean isLeft = (end_x-start_x)>0?false:true;
      if(isLeft) {
        return 3;
      } else {
        return 4;
      }
    } else {//Oben und Unten
      boolean isUp = (end_y-start_y)>0?false:true;
      if (isUp) {
        return 1;
      } else {
        return 2;
      }
    }
  }

Three. Bestimme, ob sich der leere Block basierend auf dem Gesten und der Richtung bewegen kann, und führe die Verschiebungsoperation durch.
Da es sich um ein Gestensteuerung handelt, müssen sicherlich die umliegenden Blöcke des leeren Blocks verschoben werden. Daher ist der Hauptpunkt, den Standort des leeren Blocks in der Richtung des zu verschiebenden Blocks zu bestimmen, und dann basierend auf der Richtung zu bestimmen, ob verschoben werden kann, und die Verschiebungsoperation durchzuführen. (Die Methode changeDateByImageView() enthält die spezifischen Daten und Verschiebungsoperationen des Blocks. Dies ist der Methodenaufruf im Klickereignis.)

/**Überschreibe die Methode changeByDirGes(int type);
   * Bewege den adjacenten Block des leeren Blocks basierend auf der Richtung des Gestens.
   * @param type Der Rückgabewert der Richtung 1:Oben 2:Unten 3:Links 5:Rechts
   * @param isAnim Ob es eine Animation gibt true: Ja, false: Nein
   */
  public void changeByDirGes(int type, boolean isAnim) {
    //1Bestimme den aktuellen Standort des leeren Blocks.
    GameItemView null_gameItemView = (GameItemView) iv_null_imagview.getTag();
    int new_x = null_gameItemView.getX();
    int new_y = null_gameItemView.getY();
    //2.Setzen Sie die entsprechenden benachbarten Koordinaten basierend auf der Richtung.
    if (type==1{//Das bedeutet, dass die leere Kachel über dem zu verschiebenden Block ist.
      new_x++;
    } else if (type==2{//Leere Kachel unter dem zu verschiebenden Block
      new_x--;
    } else if (type==3{//Leere Kachel auf der linken Seite des zu verschiebenden Blocks
      new_y++;
    } else if (type==4{//Leere Kachel auf der rechten Seite des zu verschiebenden Blocks
      new_y--;
    }
    //3.Prüfen Sie, ob der neue Koordinatenpunkt existiert
    if (new_x >= 0 && new_x < iv_game_arr.length && new_y >= 0 && new_y < iv_game_arr[0].length) {
      //Existiert, kann verschoben und Daten ausgetauscht werden
      if (isAnim) {//Mit Animation
        changeDateByImageView(iv_game_arr[new_x][new_y]);
      } else {
        changeDateByImageView(iv_game_arr[new_x][new_y], isAnim);
      }
    } else {
      //Nichts tun
    }
  }

Nun ist das Gestenevent erfolgreich implementiert~

Natürlich gibt es hier zwei Punkte zu beachten.1.Zunächst muss die onTouch()-Methode des aktuellen Activities aufgerufen werden, um die Touch-Events durch Gesten zu verarbeiten. Außerdem muss die dispatchTouchEvent()-Methode aufgerufen werden, um die Gestenevents weiterzuleiten. Wenn nicht weitergeleitet wird, funktionieren die Gestenevents im GridLayout nicht und nur die Klickereignisse werden ausgelöst.2.Fügen Sie einen Flag, der angibt, ob sich das Objekt in Bewegung befindet, hinzu. Wenn sich das Objekt in Bewegung befindet, wird nichts getan, andernfalls wird bei jedem Klick auf den kleinen Block, selbst wenn sich das Objekt in Bewegung befindet, ein Klickereignis ausgelöst und die Animation wird erneut gestartet, was ein schlechtes Benutzererlebnis verursacht.

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

6.Das Spiel beginnt mit dem Durcheinander der Kacheln und der Anzeige einer Toast-Benachrichtigung am Ende des Spiels.

Der Code ist einfach, direkt den Code hinzufügen, wobei der Toast, der angezeigt wird, ein Toast mit einer benutzerdefinierten View-Animation ist.

 //Zufällige Durcheinanderung der Bildreihenfolge
  public void randomOrder(){
    //Zufällige Durchmischung, um den Test zu erleichtern, wurde sehr klein eingestellt.
    for (int i=0;i<5;i++{
      //Daten werden nach dem Gesten, ohne Animation, ausgetauscht.
      int type = (int) (Math.random()*4)+1;
      // Log.i("sssssssssfdfdfd", "Austauschanzahl"+i+"Wert von type"+type);
      changeByDirGes(type, false);
    }
  }
  /**
   * Methode zur Beurteilung des Spielendes
   */
  public void isGameWin(){
    //Spielgewinnmarke
    boolean isGameWin = true;
    //Durchsuche jeden kleinen Block
    for (i = 0; i < iv_game_arr.length; i++{
      for (j = 0; j < iv_game_arr[0].length; j++{
        //Leere Blöcke werden nicht beurteilt, überspringe
        if (iv_game_arr[i][j] == iv_null_imagview){
          continue;
        }
        GameItemView gameItemView = (GameItemView) iv_game_arr[i][j].getTag();
        if (!gameItemView.isTrue()){
          isGameWin=false;
          //Springe aus dem inneren Schleifenzyklus aus
          break;
        }
      }
      if (!isGameWin){
        //Springe aus dem äußeren Schleifenzyklus aus
        break;
      }
    }
    //Ob das Spiel beendet ist, wird anhand eines Schalters festgestellt, und es wird eine Meldung angezeigt, wenn es beendet ist.
    if (isGameWin){
      // Toast.makeText(this, "Spiel gewonnen", Toast.LENGTH_SHORT).show();
      ToastUtil.makeText(this, "Glückwunsch, das Spiel wurde gewonnen, es wurden ",+step+"Schritt", ToastUtil.LENGTH_SHORT, ToastUtil.SUCCESS);
      step=0;
    }
  }

Nun sind die wichtigen Teile abgeschlossen, hier gibt es auch einen CustomView Toast. Eine detaillierte Erklärung des Toasts wird im nächsten Artikel gegeben, hier wird der Prozess der Implementierung eines Custom Toasts nur kurz erläutert.
Zunächst wird eine SuccessToast-Klasse erstellt, (der Lächel umfasst das linke Auge, das rechte Auge und die Lächelkurve). Wir geben den Kernprozess an. Mit Animationen wird der dynamische Prozess der Erstellung eines dynamischen Lächels dargestellt.

 @Override
  protected void onDraw(Canvas canvas) {}}
    super.onDraw(canvas);
    mPaint.setStyle(Paint.Style.STROKE);
    //Zeichnen Sie den Lächelbogen
    canvas.drawArc(rectF, 180, endAngle, false, mPaint);
    mPaint.setStyle(Paint.Style.FILL);
    if (isSmileLeft) {
    //Wenn es ein linkes Auge gibt, zeichnen Sie das linke Auge.
      canvas.drawCircle(mPadding + mEyeWidth + mEyeWidth / 2, mWidth / 3, mEyeWidth, mPaint);
    }
    if (isSmileRight) {
    //Wenn es ein Auge gibt, zeichnen Sie das rechte Auge.
      canvas.drawCircle(mWidth - mPadding - mEyeWidth - mEyeWidth / 2, mWidth / 3, mEyeWidth, mPaint);
    }
  }
 /**
   * Methode zum Starten der Animation
   * @param startF Der Anfangswert
   * @param endF Der Endwert
   * @param time Die Animationsdauer
   * @return
   */
  private ValueAnimator startViewAnim(float startF, final float endF, long time) {
    //Setzen Sie den Anfangs- und Endwert von valueAnimator.
    valueAnimator = ValueAnimator.ofFloat(startF, endF);
    //Setzen Sie die Animationsdauer.
    valueAnimator.setDuration(time);
    //Setzen Sie den Interpolator. Kontrollieren Sie die Geschwindigkeit der Animation.
    valueAnimator.setInterpolator(new LinearInterpolator());
    //Setzen Sie den Listener. Überwachen Sie die Änderung des animierten Wertes und reagieren Sie entsprechend.
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
      @Override
      public void onAnimationUpdate(ValueAnimator valueAnimator) {
        mAnimatedValue = (float) valueAnimator.getAnimatedValue();
        //Wenn der Wert von value kleiner als 0.5
        if (mAnimatedValue < 0.5) {
          isSmileLeft = false;
          isSmileRight = false;
          endAngle = -360 * (mAnimatedValue);
          //Wenn der Wert von value zwischen 0.55und 0.7zwischen
        } else if (mAnimatedValue > 0.55 && mAnimatedValue < 0.7) {
          endAngle = -180;
          isSmileLeft = true;
          isSmileRight = false;
          //Andere
        } else {
          endAngle = -180;
          isSmileLeft = true;
          isSmileRight = true;
        }
        //Neuzeichnen
        postInvalidate();
      }
    });
    if (!valueAnimator.isRunning()) {
      valueAnimator.start();
    }
    return valueAnimator;
  }

Dann erstellen Sie eine success_toast_layout.xml und beenden Sie das Layout für den Toast. Das Layout ist links und rechts (links der lachende View, rechts der TextView zur Anzeige der Hinweise).

<?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>

Letzlich wurde eine ToastUtil-Klasse erstellt, um die benutzerdefinierten Toasts zu verwalten.

/**
 * 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();
  }
}

Dadurch kann die benutzerdefinierte Toast-Nachricht in ManiActivity aufgerufen werden.
Gut, beendet.

Spiel-Quellcode:Puzzle-SpielUmsetzung
gitHub:Puzzle-Spiel.

Das ist der gesamte Inhalt dieses Artikels. Hoffe, dass er Ihnen beim Lernen hilft und dass Sie die Anleitung stark unterstützen.

Erklärung: Der Inhalt dieses Artikels wurde aus dem Internet übernommen und gehört dem Urheberrechtsinhaber. Der Inhalt wurde von Internetbenutzern freiwillig beigesteuert und hochgeladen. Diese Website besitzt keine Eigentumsrechte und hat den Inhalt nicht manuell bearbeitet. Sie übernimmt auch keine Haftung für rechtliche Probleme. Wenn Sie verdächtige Urheberrechtsinhalte finden, senden Sie bitte eine E-Mail an: notice#w3Erklärung: Der Inhalt dieses Artikels wurde aus dem Internet übernommen und gehört dem Urheberrechtsinhaber. Der Inhalt wurde von Internetbenutzern freiwillig beigesteuert und hochgeladen. Diese Website besitzt keine Eigentumsrechte und hat den Inhalt nicht manuell bearbeitet. Sie übernimmt auch keine Haftung für rechtliche Probleme. Wenn Sie verdächtige Urheberrechtsinhalte finden, senden Sie bitte eine E-Mail an: notice#w

Gefällt mir