A interface é simples:
O botão Guardar vai permitir adicionar o tempo atual aos tempos intermédios. O botão Iniciar vair intercalar a interface entre a hora atual e o cronometro. O botão Recomeçar vai recolocar o temporizador a zero.
Sempre que carregar no botão Guardar o tempo do cronometro é adicionado aos tempos intermédios o que implica ter uma área que se pode deslizar para cima e para baixo uma vez que não existe limite no número de tempos que podemos adicionar.
Os desafios deste pequeno programa são dois:
- primeiro a atualização da interface implica criar uma thread separada, de outro modo o sistema não atualiza a informação mostrada ao utilizador.
- segundo temos os cálculos com tempo, neste aspeto encontrei um problema inesperado! O programa apresenta resultados diferentes quando executado no emulador de quando é executado no dispositivo real. Ainda não percebi porquê mas testei com duas versões diferentes do emulador e os resultados foram os mesmos, mas quando executei num Huawei com a versão 4.0.3 do Android os resultados não coincidiam com os apresentados pelo emulador.
A interface
Vamos começar pela interface. O código é:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<Button
android:id="@+id/bt_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:onClick="bt_guardar_click"
android:text="@string/guardar" />
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@+id/bt_start"
android:layout_marginTop="16dp"
android:textAppearance="?android:attr/textAppearanceMedium" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@+id/textView1"
android:textAppearance="?android:attr/textAppearanceMedium" />
<Button
android:id="@+id/bt_iniciar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginLeft="20dp"
android:layout_toRightOf="@+id/bt_start"
android:onClick="bt_iniciar_click"
android:text="@string/inicio" />
<Button
android:id="@+id/bt_continuar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginLeft="20dp"
android:layout_toRightOf="@+id/bt_iniciar"
android:onClick="bt_continuar_click"
android:text="@string/reiniciar" />
<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@+id/textView2"
android:layout_alignBottom="@+id/textView2"
android:layout_toRightOf="@+id/bt_start"
android:text="@string/intermedios"
android:textAppearance="?android:attr/textAppearanceLarge" />
</RelativeLayout>
</ScrollView>
Resumindo temos:
- 1 scrollview para podermos deslizar o conteúdo
- 3 botões
- 3 Textviews: 1 para mostrar a hora ou o tempo cronometrado, 1 para o texto "Intermédios" e por fim para os tempos intermédios guardados.
O código
Em relação ao código vamos apresentar a função on create
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_times_up);
tv1=(TextView) findViewById(R.id.textView1);
tv2=(TextView) findViewById(R.id.textView2);
iniciado=false;
//thread que vai atualizar a UI
Thread th = new Thread(){
@Override
public void run(){
while(true){
try {
Thread.sleep(250);
} catch (InterruptedException e) {
e.printStackTrace();
}
runOnUiThread(new Runnable(){
public void run(){
String t;
if(!iniciado){
cal = Calendar.getInstance();
t=String.format("%02d:%02d:%02d",cal.get(Calendar.HOUR_OF_DAY),cal.get(Calendar.MINUTE),cal.get(Calendar.SECOND));
tv1.setText(t);
}else{
int m=0,h=0,s=0;
cal = Calendar.getInstance();
cal.setTimeInMillis(cal.getTimeInMillis()-inicio.getTimeInMillis());
//h=cal.get(Calendar.HOUR_OF_DAY);
h=(int)cal.getTimeInMillis()/(1000*60*60);
m=cal.get(Calendar.MINUTE);
s=cal.get(Calendar.SECOND);
t=String.format("%02d:%02d:%02d",h,m,s);
tv1.setText(t);// + "-" + Long.toString(mil));
} //if else
}//run
});//runOnUiThread
}//whiler
}//run
};//thread
th.start();
}//oncreate
Este código cria um ciclo infinito que vai atualizar e apresentar a hora ou o tempo cronometrado de acordo com a variável "iniciado" que indica se o cronometro foi ou não iniciado.
A linha de código que está comentada é a que apresenta um valor diferente quando executada no emulador de quando executada no dispositivo real. Esta linha devia calcular quantas horas correspondem ao valor de milissegundos calculados como diferença entre o tempo inicial do cronometro e o atual. No emulador o valor é calculado corretamente como sendo zero inicialmente enquanto que no dispositivo começa em um!! Vai-se lá saber porquê.
Esta é a imagem do programa a correr no dispositivo real.
E esta é a imagem do programa a correr no emulador.
Para resolver este problema decidi calcular a hora de outro modo utilizando a linha
h=(int)cal.getTimeInMillis()/(1000*60*60);
Por fim criei um menu com duas opções:
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/item1" android:title="Sobre"></item>
<item android:id="@+id/item2" android:title="Sair"></item>
</menu>
O código que cria o menu e responde às opções é apresentado a seguir:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_times_up, menu);
return true;
}
//opções do menu
public boolean onOptionsItemSelected(MenuItem item) {
if(item.getTitle().toString().equals("Sobre"))
showMessage("Paulo Ferreira");
if(item.getTitle().toString().equals("Sair")){
showMessage("That's all folks!");
finish();
}
return true;
}
A primeira função é criada pelo Eclipse automaticamente enquanto que a segunda é a que reage ao clique nas opções dos menus.
A função showMessage é foi apresentada noutros projetos e somente cria uma Toast com o texto que lhe foi passado:
/* mostra o texto passado numa pequena mensagem */
private void showMessage(CharSequence text) {
Context context = getApplicationContext();
int duration = Toast.LENGTH_SHORT;
//faz aparecer uma mensagem pequena durante um certo tempo
Toast toast = Toast.makeText(context, text, duration);
toast.show();
}
Por fim temos as funções que implementam a lógica dos três botões:
//botão guardar
public void bt_guardar_click(View v){
String t;
t=(String) tv2.getText();
t = t + "\n" + tv1.getText();
tv2.setText(t);
}
//botão iniciar
public void bt_iniciar_click(View v){
if(inicio==null)
inicio = Calendar.getInstance();
iniciado=!iniciado;
}
//botão recomeçar
public void bt_continuar_click(View v){
inicio = Calendar.getInstance();
}
Um dos problemas que ainda temos para resolver é o que acontece quando mudamos a orientação do dispositivo, uma vez que o programa é reiniciado o tempo do temporizador perdido. Assim podemos fixar a orientação em vertical (portait) com a seguinte linha:
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
Basta adicionar no inicio da função onCreate.
Uma funcionalidade que fica para depois, espero que não muito depois, é adicionar a possibilidade de guardar os tempos para mais tarde poder recarregar e comparar.
Como sidekick temos uma caraterística interessante que permite que o programa mesmo não estando aberto em primeiro plano continua a calcular o tempo no cronometro graças ao modo como o Android executa os programas, assim podemos ter o programa aberto a executar em modo cronometro e atender chamadas ou executar outro programa que quando voltamos o tempo continua.
Sem comentários:
Enviar um comentário