Fragment é um elemento especial dos layouts para Android que pode ser incluído dentro de uma Activity. Onde isso é usado? Dê uma olhada em vários aplicativos, como o YouTube ou o VLC: todos usam Fragments em alguma parte.

Os Fragments são bastante utilizados para criar uma navegação por abas (como nós também vamos fazer nesse exemplo), que é muito útil para exibir informações diferentes mas relacionadas ao mesmo assunto. O aplicativo do Google Play, por exemplo, utiliza um Fragment para exibir os aplicativos em destaque, outro para exibir os principais apps gratuitos, mais um Fragment para mostrar os apps pagos, outro para os novos, para as tendências, etc.

Abas com ActionBarSherlock

Mas vamos ao que interessa. Vamos alterar o exemplo do post anterior para incluir Fragments.

Vamos criar dois Fragments: FieldsFragment e ViewFragment, as duas estendendo de com.actionbarsherlock.app.SherlockFragment. Por enquanto vamos deixá-las vazias e criar o layout delas.

Um Fragment pode incluir um layout de qualquer formato com qualquer quantidade de widgets. O único limite é a imaginação (e talvez a falta de criatividade). Eu vou fazer um exemplo simples aqui apenas para demonstrar. Vou criar dois arquivos de layout na pasta res/layout (o tipo Android XML Layout File na janela do assistente). Os layouts que eu criei ficaram assim (clique para ampliar a imagem e para expandir os códigos XML):

Layouts dos Fragments

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical" >

        <EditText
            android:id="@+id/editName"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="8dip"
            android:layout_weight="1"
            android:ems="10"
            android:hint="Nome"
            android:inputType="textPersonName" >

            <requestFocus />
        </EditText>

        <EditText
            android:id="@+id/editEmail"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="8dp"
            android:layout_weight="1"
            android:ems="10"
            android:hint="E-mail"
            android:inputType="textEmailAddress" />

    </LinearLayout>

</ScrollView>
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <TextView
            android:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="8dp"
            android:text="[falta_de_criatividade mode ON]

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas vitae mollis mauris. Nulla quis magna ac diam fermentum sollicitudin eu ac leo. Nunc cursus tempus viverra. Nam ut nisl tellus.

Cras aliquet mi a mauris venenatis in gravida turpis egestas. Duis et blandit magna. Proin urna lorem, cursus eget laoreet non, ullamcorper ut dui. Duis ligula nisi, adipiscing non porta a, dignissim nec risus." />

    </FrameLayout>

</ScrollView>

Agora vamos carregar nosso layout nos Fragments. Para isso, sobrescrevemos o método onCreateView:

package seu.dominio.sherlocktest;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.actionbarsherlock.app.SherlockFragment;

public class FieldsFragment extends SherlockFragment {

    /** Método executado para inflar o layout do nosso Fragment */
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup group, Bundle saved) {
        // carrega o layout a partir do arquivo fields.xml
        return inflater.inflate(R.layout.fields, group, false);
    }
}

A mesma coisa para o ViewFragment, mas carregando o segundo layout (view). Depois de criar os Fragments, falta apenas incluí-los na Activity. Mãos à obra.

Abra o MainActivity.java. Para utilizar os Fragments precisamos estender de outra classe diferente de SherlockActivity, precisamos usar a SherlockFragmentActivity, então altere o import e a definição da classe MainActivity para referenciá-la.

Vamos alterar o método onCreate:

// imports necessários no início do arquivo:
import android.support.v4.app.FragmentTransaction;
import com.actionbarsherlock.app.ActionBar;
import com.actionbarsherlock.app.ActionBar.Tab;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    ActionBar bar = getSupportActionBar();
    bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

    // Tab listener para executar a troca de abas
    ActionBar.TabListener tabListener = new ActionBar.TabListener() {

        // evento de seleção de uma aba
        @Override
        public void onTabSelected(Tab tab, FragmentTransaction ft) {
            // primeira aba
            if (tab.getPosition() == 0) {
                ft.replace(android.R.id.content, new FieldsFragment());
            }
            // segunda aba
            else {
                ft.replace(android.R.id.content, new ViewFragment());
            }
        }

        @Override
        public void onTabUnselected(Tab tab, FragmentTransaction ft) {}
        @Override
        public void onTabReselected(Tab tab, FragmentTransaction ft) {}
    };

    bar.addTab(bar.newTab().setText("FieldsFragment").setTabListener(tabListener));
    bar.addTab(bar.newTab().setText("ViewFragment").setTabListener(tabListener));
}

Isso é o básico do básico do básico para rodar um exemplo básico que não faz nada além do básico (na verdade, não faz nada, nem mesmo o básico). Mas pelo menos está rodando no emulador:

Exemplo rodando no emulador

Tem muita coisa hardcoded nesse exemplo, como o TabListener. Essa é apenas uma ideia bem básica de como implementar essa funcionalidade do jeito mais simples, rápido e fácil possível (eu prometi o post ainda para 2012 e tive que correr pra conseguir terminar) – mas não é o melhor. Utilize o código desse post por sua própria conta e risco.

Para fazer algo realmente muito bem feito precisaríamos mais algumas 3 ou 4 classes (pagers, adapters, um PagerAdapter, …), e isso fica um pouco extenso demais para mostrar nesse post. Talvez no próximo, incluindo também o efeito de swipe. E futuramente ainda o SlidingMenu. ;-)

Bom, já estou cheio de tarefas para 2013, então que 2012 termine logo e até ano que vem!

Com as tags → 

