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

Jogo de puzzle do Android: brinque desde o básico até as mudanças de gestos de aplicação

Acredita-se que, quando pequenos, todos jogaram jogos de quebra-cabeças. Atualmente, com a popularização dos smartphones, há cada vez mais jogos disponíveis para serem jogados no telefone, então, para recordar a infância, escrevi este jogo de quebra-cabeças simples e também para aprofundar conhecimentos básicos do Android.
Como sempre, primeiro vamos ao gráfico:

Aqui, para efeito de demonstração, eu arrumei a imagem muito pouco, você pode alterá-la no código.

Primeiro, há uma imagem padrão, que pode ser usada para o quebra-cabeça, ou você pode escolher a imagem que gosta para o quebra-cabeça, o processo de quebra-cabeça registrará o número de passos movidos e, quando o jogo for vencido, exibirá uma mensagem de sorriso indicando a vitória e o número de passos usados.

ps: aqueles interessados podem continuar expandindo aqui, por exemplo, adicionando opções de dificuldade do jogo, dividindo a imagem em mais pequenos quadrados etc.

A ideia geral: cortar a grande imagem em pequenos quadrados, registrar as informações de cada pequeno quadrado em um array, usar GridLayout para exibir cada pequeno quadrado e marcar um pequeno quadrado vazio (o quadrado vazio pode ser trocado com quadrados adjacentes), adicionar eventos de clique aos pequenos quadrados no GridLayout e eventos de gestos em toda a tela, cada vez que houver um clique ou gesto, verificar se o pequeno quadrado pode ser movido e, finalmente, ao vencer o jogo, exibir uma mensagem de vitória.
Sem mais delongas, vamos diretamente para a implementação passo a passo do processo do jogo de quebra-cabeça.

1Classe relacionada ao pequeno bloco.

Este é o item de várias variáveis do pequeno bloco, a classe, usada para gerenciar as informações de cada pequeno bloco cortado da grande imagem. É muito simples, apenas várias variáveis e métodos Setter e Getter diretamente no código ~

/**
 * Created by yyh on 2016/10/21.
 */
public class GameItemView{
  /**
   * As informações do pequeno bloco
   */
  //A posição real do pequeno bloco x,
  private int x=0;
  //A posição real do pequeno bloco y,
  private int y=0;
  //A imagem do pequeno bloco,
  private Bitmap bm;
  //A posição da imagem do pequeno bloco x,
  private int p_x=0;
  //A posição da imagem do pequeno bloco.
  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;
  }
  /**
   * Verificar se a posição de cada小块 está correta
   * @return
   */
  public boolean isTrue(){
    if (x==p_x&&y==p_y){
      return true;
    }
    return false;
  }
}

2.Layout da interface principal

A interface principal é simples, um Botão para trocar a imagem, um ImageView para exibir a imagem original, um GridLayout para o jogo de quebra-cabeça e, finalmente, um TextView para mostrar o número de passos usados para completar o quebra-cabeça. A layout é a seguinte:

<?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="Selecionar imagem"
      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>
  !-- Tela principal do jogo-->
  <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="Passos usados: 0"
    android:textSize="26sp"
    />
</RelativeLayout>

3.Abrir a seleção de imagem

Para definir um evento de clique no Botão, chame o método startActivityForResult(Intent intent, int requestCode); para obter a imagem.

 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);//abrir o álbum de fotos
      }
    });

No Activity, sobrescreva o método onActivityResult(int requestCode, int resultCode, Intent dados) para exibir a imagem escolhida e inicializar o jogo. (Após a seleção da imagem, deve ser feita a corte da imagem e o início do jogo de quebra-cabeça.)

 protected void onActivityResult(int requestCode, int resultCode, Intent dados) {
    super.onActivityResult(requestCode, resultCode, dados);
    switch (requestCode) {
      case CHOICE_PHOTO::
        if (resultCode == RESULT_OK) {
          //julgar a versão do sistema do telefone
          if (Build.VERSION.SDK_INT >=19){
            handleImageOnKitKat(dados);
            //obter a imagem da imageView
            BitmapDrawable bitmapDrawable = (BitmapDrawable) photo.getDrawable();
            bt_tupan = bitmapDrawable.getBitmap();
            //remova os pequenos quadrados do GridLayout original,
            removeGameItem();
            //corte a nova imagem em pequenos quadrados e adicione ao GridLayout.
            setGameItem();
            //iniciar o jogo
            startGame();
          }else {
            handleImageBeforeKitKat(dados);
            //obter a imagem da imageView
            BitmapDrawable bitmapDrawable = (BitmapDrawable) photo.getDrawable();
            bt_tupan = bitmapDrawable.getBitmap();
             //remova os pequenos quadrados do GridLayout original,
            removeGameItem();
            //corte a nova imagem em pequenos quadrados e adicione ao GridLayout.
            setGameItem();
            //iniciar o jogo
            startGame();
          }
        }
    }
  }

Depois disso, é a implementação da função específica para escolher a imagem. As notas são claras, não há muito a dizer. Nosso foco está na implementação específica do quebra-cabeça e das mudanças de gestos, aqui há muitas maneiras de escolher a imagem. Não vou discutir mais, há frameworks prontos na internet.

 //o telefone não é maior que19método de obter dados
  private void handleImageBeforeKitKat(Intent dados) {
    Uri uri = dados.getData();
    String imagePath = getImagePath(uri, null);
    displayImage(imagePath);
  }
  /**
   * o telefone é maior que19método de obter dados
   * @param dados
   */
  @TargetApi(KITKAT_VERSION)
  private void handleImageOnKitKat(Intent data) {
    String imagePath=null;
    Uri uri=data.getData();
    if (DocumentsContract.isDocumentUri(this,uri)){
      //Se o url for do tipo document, processar através do id do document.
      String docId=DocumentsContract.getDocumentId(uri);
      if ("com.android.providers.media.documents".equals(uri.getAuthority())){
        String id =docId.split(":")[1];//Estrair o id em formato numérico;
        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 o uri não for do tipo document, usar o modo normal de processamento.
      imagePath=getImagePath(uri,null);
    }
    displayImage(imagePath);
  }
  /**
   * Exibir imagem
   * @param imagePath //O caminho da imagem.
   */
  private void displayImage(String imagePath) {
    if (imagePath != null) {
      Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
      if (isHeigthBigWidth(bitmap)) {
        Bitmap bt = rotaingImageView(bitmap);//roda a imagem90 graus.
        Bitmap disbitmapt = ajustBitmap(bt);
        photo.setImageBitmap(disbitmapt);
      }) {
        Bitmap disbitmap = ajustBitmap(bitmap);
        photo.setImageBitmap(disbitmap);
      }
    }
  }
  /**
   * ajustar a direção da imagem
   * @param bitmap
   * @return
   */
  private Bitmap rotaingImageView(Bitmap bitmap) {
    //ação de rotação da imagem
    Matrix matrix = new Matrix();;
    matrix.postRotate(270);
    // criar nova imagem
    Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0,
        bitmap.getWidth(), bitmap.getHeight(), matrix, true);
    return resizedBitmap;
  }
  /**
   * obter o caminho da imagem
   * @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 de formação de cada pequeno quadrado do quebra-cabeça.

Olhando para cada pequeno quadrado, usar GridLayout para implementar é o mais conveniente. Portanto, usar um GridLayout para exibir cada pequeno quadrado cortado da grande imagem, usar um array de ImageView para armazenar as informações de cada pequeno quadrado e, por padrão, definir o último pequeno quadrado como quadrado vazio.

Primeiro, são várias variáveis necessárias. Os comentários são claros.

 /**
   * Usar um array bidimensional para criar vários pequenos quadrados de jogo.
   */
  private ImageView [][] iv_game_arr=new ImageView[3][5];
  /**
   *Tela principal do jogo.
   *
   */
  private GridLayout gl_game_layout;
  //Linhas e colunas de pequenos quadrados.
  private int i;
  private int j;
  /**Variável global do bloco vazio.*/
  private ImageView iv_null_imagview;

