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

Gioco di puzzle in Android: esplora le trasformazioni delle gesture dal livello base all'applicazione

Credo che tutti, quando erano bambini, abbiano giocato a giochi di puzzle. Ora, con la diffusione dei telefoni cellulari, ci sono sempre più giochi disponibili sui telefoni cellulari. Pertanto, per ripetere i ricordi dell'infanzia, ho scritto questo gioco di puzzle semplice e anche per approfondire alcune basi di Android.
Secondo la regola, ecco la visualizzazione di esempio:

In questo esempio, ho disordinato poco l'immagine per l'effetto dimostrativo, ma è possibile modificare questo nella codifica.

Prima di tutto, c'è un'immagine predefinita che può essere utilizzata per il puzzle, ma è possibile scegliere l'immagine preferita per il puzzle. Il processo di puzzle registra il numero di mosse effettuate e, quando il gioco viene vinto, appare un avviso sorridente che indica la vittoria e il numero di mosse utilizzate.

ps: Chi è interessato può continuare a espandere qui, ad esempio aggiungendo opzioni di difficoltà del gioco, dividendo le immagini in più piccoli quadrati, ecc.

La generale idea: tranciare l'immagine grande in piccoli quadrati, registrare le informazioni di ciascun piccolo quadrato in un array, utilizzare GridLayout per visualizzare i vari quadrati, e contrassegnare un piccolo quadrato come quadrato vuoto (il quadrato vuoto può essere scambiato con quadrati adiacenti), aggiungere eventi di clic sui vari quadrati su GridLayout e eventi di gesti su tutta la schermata, ogni volta che si clicca o si ha un gesto, si verifica se il piccolo quadrato può essere mosso, infine, quando il gioco è vinto, viene visualizzato un messaggio di vittoria.
Non c'è molto da dire, quindi, procediamo passo dopo passo con l'implementazione del processo di puzzle game.

1. La classe relativa al piccolo quadrato.

Questo è il variabile item del piccolo quadrato, la classe, utilizzata per gestire le informazioni di ciascun piccolo quadrato tranciato da un'immagine grande. È molto semplice, sono variabili e metodi Setter e Getter, ecco il codice~

/**
 * 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;
  }
  /**
   * Verifica la posizione corretta di ogni piccolo blocco
   * @return
   */
  public boolean isTrue(){
    if (x==p_x&&y==p_y){
      return true;
    }
    return false;
  }
}

2. Layout dell'interfaccia principale

L'interfaccia principale è semplice, un pulsante per cambiare l'immagine, un ImageView per mostrare l'immagine originale, un GridLayout per il gioco del puzzle e infine un TextView per mostrare il numero di passi necessari per completare il puzzle. La layout è la seguente:

<?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="Scegli immagine"
      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>
  <!-- Interfaccia principale del gioco-->
  <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="Passi utilizzati: 0"
    android:textSize="26sp"
    />
</RelativeLayout>

3. Apri l'immagine e seleziona l'immagine

imposta l'evento di click per il pulsante, chiama il metodo startActivityForResult(Intent intent, int requestCode); per ottenere l'immagine.

 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);//打开相册
      }
    });

Riscrivi il metodo onActivityResult(int requestCode, int resultCode, Intent data) nell'Activity per visualizzare l'immagine selezionata e inizializzare il gioco. (Dopo aver selezionato l'immagine, è necessario tagliare l'immagine e iniziare il gioco del puzzle.)

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

