Estou tentando implementar guias para navegação em um aplicativo Android. Como TabActivity e ActivityGroup estão obsoletos, gostaria de implementá-lo usando Fragments.
Eu sei como configurar um fragmento para cada guia e depois alternar fragmentos quando uma guia é clicada. Mas como posso ter uma pilha traseira separada para cada guia?
Por exemplo, o fragmento A e B estaria na guia 1 e o fragmento C e D na guia 2. Quando o aplicativo é iniciado, o fragmento A é mostrado e a guia 1 é selecionada. O fragmento A pode ser substituído pelo fragmento B. Quando a guia 2 é selecionada, o fragmento C deve ser exibido. Se a guia 1 for selecionada, o fragmento B deve ser exibido novamente. Nesse ponto, deve ser possível usar o botão Voltar para mostrar o fragmento A.
Além disso, é importante que o estado de cada guia seja mantido quando o dispositivo for girado.
BR Martin
fonte
Estou muito atrasado para esta pergunta. Mas como esse tópico foi muito informativo e útil para mim, é melhor postar meus dois centavos aqui.
Eu precisava de um fluxo de tela como este (um design minimalista com 2 guias e 2 visualizações em cada guia),
Eu tinha os mesmos requisitos no passado e o fiz usando
TabActivityGroup
(que também estava obsoleto na época) e Atividades. Desta vez, eu queria usar fragmentos.Então foi assim que eu fiz.
1. Crie uma classe de fragmento base
Todos os fragmentos no seu aplicativo podem estender essa classe Base. Se você deseja usar fragmentos especiais, como
ListFragment
você deve criar uma classe base para isso também. Você será claro sobre o usoonBackPressed()
e,onActivityResult()
se ler a publicação na íntegra.2. Crie alguns identificadores de guia, acessíveis em qualquer lugar do projeto
nada a explicar aqui ..
3. Ok, Atividade da guia principal - consulte os comentários no código.
4. app_main_tab_fragment_layout.xml (no caso de alguém interessado).
5. AppTabAFirstFragment.java (Primeiro fragmento na guia A, semelhante a todas as guias)
Esta pode não ser a maneira mais polida e correta. Mas funcionou lindamente no meu caso. Além disso, eu só tinha esse requisito no modo retrato. Eu nunca tive que usar esse código em um projeto que suporta ambas as orientações. Então não posso dizer que tipo de desafios eu enfrento lá ..
EDIT:
Se alguém quiser um projeto completo, enviei um projeto de amostra para o github .
fonte
Tivemos que implementar exatamente o mesmo comportamento que você descreve para um aplicativo recentemente. As telas e o fluxo geral do aplicativo já estavam definidos, então tivemos que ficar com ele (é um clone do aplicativo iOS ...). Felizmente, conseguimos nos livrar dos botões de retorno na tela :)
Nós invadimos a solução usando uma mistura de TabActivity, FragmentActivities (estávamos usando a biblioteca de suporte para fragmentos) e Fragmentos. Em retrospectiva, tenho certeza de que não foi a melhor decisão de arquitetura, mas conseguimos fazer a coisa funcionar. Se eu tivesse que fazer isso de novo, provavelmente tentaria fazer uma solução mais baseada em atividades (sem fragmentos), ou tentaria ter apenas uma atividade para as guias e permitir que todo o resto fosse visualizações (que considero muito mais reutilizáveis do que as atividades em geral).
Portanto, os requisitos eram ter algumas guias e telas aninhadas em cada guia:
etc ...
Digamos: o usuário começa na guia 1, navega da tela 1 para a tela 2 e depois para a tela 3, depois alterna para a guia 3 e navega da tela 4 para 6; se retornou à guia 1, ele deve ver a tela 3 novamente e, se pressionou Voltar, deve retornar à tela 2; De volta e ele está na tela 1; mude para a guia 3 e ele estará na tela 6 novamente.
A principal atividade no aplicativo é MainTabActivity, que estende o TabActivity. Cada guia está associada a uma atividade, digamos ActivityInTab1, 2 e 3. E então cada tela será um fragmento:
Cada ActivityInTab contém apenas um fragmento de cada vez e sabe como substituir um fragmento por outro (praticamente o mesmo que um ActvityGroup). O legal é que é muito fácil manter pilhas de costas separadas para cada guia dessa maneira.
A funcionalidade de cada ActivityInTab era a mesma: saber como navegar de um fragmento para outro e manter uma pilha inversa; portanto, colocamos isso em uma classe base. Vamos chamá-lo simplesmente de ActivityInTab:
O activity_in_tab.xml é exatamente isso:
Como você pode ver, o layout da visualização para cada guia era o mesmo. Isso ocorre porque é apenas um FrameLayout chamado conteúdo que contém cada fragmento. Os fragmentos são os que têm a visualização de cada tela.
Apenas para os pontos de bônus, também adicionamos um pouco de código para mostrar uma caixa de diálogo de confirmação quando o usuário pressiona Voltar e não há mais fragmentos para retornar:
Essa é basicamente a configuração. Como você pode ver, cada FragmentActivity (ou simplesmente Activity in Android> 3) está cuidando de todo o empilhamento com seu próprio FragmentManager.
Uma atividade como ActivityInTab1 será realmente simples, apenas mostrará seu primeiro fragmento (por exemplo, tela):
Então, se um fragmento precisar navegar para outro fragmento, ele precisará fazer uma seleção um pouco desagradável ... mas não é tão ruim assim:
Então é isso mesmo. Tenho certeza de que esta não é uma solução muito canônica (e principalmente não muito boa), então gostaria de perguntar aos desenvolvedores experientes do Android qual seria a melhor abordagem para obter essa funcionalidade e, se não for "como é" done "no Android, eu apreciaria se você pudesse me indicar algum link ou material que explique qual é a maneira do Android de abordar isso (guias, telas aninhadas em guias etc.). Sinta-se livre para separar esta resposta nos comentários :)
Como sinal de que esta solução não é muito boa, recentemente tive que adicionar algumas funcionalidades de navegação ao aplicativo. Algum botão bizarro que deve levar o usuário de uma guia para outra e para uma tela aninhada. Fazer isso de forma programática foi uma chatice, por causa de problemas de quem sabe quem e de quando lidar com fragmentos e atividades realmente instanciados e inicializados. Acho que teria sido muito mais fácil se essas telas e guias fossem apenas apenas visualizações.
Por fim, se você precisar sobreviver às alterações de orientação, é importante que seus fragmentos sejam criados usando setArguments / getArguments. Se você definir variáveis de instância nos construtores de seus fragmentos, ficará ferrado. Mas, felizmente, é realmente fácil de corrigir: salve tudo em setArguments no construtor e, em seguida, recupere essas coisas com getArguments no onCreate para usá-las.
fonte
Isso pode ser facilmente alcançado com o ChildFragmentManager
Aqui está um post sobre isso com o projeto associado. dê uma olhada,
http://tausiq.wordpress.com/2014/06/06/android-multiple-fragments-stack-in-each-viewpager-tab/
fonte
Armazenar referências fortes a fragmentos não é a maneira correta.
O FragmentManager fornece
putFragment(Bundle, String, Fragment)
esaveFragmentInstanceState(Fragment)
.Qualquer um deles é suficiente para implementar um backstack.
Usando
putFragment
, em vez de substituir um Fragmento, você desanexa o antigo e adiciona o novo. É isso que a estrutura faz com uma transação de substituição que é adicionada ao backstack.putFragment
armazena um índice na lista atual de fragmentos ativos e esses fragmentos são salvos pela estrutura durante as alterações de orientação.A segunda maneira, usando
saveFragmentInstanceState
, salva todo o estado do fragmento em um Bundle, permitindo que você realmente o remova, em vez de desanexá-lo. O uso dessa abordagem facilita a manipulação da pilha de trás, pois você pode abrir um fragmento quando quiser.Eu usei o segundo método para este caso de uso:
Não quero que o usuário retorne à tela de inscrição, a partir da terceira, pressionando o botão Voltar. Também faço animações invertidas entre eles (usando
onCreateAnimation
), para que soluções hacky não funcionem, pelo menos sem que o usuário perceba claramente que algo não está certo.Este é um caso de uso válido para um backstack personalizado, fazendo o que o usuário espera ...
fonte
Aviso Legal:
Eu sinto que este é o melhor lugar para postar uma solução relacionada em que trabalhei para um tipo de problema semelhante que parece ser um material padrão do Android. Não vai resolver o problema para todos, mas pode ajudar alguns.
Se a principal diferença entre seus fragmentos for apenas os dados que os estão fazendo backup (ou seja, não há muitas diferenças de layout), talvez você não precise substituir o fragmento, mas apenas trocar os dados subjacentes e atualizar a exibição.
Aqui está uma descrição de um exemplo possível para essa abordagem:
Eu tenho um aplicativo que usa ListViews. Cada item da lista é um pai com algum número de filhos. Quando você toca no item, uma nova lista precisa ser aberta com esses filhos, na mesma guia ActionBar da lista original. Essas listas aninhadas têm um layout muito semelhante (talvez alguns ajustes condicionais aqui e ali, talvez), mas os dados são diferentes.
Este aplicativo tem várias camadas de filhos abaixo da lista de pais inicial e podemos ou não ter dados do servidor quando um usuário tenta acessar uma certa profundidade além da primeira. Como a lista é construída a partir de um cursor do banco de dados e os fragmentos usam um carregador e um adaptador para preencher a exibição da lista com itens da lista, tudo o que precisa acontecer quando um clique é registrado é:
1) Crie um novo adaptador com os campos 'para' e 'de' apropriados que corresponderão às novas visualizações de itens adicionadas à lista e às colunas retornadas pelo novo cursor.
2) Defina este adaptador como o novo adaptador para o ListView.
3) Crie um novo URI com base no item que foi clicado e reinicie o carregador de cursor com o novo URI (e projeção). Neste exemplo, o URI é mapeado para consultas específicas com os argumentos de seleção transmitidos da interface do usuário.
4) Quando os novos dados forem carregados a partir do URI, troque o cursor associado ao adaptador pelo novo cursor e a lista será atualizada.
Não há backstack associado a isso, uma vez que não estamos usando transações, portanto, você precisará criar suas próprias ou reproduzir as consultas ao contrário ao sair da hierarquia. Quando tentei isso, as consultas foram rápidas o suficiente para que eu as execute novamente em oNBackPressed () até chegar ao topo da hierarquia, quando a estrutura assume o botão Voltar novamente.
Se você se encontrar em uma situação semelhante, leia os documentos: http://developer.android.com/guide/topics/ui/layout/listview.html
http://developer.android.com/reference/android/support/v4/app/LoaderManager.LoaderCallbacks.html
Espero que isso ajude alguém!
fonte
Eu tive exatamente o mesmo problema e implementei um projeto de código-fonte aberto do github que abrange guias empilhadas, navegação de backup e backup e é bem testado e documentado:
https://github.com/SebastianBaltesObjectCode/PersistentFragmentTabs
fonte
Esse é um problema complexo, pois o Android lida apenas com 1 pilha traseira, mas isso é viável. Levei dias para criar uma biblioteca chamada Tab Stacker que faz exatamente o que você está procurando: um histórico de fragmentos para cada guia. É de código aberto e totalmente documentado e pode ser incluído facilmente no gradle. Você pode encontrar a biblioteca no github: https://github.com/smart-fun/TabStacker
Você também pode baixar o aplicativo de exemplo para ver se o comportamento corresponde às suas necessidades:
https://play.google.com/apps/testing/fr.arnaudguyon.tabstackerapp
Se você tiver alguma dúvida, não hesite em enviar um e-mail.
fonte
Eu gostaria de sugerir minha própria solução, caso alguém esteja procurando e queira tentar escolher a melhor para suas necessidades.
https://github.com/drusak/tabactivity
O objetivo de criar a biblioteca é bastante banal - implemente-o como o iPhone.
As principais vantagens:
fonte
ListFragment
s além deFragment
s, então dupliquei BaseTabFragment.java para BaseTabListFragment.java e estendi ListFragment. Então eu tive que mudar várias partes do código onde sempre supunha esperar um BaseTabFragment. Existe uma maneira melhor?Uma solução simples:
Sempre que você altera a chamada de guia / visualização raiz:
Isso limpará o BackStack. Lembre-se de chamar isso antes de alterar o fragmento raiz.
E adicione fragmentos com isso:
Observe que o
.addToBackStack(null)
etransaction.add
poderia, por exemplo, ser alterado comtransaction.replace
.fonte
Este tópico foi muito, muito interessante e útil.
Obrigado Krishnabhadra pela sua explicação e código, eu uso o seu código e melhorei um pouco, permitindo persistir as pilhas, o currentTab, etc ... da configuração da alteração (principalmente na rotação).
Testado em dispositivos 4.0.4 e 2.3.6 reais, não testado no emulador
Eu mudo essa parte do código em "AppMainTabActivity.java", o restante permanece o mesmo. Talvez Krishnabhadra adicione isso em seu código.
Recuperar dados emCriar:
Salve as variáveis e coloque em Bundle:
Se existir um CurrentTab anterior, configure-o, caso contrário, crie um novo Tab_A:
Espero que isso ajude outras pessoas.
fonte
Eu recomendaria não usar o backstack com base no HashMap> existem muitos erros no modo "não manter atividades". Não restaurará corretamente o estado caso você esteja profundamente na pilha de fragmentos. E também será agrupado no fragmento de mapa aninhado (com exceção: Fragmento nenhuma visualização encontrada para o ID). Coz HashMap> após o aplicativo background \ foreground será nulo
Otimizo o código acima para trabalhar com o backstack do fragmento
É TabView inferior
Atividade principal Classe
tags_activity.xml
<
tags_icon.xml
fonte