Em seguida, obter a imagem a partir de Imageview e cortar a imagem de acordo com certas linhas e colunas (aqui o quebra-cabeça é configurado como3linha5Coluna). Armazenar as informações dos pequenos quadrados cortados em um array de ImageView. Definir Tag e ouvinte de clique para cada pequeno quadrado.

 private void setGameItem() {
    //Ajustar o tamanho da imagem.
    Bitmap abitmap=ajustBitmap(bt_tupan);
    int ivWidth=getWindowManager().getDefaultDisplay().getWidth()/5;//Largura e altura de cada pequeno bloco de jogo. Cortar em quadrados perfeitos.
    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++){
        //Cortar a grande imagem em pequenos quadrados.
        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);//Definir o padrão de cada pequeno bloco.
        iv_game_arr[i][j].setLayoutParams(new RelativeLayout.LayoutParams(ivWidth, ivWidth));
        //Definir o espaço entre os quadrados
        iv_game_arr[i][j].setPadding(2, 2, 2, 2);
        iv_game_arr[i][j].setTag(new GameItemView(i, j, bm)); //Ligar dados personalizados
        iv_game_arr[i][j].setOnClickListener(new View.OnClickListener() {
        ......
);

Claro, as imagens que escolhemos não podem ser todas do tamanho padrão, então, antes de cortar a imagem, devemos ajustar a imagem. Ajustar a imagem para5:3proporção. (Desta forma, cortado3linha5Aqui, para width, conto previamente o espaço entre cada pequeno bloco (aqui para width, conto previamente o espaço entre cada pequeno bloco).

 //Ajustar o tamanho da imagem
  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;
  }

Colocar cada pequeno quadrado na GridLayout.

 /**
   * Colocar os pequenos quadrados na GridLayout
   */
  private void startGame() {
    tv_step.setText("Passos usados: 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]);
      }
    }
    //Definir o último bloco como o bloco vazio configurado.
    setNullImageView(iv_game_arr[i-1-1]

5. Processo de clique do pequeno bloco e julgamento de gestos

Este é o núcleo do jogo de quebra-cabeça, entender a regra de mudança do pequeno bloco é entender o jogo de quebra-cabeça.

Para o evento de clique, primeiramente obter as informações variadas do pequeno bloco clicado (posição, padrão) e as informações de posição do bloco vazio, verificar se o pequeno bloco clicado está adjacentes ao bloco vazio, se estiver adjacentes, mover e trocar os dados (usar TranslateAnimation para implementar a animação de movimento), se não estiver adjacentes, não realizar nenhuma operação.
a. Método para verificar se o bloco clicado está adjacentes ao bloco vazio

/**
   *  Verificar se o bloco clicado está adjacentes ao bloco vazio.
   * @param imageView 当前点击的方块
   * @return true: adjacentes. false: não adjacentes.
   */
  public boolean isAdjacentNullImageView(ImageView imageView){
    //Obter a posição atual do bloco vazio e a posição do bloco clicado
    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()){//O bloco clicado está acima do bloco vazio
      return true;
    }else if(null_gameItemView.getY()==now_gameItem_view.getY()&&now_gameItem_view.getX()==null_gameItemView.getX()+1){//O bloco clicado está abaixo do bloco vazio
      return true;
    }else if(null_gameItemView.getY()==now_gameItem_view.getY()+1&&now_gameItem_view.getX()==null_gameItemView.getX(){//O bloco clicado está à esquerda do bloco vazio
      return true;
    }else if(null_gameItemView.getY()+1==now_gameItem_view.getY()&&now_gameItem_view.getX()==null_gameItemView.getX(){ ////O bloco clicado está à direita do bloco vazio
      return true;
    }
    return false;
  }

b. Em seguida, se adjacentes, entra no método de troca de dados do bloco
Aqui há uma sobrecarga de método, se precisa de efeito de animação, a troca de dados sem animação é para preparar a confusão do quebra-cabeças no inicialização do jogo. Aqui está listado o código de troca de núcleo. Após cada troca, ainda é necessário verificar se o jogo foi vencido (ou seja, se o quebra-cabeças foi completado ~).

    //Obter dados do bloco clicado vinculado
      GameItemView gameItemView = (GameItemView) itemimageView.getTag();
      //Definir o padrão do bloco vazio como o bloco clicado
      iv_null_imagview.setImageBitmap(gameItemView.getBm());
      //Obter dados do bloco vazio vinculado
      GameItemView null_gameItemView = (GameItemView) iv_null_imagview.getTag();
      //trocar dados (passar os dados do bloco clicado para o bloco vazio)
      null_gameItemView.setBm(gameItemView.getBm());
      null_gameItemView.setP_x(gameItemView.getP_x());
      null_gameItemView.setP_y(gameItemView.getP_y());
      //configurar o bloco clicado como bloco vazio.
      setNullImageView(itemimageView);
      if (isStart){
        isGameWin();//ao sucesso, um toast será exibido.
      }

c. configuração da animação ao trocar
ao trocar a configuração da animação, primeiro julgar a direção de movimento, configurar diferentes animações de movimento com base na direção, e então escutar o final da animação, realizar a operação de troca de dados. Ou seja, acima b. Em seguida, se adjacentes, entrar no método de troca de dados do bloco. Finalmente, executar a animação.

 //1.criar uma animação, configurar a direção, configurar a distância de movimento
//julgar a direção, configurar animação
    if (itemimageView.getX()>iv_null_imagview.getX()){//O bloco clicado está acima do bloco vazio
      //mover para baixo
      translateAnimation = new TranslateAnimation(0.1f,-itemimageView.getWidth(),0.1f,0.1f);
    }else if (itemimageView.getX()<iv_null_imagview.getX()){//O bloco clicado está abaixo do bloco vazio
      //mover para cima
      boolean f=itemimageView.getX()<iv_null_imagview.getX();
      //Log.i("clicar bloco","sssssssssssssssssssssssss"+f);
      translateAnimation = new TranslateAnimation(0.1f,itemimageView.getWidth(),0.1f,0.1f);
    }else if (itemimageView.getY()>iv_null_imagview.getY()){//O bloco clicado está à esquerda do bloco vazio
      //mover para a direita
      translateAnimation=new TranslateAnimation(0.1f,0.1f,0.1f,-itemimageView.getWidth());
    else if(itemimageView.getY()<iv_null_imagview.getY()){//O bloco clicado está à direita do bloco vazio
      //mover para a esquerda
      translateAnimation=new TranslateAnimation(0.1f,0.1f,0.1f,itemimageView.getWidth());
    }
    //2.Definir vários parâmetros da animação
    translateAnimation.setDuration(80);
    translateAnimation.setFillAfter(true);
    //3.Definir o ouvinte da animação
    translateAnimation.setAnimationListener(new Animation.AnimationListener() {
      @Override
      public void onAnimationStart(Animation animation) {
        isAminMove=true;
      }
      @Override
      public void onAnimationEnd(Animation animation) {
        //Fim da animação, troca de dados
        ......
      }
 //Execução da animação
    itemimageView.startAnimation(translateAnimation);

O fluxo do evento de clique está concluído, agora é o evento de julgamento de gestos. Isso significa que não só é possível mover o quadrado pequeno clicando nele, mas também é possível mover o quadrado pequeno com gestos.

One. Criar objeto de gestos
Concluir operações de gestos no método onFling.

 //Criar objeto de gestos
    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) {
      //Operações relacionadas a gestos
      ......
  }

Em seguida, realizamos operações específicas no método onFling

Two. Determinar a direção do movimento do gesto
Obter diferentes direções de movimento com base em diferentes valores de retorno.

 /**
   * Aumentar o deslize do gesto, determinar se é deslize de cima para baixo ou esquerda para direita com base no gesto
   * @param start_x Ponto inicial do gesto x
   * @param start_y Ponto inicial do gesto y
   * @param end_x Ponto final do gesto x
   * @param end_y Ponto final do gesto y
   * @return 1direção: cima 2direção: baixo 3direção: esquerda 4direção: direita
   */
  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))&63;true:false; //Se é esquerda ou direita
    if(isLeftOrRight){//esquerda e direita
      boolean isLeft=(end_x-start_x)>0&63;false:true;
      if(isLeft){
        return 3;
      }else {
        return 4;
      }
    }else{//cima e baixo
      boolean isUp=(end_y-start_y)>0&63;false:true;
      if (isUp){
        return 1;
      }else {
        return 2;
      }
    }
  }

Three. Determinar se pode mover e realizar a operação de movimento com base no bloco vazio e na direção do movimento.
Como é um gesto, o movimento será certamente ao redor do bloco vazio, então o ponto principal é determinar a direção do bloco vazio na direção do bloco a ser movido, e então determinar se pode ser movido com base na direção, realizar a operação de movimento. (O método changeDateByImageView() é a operação específica de troca de dados e movimento do bloco. É o método do evento de clique.)

/**Sobrecarregar o método changeByDirGes(int type);
   * Segundo a direção do gesto, mover os blocos adjacentes ao bloco vazio.
   * @param type Valor de retorno da direção 1direção: cima 2direção: baixo 3direção: esquerda 5direção: direita
   * @param isAnim Se há animação true: há animação, false: não há animação
   */
  public void changeByDirGes(int type, boolean isAnim){
    //1Obter a posição atual do bloco vazio.
    GameItemView null_gameItemView = (GameItemView) iv_null_imagview.getTag();
    int new_x=null_gameItemView.getX();
    int new_y=null_gameItemView.getY();
    //2.De acordo com a direção, configure as coordenadas adjacentes correspondentes.
    if (type==1){//Isso significa que o bloco vazio está acima do bloco a ser movido.
      new_x++;
    }else if (type==2){//O bloco vazio está abaixo do bloco a ser movido
      new_x--;
    }else if (type==3){//O bloco vazio está à esquerda do bloco a ser movido
      new_y++;
    }else if (type==4){//O bloco vazio está à direita do bloco a ser movido
      new_y--;
    }
    //3.Verifique se existe essa nova coordenada
    if(new_x>=0&&new_x<iv_game_arr.length&&new_y>=0&&new_y<iv_game_arr[0].length){
      //Existe, pode mover e trocar dados
      if(isAnim){//Há animação
        changeDateByImageView(iv_game_arr[new_x][new_y]);
      }else{
        changeDateByImageView(iv_game_arr[new_x][new_y],isAnim);
      }
    }else{
      //Nada fazer
    }
  }

Bom, o evento de gesto está pronto!

Claro, há dois pontos a serem notados aqui.1.Primeiro, configure o método onTouchEvent() do Activity atual, faça com que o evento de toque seja tratado pelo gesto, e também configure o método dispatchTouchEvent(), onde também deve distribuir o evento de gesto para baixo, se não configurar para distribuir o evento de gesto para baixo, então no GridLayout, só pode desencadear o evento de clique e o evento de gesto não funcionará.2.Precisa adicionar um flag para verificar se está em movimento, se estiver em movimento, não faz nada, senão, cada clique no pequeno bloco, mesmo em movimento, desencadeia o evento de clique e realiza o movimento de animação, causando uma má experiência do usuário.

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

6.O jogo começa a desorganizar os blocos e, ao final, exibe uma mensagem de Toast.

O código é simples, vamos direto ao ponto, entre eles, o Toast expandido é um Toast com animação de View personalizada.

 //Desordene a sequência das imagens aleatoriamente
  public void randomOrder(){
    //Número de vezes de bagunça, para facilitar o teste, configurado para ser muito pequeno.
    for (int i=0;i<5;i++){
      //Troque os dados com gestos, sem animação.
      int type = (int) (Math.random()*4)+1;
      // Log.i("sssssssssfdfdfd","Número de trocas"+i+"Valor de type"+type);
      changeByDirGes(type, false);
    }
  }
  /**
   * Método de decisão do final do jogo
   */
  public void isGameWin(){
    //Sinal de vitória do jogo
    boolean isGameWin =true;
    //Percorra cada pequeno bloco
    for (i = 0; i <iv_game_arr.length; i++){
      for (j = 0; j <iv_game_arr[0].length; j++){
        //Não há necessidade de julgar o bloco vazio, pule
        if (iv_game_arr[i][j]==iv_null_imagview){
          continue;
        }
        GameItemView gameItemView= (GameItemView) iv_game_arr[i][j].getTag();
        if (!gameItemView.isTrue()){
          isGameWin=false;
          //Sai do loop interno
          break;
        }
      }
      if (!isGameWin){
        //Sai do loop externo
        break;
      }
    }
    //Decida se o jogo termina com uma variável de interruptor, dê uma dica quando terminar.
    if (isGameWin){
      // Toast.makeText(this,"Você venceu o jogo", Toast.LENGTH_SHORT).show();
      ToastUtil.makeText(this,"Parabéns, você venceu o jogo, usou"+step+"Passo", ToastUtil.LENGTH_SHORT, ToastUtil.SUCCESS);
      step=0;
    }
  }

Bom, a parte importante já está concluída, aqui também há um Toast de View personalizada, uma explicação detalhada sobre Toast será fornecida no artigo seguinte, aqui vamos explicar brevemente o processo de implementação do Toast personalizado.
Primeiro, crie uma classe SuccessToast (um sorriso inclui olho esquerdo, olho direito, arco de sorriso). Vamos fornecer o processo central. Utilize animação para implementar o processo dinâmico de desenho de sorriso.

 @Override
  protected void onDraw(Canvas canvas) {}}
    super.onDraw(canvas);
    mPaint.setStyle(Paint.Style.STROKE);
    //Desenhar arco de sorriso
    canvas.drawArc(rectF, 180, endAngle, false, mPaint);
    mPaint.setStyle(Paint.Style.FILL);
    if (isSmileLeft) {
    //Se for o olho esquerdo, desenhar o olho esquerdo
      canvas.drawCircle(mPadding + mEyeWidth + mEyeWidth / 2, mWidth / 3, mEyeWidth, mPaint);
    }
    if (isSmileRight) {
    //Se houver olhos, desenhar o olho direito.
      canvas.drawCircle(mWidth - mPadding - mEyeWidth - mEyeWidth / 2, mWidth / 3, mEyeWidth, mPaint);
    }
  }
 /**
   * Método para iniciar a animação
   * @param startF O valor inicial
   * @param endF O valor final
   * @param time O tempo da animação
   * @return
   */
  private ValueAnimator startViewAnim(float startF, final float endF, long time) {
    //Definir os valores inicial e final do valueAnimator.
    valueAnimator = ValueAnimator.ofFloat(startF, endF);
    //Definir o tempo da animação
    valueAnimator.setDuration(time);
    //Definir o interpolador. Controlar a velocidade de variação da animação
    valueAnimator.setInterpolator(new LinearInterpolator());
    //Definir o ouvinte. Observar a variação do valor da animação e reagir de maneira apropriada.
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
      @Override
      public void onAnimationUpdate(ValueAnimator valueAnimator) {
        mAnimatedValue = (float) valueAnimator.getAnimatedValue();
        //se o valor de value for menor que 0.5
        se (mAnimatedValue < 0.5) {
          isSmileLeft = false;
          isSmileRight = false;
          endAngle = -360 * (mAnimatedValue);
          //se o valor de value estiver em 0.55e 0.7entre
        }) {55 && mAnimatedValue < 0.7) {
          endAngle = -180;
          isSmileLeft = true;
          isSmileRight = false;
          //outros
        }) {
          endAngle = -180;
          isSmileLeft = true;
          isSmileRight = true;
        }
        //redesenhar
        postInvalidate();
      }
    });
    if (!valueAnimator.isRunning()) {
      valueAnimator.start();
    }
    return valueAnimator;
  }

Então, crie um arquivo success_toast_layout.xml, complete a configuração do toast. A configuração é de esquerda para direita (view de sorriso na esquerda, TextView de aviso na direita).

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

Finally, create a ToastUtil class to manage the custom 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();
  }
}

Dessa forma, você pode chamar este Toast personalizado no ManiActivity.
Bom, concluído.

Código-fonte do jogo:Jogo de Puzzleo processo de implementação
gitHub:Jogo de Puzzle.

Isso é tudo o que há no artigo. Esperamos que isso ajude na sua aprendizagem e que você apoie o tutorial Yell.

Declaração: o conteúdo deste artigo é de origem na internet, pertencente ao respectivo proprietário. O conteúdo é contribuído e carregado voluntariamente pelos usuários da internet. Este site não possui direitos de propriedade, não foi editado manualmente e não assume responsabilidade por eventuais responsabilidades legais. Se você encontrar conteúdo suspeito de violação de direitos autorais, por favor, envie um e-mail para: notice#oldtoolbag.com (ao enviar e-mail, substitua # por @ para denunciar e forneça provas relevantes. Caso seja confirmado, o site deletará imediatamente o conteúdo suspeito de violação de direitos autorais.)

Você também pode gostar