TL; DR: Estou procurando uma amostra de trabalho completa do que chamarei de cenário de "animação de três fragmentos do Gmail". Especificamente, queremos começar com dois fragmentos, como este:
Em algum evento da interface do usuário (por exemplo, tocando em algo no fragmento B), queremos:
- Fragmento A para deslizar para fora da tela para a esquerda
- Fragmento B para deslizar para a borda esquerda da tela e encolher para ocupar o lugar vazio pelo fragmento A
- Fragmento C para deslizar do lado direito da tela e ocupar o lugar desocupado pelo Fragmento B
E, pressionando o botão VOLTAR, queremos que esse conjunto de operações seja revertido.
Agora, eu tenho visto muitas implementações parciais; Vou revisar quatro deles abaixo. Além de incompletos, todos têm seus problemas.
@Reto Meier contribuiu com essa resposta popular para a mesma pergunta básica, indicando que você usaria setCustomAnimations()
com a FragmentTransaction
. Para um cenário de dois fragmentos (por exemplo, você só vê o fragmento A inicialmente e deseja substituí-lo por um novo fragmento B usando efeitos animados), concordo plenamente. Contudo:
- Como você pode especificar apenas uma animação "de entrada" e outra "de saída", não vejo como você lida com todas as diferentes animações necessárias para o cenário de três fragmentos.
- O
<objectAnimator>
código de exemplo usa posições fixas em pixels, o que parece impraticável considerando os tamanhos de tela variados, massetCustomAnimations()
requer recursos de animação, impedindo a possibilidade de definir essas coisas em Java - Não sei como os animadores de objetos de escala se relacionam com coisas como
android:layout_weight
em umLinearLayout
para alocar espaço em porcentagem - Eu estou em uma perda a respeito de como Fragmento C é tratada no início (
GONE
?android:layout_weight
De0
? Pré-animado com uma escala de 0? Outra coisa?)
A @Roman Nurik ressalta que você pode animar qualquer propriedade , incluindo aquelas que você mesmo define. Isso pode ajudar a resolver o problema das posições com fio, ao custo de inventar sua própria subclasse do gerenciador de layout personalizado. Isso ajuda alguns, mas ainda estou confuso com o resto da solução de Reto.
O autor desta entrada pastebin mostra alguns pseudocódigos tentadores, basicamente dizendo que todos os três fragmentos residiriam no contêiner inicialmente, com o Fragmento C oculto no início por meio de uma hide()
operação de transação. Em seguida, show()
C e hide()
A quando o evento da interface do usuário ocorre. No entanto, não vejo como isso lida com o fato de B mudar de tamanho. Ele também se baseia no fato de que você aparentemente pode adicionar vários fragmentos ao mesmo contêiner, e não tenho certeza se esse é um comportamento confiável a longo prazo (sem mencionar que ele deve quebrar findFragmentById()
, embora eu possa conviver com isso).
O autor desta postagem no blog indica que o Gmail não está usando setCustomAnimations()
nada, mas usa diretamente animadores de objetos ("você acabou de alterar a margem esquerda da visualização raiz + a largura da visualização correta"). No entanto, essa ainda é uma solução AFAICT de dois fragmentos, e a implementação mostrada novamente mostra as dimensões dos fios em pixels.
Vou continuar me entendendo, para que eu possa responder isso algum dia, mas realmente espero que alguém tenha elaborado a solução de três fragmentos para esse cenário de animação e possa postar o código (ou um link para ele). As animações no Android me fazem querer arrancar meus cabelos, e aqueles que me viram sabem que esse é um empreendimento amplamente infrutífero.
fonte
Respostas:
Carreguei minha proposta no github (está funcionando com todas as versões do Android, embora a aceleração de hardware da visualização seja altamente recomendada para esse tipo de animação. Para dispositivos sem aceleração de hardware, uma implementação de cache de bitmap deve se encaixar melhor)
O vídeo de demonstração com a animação está aqui (causa da taxa de quadros lenta da transmissão da tela. O desempenho real é muito rápido)
Uso:
Explicação :
Criou um novo RelativeLayout personalizado (ThreeLayout) e duas animações personalizadas (MyScalAnimation, MyTranslateAnimation)
ThreeLayout
obtém o peso do painel esquerdo como param, assumindo que a outra exibição visível tenhaweight=1
.Assim,
new ThreeLayout(context,3)
cria uma nova visualização com 3 filhos e o painel esquerdo com 1/3 da tela total. A outra visualização ocupa todo o espaço disponível.As animações Scale e Translate, na verdade, redimensionam e movem a exibição, e não pseudo- [scale, move]. Observe que
fillAfter(true)
não é usado em nenhum lugar.View2 está certo_de View1
e
View3 está certo_de View2
Depois de definir essas regras, o RelativeLayout cuida de tudo o mais. Animações alteram
margins
(em movimento) e[width,height]
em escalaPara acessar cada filho (para que você possa inflá-lo com seu Fragmento, você pode chamar
Abaixo são demonstradas as 2 animações
Estágio 1
Stage2
fonte
View
menor), mas no Gmail / Email, não temos fontes menores, apenas menos palavras.scaleX
valorView
, embora eu possa estar interpretando mal as coisas.OK, aqui está minha própria solução, derivada do aplicativo Email AOSP, de acordo com a sugestão de @ Christopher nos comentários da pergunta.
https://github.com/commonsguy/cw-omnibus/tree/master/Animation/ThreePane
A solução do @ fracawire é uma reminiscência da minha, embora ele use clássicos em
Animation
vez de animadores, e ele useRelativeLayout
regras para reforçar o posicionamento. Do ponto de vista da recompensa, ele provavelmente receberá a recompensa, a menos que alguém com uma solução mais simples ainda publique uma resposta.Em poucas palavras, o
ThreePaneLayout
projeto é umaLinearLayout
subclasse, projetada para trabalhar na paisagem com três filhos. As larguras dessas crianças podem ser definidas no XML do layout, por qualquer meio desejado - mostro usando pesos, mas você pode ter larguras específicas definidas por recursos de dimensão ou qualquer outra coisa. O terceiro filho - Fragmento C na pergunta - deve ter largura zero.No entanto, no tempo de execução, não importa como você configurou originalmente as larguras, o gerenciamento de largura é assumido pela
ThreePaneLayout
primeira vez que você usahideLeft()
para mudar de mostrar o que a pergunta referida como Fragmentos A e B para Fragmentos B e C. Na terminologia deThreePaneLayout
- o qual não tem ligações específicas para fragmentos - as três peças sãoleft
,middle
eright
. No momento em que você ligahideLeft()
, registramos os tamanhos deleft
emiddle
zeramos os pesos usados em qualquer um dos três, para que possamos controlar completamente os tamanhos. No momentohideLeft()
, definimos o tamanho deright
como o tamanho original demiddle
.As animações são duplas:
ViewPropertyAnimator
para realizar uma conversão dos três widgets para a esquerda pela largura deleft
, usando uma camada de hardwareObjectAnimator
pseudo-propriedade personalizada demiddleWidth
para alterar amiddle
largura do que quer que tenha começado para a largura original deleft
(é possível que seja uma idéia melhor usar um
AnimatorSet
eObjectAnimators
para todos eles, embora isso funcione por enquanto)(também é possível que
middleWidth
ObjectAnimator
negue o valor da camada de hardware, pois isso exige uma invalidação relativamente contínua)(é definitivamente possível que eu ainda tenha falhas na compreensão da animação e que goste de declarações entre parênteses)
O efeito líquido é que
left
desliza para fora da tela,middle
desliza para a posição original e tamanho deleft
, e seright
traduz logo atrásmiddle
.showLeft()
simplesmente inverte o processo, com a mesma mistura de animadores, apenas com as direções invertidas.A atividade usa a
ThreePaneLayout
para armazenar um par deListFragment
widgets e aButton
. Selecionar algo no fragmento esquerdo adiciona (ou atualiza o conteúdo) ao fragmento do meio. Selecionar algo no fragmento do meio define a legenda doButton
, além de ser executadohideLeft()
noThreePaneLayout
. Pressionar BACK, se escondermos o lado esquerdo, será executadoshowLeft()
; caso contrário, BACK sai da atividade. Como isso não é usadoFragmentTransactions
para afetar as animações, estamos presos ao gerenciamento dessa "pilha de trás".O projeto vinculado acima usa fragmentos nativos e a estrutura do animador nativo. Eu tenho outra versão do mesmo projeto que usa o backport de fragmentos do Suporte Android e o NineOldAndroids para a animação:
https://github.com/commonsguy/cw-omnibus/tree/master/Animation/ThreePaneBC
O backport funciona bem em um Kindle Fire de 1ª geração, embora a animação seja um pouco instável, dadas as especificações de hardware mais baixas e a falta de suporte à aceleração de hardware. Ambas as implementações parecem suaves em um Nexus 7 e em outros tablets da geração atual.
Estou certamente aberto a idéias de como melhorar essa solução ou outras soluções que oferecem vantagens claras sobre o que eu fiz aqui (ou o que o @weakwire usou).
Mais uma vez obrigado a todos que contribuíram!
fonte
ListView
e pressionei rapidamente o botão voltar e repeti-lo, o Layout inteiro foi para a direita. Começou a mostrar a coluna de espaço extra à esquerda. Esse comportamento foi introduzido porque eu não deixei a primeira animação (iniciada por toque no meioListView
) ser concluída e pressionei o botão Voltar. Corrigi-o substituindotranslationXBy
portranslationX
notranslateWidgets
método etranslateWidgets(leftWidth, left, middle, right);
portranslateWidgets(0, left, middle, right);
noshowLeft()
método.requestLayout();
commiddle.requestLayout();
nosetMiddleWidth(int value);
método Pode não afetar o desempenho, pois acho que a GPU ainda fará um passe de layout completo, algum comentário?Criamos uma biblioteca chamada PanesLibrary que resolve esse problema. É ainda mais flexível do que o que foi oferecido anteriormente porque:
Você pode conferir aqui: https://github.com/Mapsaurus/Android-PanesLibrary
Aqui está uma demonstração: http://www.youtube.com/watch?v=UA-lAGVXoLU&feature=youtu.be
Basicamente, você pode adicionar facilmente qualquer número de painéis de tamanho dinâmico e anexar fragmentos a esses painéis. Espero que você ache útil! :)
fonte
Criando um dos exemplos aos quais você vinculou ( http://android.amberfog.com/?p=758 ), que tal animar a
layout_weight
propriedade? Dessa forma, você pode animar a mudança de peso dos 3 fragmentos juntos e obter o bônus de que todos eles se encaixam perfeitamente:Comece com um layout simples. Como vamos animar
layout_weight
, precisamos de umaLinearLayout
visualização como raiz para os 3 painéis .:Então a classe demo:
Além disso, este exemplo não usa FragmentTransactions para obter as animações (em vez disso, anima as visualizações às quais os fragmentos estão anexados); portanto, você precisa fazer todas as transações de backstack / fragmento, mas comparado ao esforço de fazer as animações funcionarem. bem, isso não parece uma troca ruim :)
Horrível vídeo de baixa resolução em ação: http://youtu.be/Zm517j3bFCo
fonte
TextView
comwrap_content
alongamento-se verticalmente como a animação prossegue). No entanto, pode ser que animar o peso seja como eles redimensionam o que eu chamei de Fragmento B. Obrigado!Isso não está usando fragmentos ... É um layout personalizado com 3 filhos. Ao clicar em uma mensagem, você compensa as 3 crianças usando
offsetLeftAndRight()
e um animador.Em
JellyBean
você pode ativar "Mostrar disposição limites" nas configurações de "Opções developper". Quando a animação do slide estiver concluída, você ainda poderá ver que o menu esquerdo ainda está lá, mas abaixo do painel do meio.É semelhante ao menu do aplicativo Fly-in de Cyril Mottier , mas com 3 elementos em vez de 2.
Além disso, o
ViewPager
terceiro filho é outra indicação desse comportamento:ViewPager
geralmente usa Fragmentos (eu sei que eles não precisam, mas nunca vi uma implementação diferente dissoFragment
), e como você não pode usarFragments
dentro de outro,Fragment
os três filhos provavelmente não são fragmentos ....fonte
TranslateAnimation
, embora eu esteja Certifique-se de que existem maneiras de usar animadores para atingir os mesmos fins. Vou ter que fazer algumas experiências sobre isso. Obrigado!Atualmente, estou tentando fazer algo assim, exceto a escala do fragmento B para ocupar o espaço disponível e o painel 3 pode ser aberto ao mesmo tempo, se houver espaço suficiente. Aqui está a minha solução até agora, mas não tenho certeza se vou continuar com ela. Espero que alguém dê uma resposta mostrando o caminho certo.
Em vez de usar um LinearLayout e animar o peso, eu uso um RelativeLayout e animar as margens. Não tenho certeza de que é a melhor maneira, pois exige uma chamada para requestLayout () a cada atualização. É bom em todos os meus dispositivos.
Então, eu animar o layout, não estou usando a transação de fragmentos. Manipulo o botão voltar manualmente para fechar o fragmento C, se estiver aberto.
FragmentB use layout_toLeftOf / ToRightOf para mantê-lo alinhado ao fragmento A e C.
Quando meu aplicativo aciona um evento para exibir o fragmento C, deslizo o fragmento C e deslizo o fragmento A ao mesmo tempo. (2 animação separada). Inversamente, quando o fragmento A é aberto, fecho C ao mesmo tempo.
No modo retrato ou em tela menor, uso um layout ligeiramente diferente e deslizo o Fragmento C sobre a tela.
Para usar a porcentagem para a largura do fragmento A e C, acho que você precisaria calculá-lo em tempo de execução ... (?)
Aqui está o layout da atividade:
A animação para deslizar dentro ou fora do FragmentC:
fonte