34 Responses to Fragments com ActionBarSherlock

  1. willian disse:

    Amigo, tentei fazer o seu exemplo não consegui.
    Meu projeto possui 2 classes R.java e quando chamo o layout de dentro do SherlockFragment ele não acha meu layout. O que pode ser?

    • Eduardo disse:

      Olá Wilian,

      Duas classes R.java é estranho, só poderia existir uma… Já tentou limpar o projeto (Project > Clean), assim ele deve recriar essa classe.

      • willian disse:

        Eduardo,

        tinha a classe R do meu projeto e quando importei a ActionBarSherlock ele criou um novo pacote na pasta Gen.

        Esta a seguinte estrutura.

        Gen > meupacote
        BuilConfig.java
        R.java

        Gen > com.actionbarsherlock
        R.java

        Os meus layouts nao estão no R.java do pacote com.actionbarsherlock.
        Somente os meus layouts não estão lá.

        Tentei criar manualmente, mas ai da erro e ele elimina minha inserção.

        • Eduardo disse:

          Chegou a limpar o projeto? Como esses arquivos são gerados automaticamente a partir dos arquivos XML do layout, não é possível editá-los na mão. Limpar o projeto é uma forma de gerar novamente esses arquivos; se existir algum erro neles, isso deve corrigir.

          Mesmo assim, não vejo nenhum problema com a estrutura dos arquivos do seu projeto. Qual é o erro exatamente que você está enfrentando?

          • willian disse:

            O problema é que quando vou chamar o meu layout nos fragmentos ele não é mostrado.

            public View onCreateView (LayoutInflater inflater, ViewGroup group, Bundle saved ) {

            return inflater.inflate(R.layout.activity_principal , group, false);

            O R.layout não mostra minhas activitys.

          • Eduardo disse:

            Willian

            Como eu disse, se a classe R não está atualizada, limpar o projeto vai forçar que esse arquivo seja recriado. Se você tiver esse layout “activity_principal.xml” no mesmo projeto ele deve encontrar e adicionar na classe. Se mesmo assim o problema não for resolvido, provavelmente pode ser algum erro na configuração do projeto ou no código mesmo, e se for isso só vendo o código pra saber o que é.

      • willian disse:

        Olá boa tarde,

        consegui resolver o problema, agora tenho um outro problema.

        Eu já tinha um projeto onde eu tinha um buttton que chamada uma uma activity de informações.

        Eu clico nesse button ele nao funciona dentro do fragmento.

        Tem que declarar alguma outra coisa expecifica?

        Tem que informar que ele vai ser aberto em um fragmento?

        //Metodo para chamar a tela de informações
        public void chamaInfo(){
        Intent info = new Intent (Principal.this,Informacao.class);
        startActivity(info);
        }

        o startActivity tem que ter outro tipo de chamada?

        • Eduardo disse:

          Não, para iniciar uma nova Activity você só precisa criar um Intent (foi o que você fez) informando a classe da Activity que deve ser executada (foi o que você também fez) e o contexto (que eu não sei bem como você fez). Eu não entendi porque você usou “Principal.this” como contexto para o Intent. O mais comum é utilizar getActivity() dentro do Fragment para obter o contexto. Ficaria assim:

          startActivity(new Intent(getActivity(), Informacao.class));

          Desde que a classe Informacao estenda de Fragment, isso deve funcionar.

          • willian disse:

            tentei fazer como voce disse, porem ocorre o erro.

            The method getActivity() is undefined for the type Principal

            A classe que quero chamar é uma classe que extende de SherlockActivity.

          • willian disse:

            Me desculpe eu demorei a entender aqui.

            Na verdade eu estou chamando o layout principal atravez do

            public View onCreateView (LayoutInflater inflater, ViewGroup group, Bundle saved ) {

            return inflater.inflate(R.layout.activity_principal , group, false);

            Na verdade eu estou apenas chamando o layout sem código algum eu não estou chamando a classe principal onde os códigos estão.

            Agora que eu consegui entender o que se passa.

            Mas então como carregar o layout e chamar a classe principal que contem os códigos para os botoes?

            os códigos terão que ir para os fragmentos?

          • Eduardo disse:

            eu estou apenas chamando o layout sem código algum

            Um layout sozinho não tem como existir, ele precisa estar associado a alguma Activity. Todo Fragment também não passa de um item do layout, então ele também tem uma Activity associada. Sem ver como você está estruturando o código eu não tenho muito como ajudar.

          • willian disse:

            Eu tenho uma classe principal que chama meu layout principal.

            public class Principal extends SherlockActivity {
            String mensagem =”";
            private AdView adView;
            EditText editAltura,editPeso;
            @Override
            public void onCreate(Bundle savedInstanceState) {
            setTheme(R.style.Sherlock___Theme_Light);
            setTitle(“Calculadora Corporal”);

            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_principal);
            }

            Eu criei um container para colocar os metodos para chamar os layouts.
            public class Container extends SherlockFragmentActivity{
            @Override
            public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            ActionBar bar = getSupportActionBar();
            bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

            // Tab listener para executar a troca de abas
            ActionBar.TabListener tabListener = new ActionBar.TabListener() {

            // evento de seleção de uma aba
            @Override
            public void onTabSelected(Tab tab, FragmentTransaction ft) {
            // primeira aba
            if (tab.getPosition() == 0) {
            ft.replace(android.R.id.content, new FragmentoInicial() );
            }
            // segunda aba
            else {
            ft.replace(android.R.id.content, new FragmentoInformacao());
            }
            }

            @Override
            public void onTabUnselected(Tab tab, FragmentTransaction ft) {}
            @Override
            public void onTabReselected(Tab tab, FragmentTransaction ft) {}
            };

            bar.addTab(bar.newTab().setText(“Calculadora IMC”).setTabListener(tabListener));
            bar.addTab(bar.newTab().setText(“Informações”).setTabListener(tabListener));
            }
            }

            Depois criei um fragmento Principal para chamar o layout principal.

            public class FragmentoInicial extends SherlockFragment{

            @Override
            public View onCreateView (LayoutInflater inflater, ViewGroup group, Bundle saved ) {

            return inflater.inflate(R.layout.activity_principal , group, false);

            }

            Minha dúvida.

            Como chamar a classe principal que chama o layout principal para que os códigos na mesma sejam executados, pois como estou chamando apenas o layout a classe principal não está sendo criada.

          • Eduardo disse:

            Já vi onde está a confusão: você tem duas Activity’s diferentes e quer tentar fazer uma funcionar dentro de outra:

            Principal extends SherlockActivity
            Essa é uma Activity comum, que não pode incluir Fragment’s dentro.

            Container extends SherlockFragmentActivity
            Essa classe é uma Activity que pode conter Fragment’s dentro dela.

            FragmentoInicial extends SherlockFragment
            Esse é um Fragment que pode ser incluído dentro do “Container” (mas não do “Principal”). O layout desse Fragment é igual ao da Activity “Principal”, mas é apenas o layout igual. É uma cópia da interface e nada mais.

            Sugiro que as classes Principal e Container sejam mescladas em uma classe só que estende de SherlockFragmentActivity, é a melhor forma de resolver isso.

          • willian disse:

            Minha classe principal ficou assim.

            public class Principal extends SherlockFragmentActivity {
            String mensagem =”";
            private AdView adView;
            EditText editAltura,editPeso;
            @Override
            public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            ActionBar bar = getSupportActionBar();
            bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

            // Tab listener para executar a troca de abas
            ActionBar.TabListener tabListener = new ActionBar.TabListener() {

            // evento de seleção de uma aba
            @Override
            public void onTabSelected(Tab tab, FragmentTransaction ft) {
            // primeira aba
            if (tab.getPosition() == 0) {
            ft.replace(android.R.id.content, new FragmentoInicial() );
            }
            // segunda aba
            else {
            ft.replace(android.R.id.content, new FragmentoInformacao());
            }
            }

            @Override
            public void onTabUnselected(Tab tab, FragmentTransaction ft) {}
            @Override
            public void onTabReselected(Tab tab, FragmentTransaction ft) {}
            };

            bar.addTab(bar.newTab().setText(“Calculadora IMC”).setTabListener(tabListener));
            bar.addTab(bar.newTab().setText(“Informações”).setTabListener(tabListener));

            Button btn_limpar = (Button)findViewById(R.id.btn_Limpar);
            btn_limpar.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
            limpar();

            }
            });

            Button btn_Info = (Button)findViewById(R.id.btn_Info);
            btn_Info.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View arg0) {
            // TODO Auto-generated method stub
            chamaInfo();

            }
            });

            Button btnSair = (Button)findViewById(R.id.btn_Sair);
            btnSair.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
            Sair();

            }
            });

            Ele executa mais mostra somente uma tela em branco.

            O que estou fazendo errado?

          • Eduardo disse:

            Com esse código, o aplicativo deve iniciar exibindo a primeira aba inserida (no caso, seria “Calculadora IMC” e a classe FragmentoInicial). As abas chegam a ser exibidas sem conteúdo? Ou realmente nada é exibido no aplicativo (nem mesmo a ActionBar)? Eu não tenho o ambiente para testar agora, mas a princípio não vejo nenhum erro nessa classe.

          • willian disse:

            Nem mesmo a action bar é exibida, somente o titulo e o logo. O resto tudo em branco.

          • Eduardo disse:

            Agora eu tenho o ambiente para testar o código. Consegui rodar a sua classe sem problemas, exibiu a Action Bar e as abas como deveria. Eu acho que pode ser algum problema com a configuração do seu projeto ou do AndroidManifest.xml. Verifique duas coisas:

            - A classe que possui o Intent Filter com action MAIN e category LAUNCHER é a classe “Principal”
            - O aplicativo use um tema do Sherlock

            Esses dois tópicos estão descritos no post anterior, Introdução ao ActionBarSherlock.

          • willian disse:

            Bom dia Eduardo,

            desculpa a demora em responder.

            Eu fiz o que voce disse acima e realmente não da certo.

            O erro, ao que parece ocorre nessa linha.

            ActionBar.TabListener tabListener = new ActionBar.TabListener()

            Debuguei e quando passa nessa linha trava.

          • Eduardo disse:

            Como eu já havia dito, não achei nenhum problema com o seu código. Muito provavelmente é só alguma configuração do projeto que está errada. Se os dois pontos que eu comentei anteriormente estão OK (a classe Principal está configurada como launcher e o aplicativo usa um tema do Sherlock), então eu acho que o problema pode ser ou os imports errados ou alguma opção de compilação do Eclipse mal-configurada (eu suponho que você deve estar usando o Eclipse). Verifica todas as configurações, se você inclui a biblioteca do ActionBarSherlock e outras opções. Algumas vezes essa dica também pode ajudar http://stackoverflow.com/a/17205659

  2. Fernando disse:

    Boa Tarde Eduardo,
    Primeiramente parabenizo pelo conteúdo apresentado e disponibilizado!

    Estou com um problema na implementação desde projeto, se possível peço ajuda.

    Nesta etapa:
    if (tab.getPosition() == 0) {
    ft.replace(android.R.id.content, new Exemplo1());
    }

    Neste meu teste a classe Exemplo1 está estendendo SherlockActivity, ocasionando o erro no replace da condição acima. Este erro permanece mesmo com a mudança de SherlockActivity para SherlockFragmentActivity.

    O que pode estar ocasionando este erro?
    Possui alguma forma de contorna-lo?

    Desde já agradeço a atenção

    • Eduardo disse:

      Olá Fernando,

      Obrigado pelos elogios :)

      Bom, quanto ao seu problema, a classe “Exemplo1″ deve ser um Fragment e não uma Activity, ou seja, ela deve estender de SherlockFragment.

      Isso deve resolver. Qualquer problema pode deixar outro comentário ;-)

      • Fernando disse:

        Problema resolvido, nem havia percebido os “extends”.

        Eduardo, tenho mais uma duvida.
        na MainActivity.java onde existe o evento de seleção de cada aba, tem alguma maneira de passar assim que a aba é selecionada uma classe Activity que possui se respectivo layout.

        Caso exista, como seria esta passagem? por métodos?

        Pois estou tentando implementar este seu exemplo em um projeto meu, onde possui esta classe activity que está executando um “ListView” que retorna informações de diferentes sites.

        Grato.

        • Eduardo disse:

          Não, uma aba só pode conter um item do tipo “Fragment” dentro dela. A não ser é claro que você queira iniciar uma nova Activity, mas nesse caso a aba perderia completamente o funcionamento padrão (clicar na aba não troca para a aba mas abre uma nova janela?). Se você quer exibir uma lista dentro de uma aba você pode criar uma classe que estenda de SherlockListFragment.

  3. Fred disse:

    O que a ViewFragment faz?

    • Eduardo disse:

      A ViewFragment é apenas um exemplo que eu utilizei, nesse caso apenas para exibir um texto estático. Mas ela poderia fazer qualquer coisa.

  4. Fred disse:

    Eduardo, obrigado pela resposta. Como que faço para recuperar o que foi digitado na Primeira Tab estando na Tab 2?

  5. Fred disse:

    Então, como faço um listview com fragment? To com essa duvida. Mais a respeito de listview com sqlite, entendeu?

Plataforma Wordpress 3.8 (GPLv2)
Tema PageLines Lite Improved (GPLv3)
Hospedado por OpenShift by RedHat
Domínio registrado pela Neep Host
Creative Commons Attribution-ShareAlike License
Copyleft © 2014 Eduardo Weiland
Utilize um leitor de QR Code para acessar o blog no seu celular:
21
%d blogueiros gostam disto: