segunda-feira, 22 de novembro de 2010

TabHost, TabWidget e TabActivity

Olá povo,

Dar aulas é um desafio bem grande, e às vezes você paga uns micos esquecendo coisas simples. Esse post é dedicado aos meus alunos da LinuxFI em João Pessoa, aos quais presenciaram mais um mico proporcionado por minha pessoa.
Estava explicando como se fazia para criar uma tela com abas (ou guias, como preferir) no Android, e como não queria fazer como estava no livro, fui fazer de outro jeito. Aí claro que deu merda. Então implementei de outro jeito e prometi que iria fazer esse post. No final acabou que um aluno (perdão por não lembrar do nome) Raniere Fernandes descobriu o que estava faltando (era um maldito tabHost.setup()), mas aí já era!

Então vamos lá! Vou mostrar primeiro a maneira mais simples. Primeiro vou criar dois arquivos de layout: aba1.xml e aba2.xml.


<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/raizAba1">

<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Nome:" />
<EditText
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:id="@+id/edtNome"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Email"/>
<EditText
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:id="@+id/edtEmail"/>
</LinearLayout>


<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/raizAba2"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Observações:"/>
<EditText
android:id="@+id/edtObs"
android:layout_height="fill_parent"
android:layout_width="fill_parent"
android:gravity="top"/>
</LinearLayout>

Notem que o elemento raiz do XML tem os identificadores raizAba1 e raizAba2. Esses identificadores serão utilizados para setar o conteúdo das abas.
Vamos agora ao código que configura a tela.

public class ExTabActivity1 extends TabActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

TabHost tabHost = getTabHost();

LayoutInflater.from(this).inflate(
R.layout.aba1, tabHost.getTabContentView());

LayoutInflater.from(this).inflate(
R.layout.aba2, tabHost.getTabContentView());

TabSpec aba1 = tabHost.newTabSpec("foo1");
aba1.setIndicator("Informações");

aba1.setContent(R.id.raizAba1);

TabSpec aba2 = tabHost.newTabSpec("foo2");
aba2.setIndicator("Observações");
aba2.setContent(R.id.raizAba2);

tabHost.addTab(aba1);
tabHost.addTab(aba2);
}
}

A classe TabActivity da qual nossa classe está herdando facilita o nosso trabalho para telas que usam abas. No método onCreate estamos obtendo a instância do TabHost que é o gerenciador de layout responsável por esse tipo de tela. Internamente ele contém um objeto TabWidget e um FrameLayout que exibe o conteúdo das abas (como veremos no próximo exemplo).

Cada aba é representada por um objeto do tipo TabSpec, que é adicionado ao TabHost. Para criar uma TabSpec chamamos o método newTabSpec do objeto TabHost passando uma tag de identificação. Para definir o texto de cada aba chamamos o método setIndicator. E para setar o conteúdo, carregamos o arquivo de layout através da chamada:

LayoutInflater.from(this).inflate(
R.layout.aba1, tabHost.getTabContentView());

Isso carregará o arquivo de layout no FrameLayout interno do TabHost. Uma vez que o layout está carregado, podemos definir o conteúdo da TabSpec, chamando o método setContent e passando o id da view raiz do arquivo de layout. No caso, R.id.raizAba1 para primeira aba e R.id.raizAba2 para a segunda.

Mas e se eu quiser ter mais do que uma TabHost na minha tela? Como, por exemplo, um botão no canto inferior? Pois é, esse era o exemplo que eu queria ter terminado em "Jampa".
Primeiro devemos criar um arquivo de layout igual ao definido abaixo, e a este, chamaremos de meuTabHost.xml:

<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">

<TabHost
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/meuTabHost"
android:layout_weight="1">
<LinearLayout
android:id="@+id/linearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >

<TabWidget
android:id="@android:id/tabs"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>

<FrameLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@android:id/tabcontent"/>
</LinearLayout>
</TabHost>

<Button
android:text="Salvar"
android:id="@+id/Button01"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
</LinearLayout>


Notem que nele, dentro de um LinearLayout, temos um TabHost, que dentro dele temos um TabWidget e um FrameLayout, conforme já tínhamos descrito. Uma atenção especial nos ids do TabHost e do TabWidget, que devem, obrigatoriamente chamar-se @android:id/tabs e @android:id/tabcontent respectivamente.

Para finalizar, a classe Java ficaria conforme abaixo:


public class ExTabActivity2 extends Activity {

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

setContentView(R.layout.meutab);

TabHost tabHost = (TabHost)findViewById(
R.id.meuTabHost);

tabHost.setup();

// O resto é igual ao exemplo anterior
}
}


Agora a nossa classe herda de Activity e não mais de TabActivity. Dessa forma, temos que chamar o método setContentView passando o nosso layout e chamar o (MALDITO) método setup do objeto TabHost, que agora é obtido através do arquivo de layout.

Aê turma da LinuxFI, um "Aleluia" pro professor! :)

4br4ç05,
nglauber

5 comentários:

Anônimo disse...

Legal o post, parabéns! Eu queria saber como fazer para usar o TabHost em uma activity que já possui outros campos, fazer do tabHost uma View e não uma activity em si. Você poderia fazer um post relacionado à isto?

Grato.

Nelson Glauber de Vasconcelos Leal disse...

Oi Anônimo,

Isso já é feito no exemplo acima. Dá uma sacada. Eu tenho uma Activity normal e seto um arquivo de layout com uma TabHost.

4br4ç05,
nglauber

Ederson Faccin Frasson disse...
Este comentário foi removido pelo autor.
Psyduck disse...

Glauber, o TabActivity está deprecated, qual é a melhor opção para trabalhar com abas atualmente?

Nelson Glauber disse...

Olá PsyDuck,

Realmente! Mas esse meu post é de 2010 :)
O que usamos agora é o ViewPager + TabLayout. Dá uma olhada nesse vídeo: https://www.youtube.com/watch?v=zQekzaAgIlQ

4br4ç05,
nglauber