Criando um ProgressBar indeterminado
Autor: Felipe Bonezi | Publicado: 2012-10-23T17:10:00-03:00

A linguagem do Google Android nos permite a manipulação de componentes visuais de forma fácil e prática. Utilizando alguns conceitos de animação, resources e fé, podemos criar um ProgressBar que fique executando de forma indeterminada, ou seja, sem fim.

Geralmente, utilizamos estes ProgressBar's como forma de feedback para o usuário em operações que custam tempo, diminuindo o fator "estressante" do usuário com o aplicativo. Deste jeito, a experiência para com o aplicativo se eleva.

Irei exemplificar um ProgressBar, onde algum processando está sendo feito em background, enquanto este ProgressBar é exibido. Para isto, utilizaremos de uma classe e dois arquivos XML, que serão nossos "shapes" onde será feito uma animação de transposição das imagens.

shape_white.xml:


<?xml version="1" encoding="utf-8">
<layer-list
   xmlns:android="http://schemas.android.com/apk/res/android" >

   
      
         
      
   





shape_orange.xml:



<?xml version="1" encoding="utf-8">
<layer-list
   xmlns:android="http://schemas.android.com/apk/res/android" >

   
      
         
      
   





Esses dois shapes serão utilizados para termos o efeito de animação visível ao usuário. A única coisa que faremos será a transposição dessas duas imagens pelas ImageViews correspondentes do layout abaixo.



layout.xml:



<?xml version="1" encoding="utf-8">
<RelativeLayout
   xmlns:android="http://schamas.android.com/apk/res/android"
   android:id="@+id/relativeLayout01"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:gravity="center"
   android:ignoreGravity="@+id/txtProcessando" >

   <TextView 
      android:id="@+id/txtProcessando"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_centerHorizontal="true"
      android:layout_marginTop="30dp"
      android:text="Processando..."
      android:textColor="#FFFFFF" />

   <ImageView 
      android:id="@+id/imgThree"
      android:layout_width="20dp"
      android:layout_height="55dp"
      android:layout_below="@+id/txtProcessando"
      android:layout_marginLeft="5dp"
      android:layout_marginRight="5dp"
      android:layout_toRightOf="@+id/imgThree"
      android:src="@drawable/shape_white"
      android:tag="3" />

   <ImageView 
      android:id="@+id/imgTwo"
      android:layout_width="20dp"
      android:layout_height="55dp"
      android:layout_below="@+id/txtProcessando"
      android:layout_marginLeft="5dp"
      android:layout_toRightOf="@+id/imgFour"
      android:src="@drawable/shape_white"
      android:tag="2" />

   <ImageView 
      android:id="@+id/imgOne"
      android:layout_width="20dp"
      android:layout_height="55dp"
      android:layout_alignTop="@+id/imgTwo"
      android:layout_marginRight="5dp"
      android:layout_toRightOf="@+id/imgTow"
      android:src="@drawable/shape_white"
      android:tag="1" />

   <ImageView 
      android:id="@+id/imgFive"
      android:layout_width="20dp"
      android:layout_height="55dp"
      android:layout_alignBottom="@+id/imgOne"
      android:layout_marginRight="5dp"
      android:layout_toRightOf="@+id/imgOne"
      android:src="@drawable/shape_white"
      android:tag="5" />

   <ImageView 
      android:id="@+id/imgFour"
      android:layout_width="20dp"
      android:layout_height="55dp"
      android:layout_alignLeft="@+id/txtProcessando"
      android:layout_below="@+id/txtProcessando"
      android:layout_marginLeft="10dp"
      android:layout_toRightOf="@+id/imgTow"
      android:src="@drawable/shape_orange"
      android:tag="4" />






Com nossos shapes inseridos em seu determinado layout, agora precisamos criar nossa classe responsável por carregar esta view e efetuar a animação de transposição.


ProcessingBar.java:


public class ProcessingBar extends LinearLayout implements Runnable {

   private Context mContext;
   private List mImageHolders;
   private List mImages;
   private Thread mAnimationThread;
   private Handler mHandler;
   private boolean isAnimating;

   public ProcessingBar(Context context) {
      super(context);
      mContext = context;
      prepareLayout();

      mImageHolders = new ArrayList();
      mImageHolders.add((ImageView)view.findViewById(R.id.imgFour));
      mImageHolders.add((ImageView)view.findViewById(R.id.imgThree));
      mImageHolders.add((ImageView)view.findViewById(R.id.imgTwo));
      mImageHolders.add((ImageView)view.findViewById(R.id.imgOne));
      mImageHolders.add((ImageView)view.findViewById(R.id.imgFive));

      mImages = new ArrayList();
      mImages.add("shape_white");
      mImages.add("shape_white");
      mImages.add("shape_white");
      mImages.add("shape_orange");

      mHandler = new Handler() {
         
         @Override
         public void handleMessage(Message m) {
            int currentImg = 0;
            int nextImage = 0;
            
            for (ImageView img : mImageHolders) {
               currentImg = Integer.parseInt(img.getTag().toString());
               if (currentImg < 5) {
                  nextImage = currentImg + 1;
               } else {
                  nextImage = 1;
               }

               int resource = (getResources().getIdentifier(mImages.get(nextImage - 1), "drawable", "NOME_DO_PACOTE_DO_PROJETO"));
               imageView.setTag(String.valueOf(nextImage));
               imageView.setImageResource(resource);
            }

            super.handleMessage(msg);
         }

      };


      startAnimation();
   }

   private void prepareLayout() {
      LayoutInflater inflater = LayoutInflater.from(mContext);
      View view = inflater.inflate(R.layout.processing_dialog, null);
      addView(view);
   }

   private void startAnimation() {
      clear();
      mAnimationThread = new Thread(this);
      mAnimationThread.start();
   }

   public void clear() {
      if (!isAnimating) {
         removeAllViewsInLayout();
         prepareLayout();
      }
   }

   @Override
   public void run() {
      isAnimating = true;
      while (isAnimating) {
         Thread.sleep(300);
         mHandler.sendEmptyMessage(0);
      }
   }

}



Lendo o código acima, percebemos que ao instanciarmos o construtor da classe ProcessingBar, os métodos prepareLayout() e startAnimation() são chamados, onde o primeiro é responsável por dar um inflate no layout que definimos acima, depois todas as imagens são instanciadas baseado em suas identificações e um ArrayList com o nome dos arquivos dos shapes (e.g. shape_white , shape_orange) é criado. Já o segundo método é responsável por iniciar a Thread responsável pela animação de transposição das ImageViews, basicamente a cada 300 milisegundos uma mensagem é enviada para nosso Handler, que ao capta-las executa um trecho de código modificando cada imagem das ImageView's baseado no nome dos shapes que estão contidas no ArrayList que criamos no método prepareLayout().



Simples, correto?

O resultado pode ser visto abaixo:







Espero que gostem do resultado.