Poi è la funzione di implementazione del metodo specifico per la selezione dell'immagine. I commenti sono chiari, non c'è molto da dire. Il nostro punto focale è l'implementazione specifica del puzzle e delle variazioni delle gesture, qui ci sono molti modi per selezionare l'immagine. Non c'è molto da dire, ci sono molte librerie pronte disponibili su Internet.

 //手机不大于19的取数据方法
  private void handleImageBeforeKitKat(Intent data) {
    Uri uri =data.getData();
    String imagePath = getImagePath(uri, null);
    displayImage(imagePath);
  }
  /**
   * Metodo per ottenere i dati per dispositivi con versione maggiore di 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)){
      //Se è un url di tipo document, gestire tramite l'id del document.
      String docId=DocumentsContract.getDocumentId(uri);
      if ("com.android.providers.media.documents".equals(uri.getAuthority())){
        String id =docId.split(":")[1];//Estrae l'id in formato numerico;
        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())){
      //Se non è un uri di tipo document, utilizzare il metodo normale di gestione.
      imagePath = getImagePath(uri, null);
    }
    displayImage(imagePath);
  }
  /**
   * Mostrare l'immagine
   * @param imagePath // Percorso dell'immagine.
   */
  private void displayImage(String imagePath) {
    if (imagePath != null) {
      Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
      if (isHeigthBigWidth(bitmap)) {
        Bitmap bt = rotaingImageView(bitmap); // Rotare l'immagine di 90 gradi.
        Bitmap disbitmapt = ajustBitmap(bt);
        photo.setImageBitmap(disbitmapt);
      } else {
        Bitmap disbitmap = ajustBitmap(bitmap);
        photo.setImageBitmap(disbitmap);
      }
    }
  }
  /**
   * Regolare la direzione dell'immagine
   * @param bitmap
   * @return
   */
  private Bitmap rotaingImageView(Bitmap bitmap) {
    // Azione di rotazione dell'immagine
    Matrix matrix = new Matrix();;
    matrix.postRotate(270);
    // Creare una nuova immagine
    Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0,
        bitmap.getWidth(), bitmap.getHeight(), matrix, true);
    return resizedBitmap;
  }
  /**
   * Ottenere il percorso dell'immagine
   * @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. processo di formazione dei vari quadrati del rompicapo.

Guardando i vari piccoli quadrati, usare GridLayout è il più conveniente. Quindi, usare un GridLayout per visualizzare i vari quadrati tagliati dall'immagine grande, usare un array di ImageView per salvare le informazioni di ciascun quadrato, e di default impostare l'ultimo quadrato come quadrato vuoto.

Prima ci sono vari variabili necessarie. I commenti sono chiari.

 /**
   *creare diversi piccoli riquadri di gioco utilizzando un array bidimensionale
   */
  private ImageView[][] iv_game_arr=new ImageView[3][5];
  /**
   *interfaccia principale del gioco
   *
   */
  private GridLayout gl_game_layout;
  //righe e colonne del quadrato piccolo
  private int i;
  private int j;
  /**variabile globale per quadrato vuoto*/
  private ImageView iv_null_imagview;

Quindi, ottenere l'immagine dall'Imageview e tagliare l'immagine secondo una certa riga e colonna (qui impostare il rompicapo a 3 righe e 5 colonne). Salvare le informazioni di ciascun piccolo quadrato in un array di ImageView. Impostare Tag e listener di clic per ogni piccolo quadrato.

 private void setGameItem() {
    //adattare la dimensione dell'immagine
    Bitmap abitmap=ajustBitmap(bt_tupan);
    int ivWidth=getWindowManager().getDefaultDisplay().getWidth()/5;//larghezza e altezza di ogni piccolo quadrato di gioco. Tagliato in quadrato
    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++){
        //tagliare l'immagine grande in piccoli quadrati
        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); //Imposta l'immagine di ogni piccolo quadrato
        iv_game_arr[i][j].setLayoutParams(new RelativeLayout.LayoutParams(ivWidth, ivWidth));
        //Imposta lo spazio tra i quadrati
        iv_game_arr[i][j].setPadding(2, 2, 2, 2);
        iv_game_arr[i][j].setTag(new GameItemView(i, j, bm)); //Assegna dati personalizzati
        iv_game_arr[i][j].setOnClickListener(new View.OnClickListener() {
        ......
);

Naturalmente, le immagini che abbiamo scelto non possono essere tutte di dimensioni standard, quindi dobbiamo regolare le immagini prima di tagliare le immagini. Regola l'immagine per una proporzione 5:3 (così è possibile tagliare correttamente in 3 righe e 5 colonne di piccoli quadrati). Ecco come considero la distanza tra i quadrati per width.

 //Regola la dimensione dell'immagine
  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;
  }

Inserisci ogni piccolo quadrato nel GridLayout.

 /**
   * Metti i piccoli quadrati nel GridLayout
   */
  private void startGame() {
    tv_step.setText("Passi utilizzati: 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]);
      }
    }
    //Imposta l'ultimo blocco come blocco vuoto.
    setNullImageView(iv_game_arr[i-1][j-1]);

5. The click event and gesture judgment process of the small block.

This is the core of the puzzle game, understanding the movement and change rules of the small blocks will also understand the puzzle game.

For the click event, first obtain various information (position, pattern) of the clicked small block and the position information of the empty small block, determine whether the clicked small block is adjacent to the empty small block, if adjacent, then move and exchange data (using TranslateAnimation to realize the moving animation), if not adjacent, then no operation.
a. Method to determine whether the clicked block and the blank block are adjacent

/**
   *  Determine whether the current clicked block is adjacent to the empty block.
   * @param imageView The current clicked block
   * @return true: Adjacent. false: Not adjacent.
   */
  public boolean isAdjacentNullImageView(ImageView imageView){
    //Get the position of the current empty block and the position of the clicked block
    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()){//The current clicked block is above the empty block
      return true;
    else if(null_gameItemView.getY()==now_gameItem_view.getY()&&now_gameItem_view.getX()==null_gameItemView.getX()+1){//The current clicked block is below the empty block
      return true;
    else if(null_gameItemView.getY()==now_gameItem_view.getY()+1&&now_gameItem_view.getX()==null_gameItemView.getX()){//The current clicked block is on the left side of the empty block
      return true;
    else if(null_gameItemView.getY()+1==now_gameItem_view.getY()&&now_gameItem_view.getX()==null_gameItemView.getX()){ ////The current clicked block is on the right side of the empty block
      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);

La sequenza dell'evento di clic è completata, ora è l'evento di judgment del gesto. Non solo è possibile muovere il quadrato piccolo tramite clic, ma anche tramite il movimento del gesto.

One. Creare l'oggetto gesto
Eseguire le operazioni correlate al gesto nel metodo onFling.

 // Creare l'oggetto gesto
    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) {
      // Operazioni di gesto
      ......
  }

Poi eseguiamo le operazioni specifiche nel metodo onFling

Due. Determinare la direzione di movimento del gesto
Ottieni diverse direzioni di movimento in base al valore di ritorno.

 /**
   * Aggiungere lo scorrimento del gesto, determinare la direzione del gesto
   * @param start_x punto x di inizio del gesto
   * @param start_y punto y di inizio del gesto
   * @param end_x punto x di fine del gesto
   * @param end_y punto y di fine del gesto
   * @return 1: alto 2: basso 3: sinistra 4: destra
   */
  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; // è a destra o a sinistra
    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) {}}
      // esiste, può muoversi e scambiare i dati
      if(isAnim) { // c'è un'animazione
        changeDateByImageView(iv_game_arr[new_x][new_y]);
      } else {
        changeDateByImageView(iv_game_arr[new_x][new_y], isAnim);
      }
    } else {
      // non fare nulla
    }
  }

Bene, l'elaborazione degli eventi di gesto è completa~

Naturalmente ci sono due punti da notare. 1. Prima di tutto, è necessario impostare il metodo onTouch() dell'Activity corrente, passare l'evento di Touch all'elaborazione del gesto, e anche impostare il metodo dispatchTouchEvent(), dove anche deve essere distribuito l'evento di gesto. Se non viene distribuito l'evento di gesto, allora solo l'evento di click può essere attivato nel GridLayout, e l'evento di gesto non avrà alcun effetto. 2. È necessario aggiungere un flag per indicare se si è in movimento. Se si è in movimento, non fare nulla, altrimenti ogni click sui piccoli quadrati, anche durante il movimento, attiverà l'evento di click e causerà un altro movimento animato, causando una cattiva esperienza utente.

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

6. Il metodo di mescolare i quadrati all'inizio del gioco e il metodo di popolare il Toast quando il gioco termina.

Il codice è molto semplice, eccolo qui, tra cui il Toast che si apre è un Toast con un'animazione di View personalizzata.

 // casualizza l'ordine delle immagini
  public void randomOrder(){
    // numero di scambi casuali, per facilitare i test, impostato molto piccolo.
    for (int i=0;i<5;i++){
      // secondo il gesto, scambia i dati, senza animazione.
      int type = (int) (Math.random()*4)+1;
      // Log.i("sssssssssfdfdfd","交换次数"+i+"type的值"+type);
      changeByDirGes(type, false);
    }
  }
  /**
   * Metodo per determinare se il gioco è finito
   */
  public void isGameWin(){
    // Segno di vittoria del gioco
    boolean isGameWin =true;
    // Percorri ogni piccolo quadrato
    for (i = 0; i <iv_game_arr.length; i++){
      for (j = 0; j <iv_game_arr[0].length; j++){
        // Non giudicare i quadrati vuoti, salta
        if (iv_game_arr[i][j]==iv_null_imagview){
          continue;
        }
        GameItemView gameItemView= (GameItemView) iv_game_arr[i][j].getTag();
        if (!gameItemView.isTrue()){
          isGameWin=false;
          // Esci dal ciclo interno
          break;
        }
      }
      if (!isGameWin){
        // Esci dal ciclo esterno
        break;
      }
    }
    // Secondo un variabile di interruzione determina se il gioco è finito, quando è finito da dare un suggerimento.
    if (isGameWin){
      // Toast.makeText(this,"Gioco vinto",Toast.LENGTH_SHORT).show();
      ToastUtil.makeText(this,"Congratulazioni, hai vinto il gioco, hai usato"+step+"passi",ToastUtil.LENGTH_SHORT,ToastUtil.SUCCESS);
      step=0;
    }
  }

Bene, la parte importante è finita, c'è anche un Toast personalizzato View, per dettagli su Toast, vedremo nell'articolo successivo, qui spieghiamo brevemente il processo di implementazione del Toast personalizzato.
Prima di tutto, crea una classe SuccessToast, (il sorriso include l'occhio sinistro, l'occhio destro, l'arc di sorriso). Forniamo il processo principale. Utilizzando l'animazione, realizziamo il processo di disegno dinamico del sorriso.

 @Override
  protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    mPaint.setStyle(Paint.Style.STROKE);
    // sorriso arc
    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);
          } else if (mAnimatedValue > 0.55 && mAnimatedValue < 0.7) {
        }
          endAngle = -180;
          isSmileLeft = true;
          isSmileRight = false;
          //Altri
        } else {
          endAngle = -180;
          isSmileLeft = true;
          isSmileRight = true;
        }
        //Ridisegna
        postInvalidate();
      }
    });
    if (!valueAnimator.isRunning()) {
      valueAnimator.start();
    }
    return valueAnimator;
  }

Poi crea un nuovo file success_toast_layout.xml, completa la disposizione del toast. La disposizione è da sinistra a destra (view di faccia sorridente a sinistra, TextView di avviso a destra).

<?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="Nuovo Testo" />
  </LinearLayout>
</LinearLayout>

Ultimo, creare una classe ToastUtil per gestire la Toast personalizzata.

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

In questo modo è possibile chiamare questo Toast personalizzato in ManiActivity.
Bene, finito.

Codice sorgente del gioco:Gioco di puzzleprocedura di implementazione
gitHub:Gioco di puzzle.

Questo è tutto il contenuto dell'articolo, spero che sia utile per la tua apprendimento, e spero che tutti possano sostenere la guida Yell.

Dichiarazione: il contenuto di questo articolo è stato tratto da Internet, il copyright spetta agli autori, il contenuto è stato contribuito e caricato autonomamente dagli utenti di Internet, questo sito non detiene il diritto di proprietà, non è stato editato manualmente e non assume responsabilità legali correlate. Se trovi contenuti sospetti di violazione del copyright, invia un'e-mail a notice#oldtoolbag.com (sostituisci # con @) per segnalare il problema e fornire prove pertinenti. Una volta verificata, questo sito eliminerà immediatamente i contenuti sospetti di violazione del copyright.

Ti potrebbe interessare