quarta-feira, 16 de dezembro de 2015

Criando um contador em Android utilizando Handle em uma classe externa

A maioria dos exemplos de códigos de Threads e Handlers em Android executam todo código na Activity principal. 
Neste exemplo mostro uma abordagem de separar a Thread em uma Classe separada com métodos para controlar o estado de execução da Thread, deixando o código mais limpo.


Crie uma classe ContadorHandle.class. Esta classe receberá o EditText onde será atualizado o contador.

public class ContadorHandle implements Runnable {
    private TextView txtView;
    private Handler handler;
    private boolean running = false;
    private int counter = 0;
    private int interval=1000;

    public ContadorHandle(TextView txtView) {

        this.txtView = txtView;
        handler = new Handler();
    }
    public int getInterval() {
        return interval;
    }
    public void setInterval(int interval) {
        this.interval = interval;
    }
    public void start() {
        if (running == false) {
            running = true;
            handler.postDelayed(this, interval);
            Log.i("LOG", "START");
        } else{
            Log.i("LOG", "ALREADY RUNNING");
        }
    }
    public void resume(){
        start();
    }
    public void restart(){
        stop();
        start();
    }
    public void stop() {
        Log.i("LOG", "STOPPED");
        running = false;
        handler.removeCallbacks(this);
        // No stop eu reinicio as variaveis       
        counter = 0;
        txtView.setText("" + 0);
    }
    public void pause() {
        Log.i("LOG", "PAUSED");
        running = false;
        handler.removeCallbacks(this);
    }    public boolean isRunning() {
        return running;
    }
    @Override    
     public void run() {
        counter++;
        handler.postDelayed(this, interval);
        txtView.setText("" + counter);
    }
}

Como utilizar esta classe:
 1) Instancie a classe 
 2) Configure o intervalo de tempo
 3) Utilize os métodos para controlar o estado da Thread.

Exemplo de utilização na MainActivity

@Override 

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    final Button startButton = (Button) findViewById(R.id.start_button);

    final Button stopButton = (Button) findViewById(R.id.stop_button);
    final TextView txtView = (TextView) findViewById(R.id.txtview);

    // Instancia o contador com handle e passa o TextView que será atualizado

    final ContadorHandle contador = new ContadorHandle(txtView);

    // Configure o intervalo de execução

    contador.setInterval(1000); // 1 segundo

    // Se clicar no botão PAUSAR mudar o texto para CONTINUAR

    startButton.setOnClickListener(new View.OnClickListener() {
        @Override 
        public void onClick(View v) {
            if (!contador.isRunning()) {
                contador.start();    // Inicia ou continua a contagem
                startButton.setText("PAUSAR");
            } else {
                contador.pause();    // Pausa a contagem
                startButton.setText("CONTINUAR");
            }
        }
    });

    // Evento de clique do botão STOP     
       stopButton.setOnClickListener(new View.OnClickListener() {
        @Override        
        public void onClick(View v) {
            contador.stop();        // Para o contador (volta em zero)
            startButton.setText("INICIAR");  
        }
    });

}


Arquivo activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout    
xmlns:android="http://schemas.android.com/apk/res/android"   
xmlns:app="http://schemas.android.com/apk/res-auto"    
xmlns:tools="http://schemas.android.com/tools"    
android:layout_width="match_parent"    
android:layout_height="match_parent"    
android:orientation="vertical"    
app:layout_behavior="@string/appbar_scrolling_view_behavior"    
tools:context=".MainActivity"    
tools:showIn="@layout/activity_main">

    <LinearLayout        

 android:orientation="horizontal"        
 android:layout_width="match_parent"        
 android:layout_height="wrap_content">

        <TextView            

  android:layout_width="100dp"            
  android:layout_height="wrap_content"            
  android:textAppearance="?android:attr/textAppearanceMedium"            
  android:text="HANDLE"            
  android:id="@+id/textView"            />

        <TextView            

  android:id="@+id/txtview"            
  android:layout_width="wrap_content"            
  android:layout_height="wrap_content"            
  android:gravity="center_horizontal"            
  android:text="0"            
  android:textSize="30dp"            
  android:layout_weight="1"/>
    </LinearLayout>

    <LinearLayout        

 android:layout_width="match_parent"        
 android:layout_height="wrap_content"        
 android:orientation="horizontal">

        <Button            

  android:id="@+id/start_button"            
  android:layout_width="wrap_content"            
  android:layout_height="wrap_content"            
  android:layout_weight="1"            
  android:text="INICIAR"/>

        <Button            

  android:id="@+id/stop_button"            
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"            
  android:layout_weight="1"            
  android:text="PARAR"/>
    </LinearLayout>

</LinearLayout>


Vídeo do contador:




A desvantagem desse exemplo é que utilizando Handler desta forma não oferece uma precisão muito boa, com o tempo é possível verificar que o contador começa atrasar a contagem de segundos. De qualquer forma, essa classe permite ser adaptada para outros fins onde não é necessário muita precisão, como por exemplo fazer a animação de uma figura.


Exemplo da classe ContadorHandle adaptada para girar uma figura:

public class RotateImageHandle implements Runnable {
    private ImageView imageView;
    private Handler handler;
    private boolean running = false;
    private float rotation = 0;
    private int interval = 1;

    public RotateImageHandle(ImageView imageView) {
        this.imageView = imageView;
        handler = new Handler();
    }

    public int getInterval() {
        return interval;
    }

    public void setInterval(int interval) {
        this.interval = interval;
    }

    public void start() {
        if (running == false) {
            running = true;
            handler.postDelayed(this, interval);
            Log.i("LOG", "START");
        } else {
            Log.i("LOG", "ALREADY RUNNING");
        }
    }

    public void stop() {
        Log.i("LOG", "STOP");
        running = false;
        handler.removeCallbacks(this);
        // No stop eu reinicio as variaveis
        rotation = 0;
        imageView.setRotation(0);
    }

    public void pause() {
        Log.i("LOG", "PAUSE");
        running = false;
        handler.removeCallbacks(this);
    }

    public boolean isRunning() {
        return running;
    }

    @Override
    public void run() {
        rotation = rotation + 8;
        handler.postDelayed(this, interval);
        imageView.setRotation(rotation);
    }
}

Esta classe funciona igual ao ContadorHandle, mas ao invés de receber um EditText recebe um ImageView, e começa a rotacionar a imagem com o método start.

Para criar um contador com maior precisão é necessário utilizar a classe TimerTask, que mostro no post a seguir.

Até mais.

Nenhum comentário:

Postar um comentário