TL; DR: Estoy buscando una muestra de trabajo completa de lo que denominaré "el escenario de animación de tres fragmentos de Gmail". Específicamente, queremos comenzar con dos fragmentos, como este:
En algún evento de IU (por ejemplo, tocando algo en el Fragmento B), queremos:
- Fragmento A para deslizarse fuera de la pantalla hacia la izquierda
- Fragmento B para deslizarse hacia el borde izquierdo de la pantalla y reducir para ocupar el lugar desocupado por el Fragmento A
- Fragmento C para deslizarse desde el lado derecho de la pantalla y ocupar el lugar desocupado por el Fragmento B
Y, al presionar el botón ATRÁS, queremos que se revierta ese conjunto de operaciones.
Ahora, he visto muchas implementaciones parciales; Revisaré cuatro de ellos a continuación. Más allá de estar incompletos, todos tienen sus problemas.
@Reto Meier contribuyó con esta respuesta popular a la misma pregunta básica, indicando que usaría setCustomAnimations()
con a FragmentTransaction
. Para un escenario de dos fragmentos (por ejemplo, solo ve el Fragmento A inicialmente y desea reemplazarlo por un nuevo Fragmento B que usa efectos animados), estoy totalmente de acuerdo. Sin embargo:
- Dado que solo puede especificar una animación de "entrada" y una de "salida", no puedo ver cómo manejaría todas las animaciones diferentes requeridas para el escenario de tres fragmentos
- El
<objectAnimator>
código en su muestra utiliza posiciones cableadas en píxeles, y eso parecería poco práctico dados los diferentes tamaños de pantalla, perosetCustomAnimations()
requiere recursos de animación, lo que impide la posibilidad de definir estas cosas en Java - Estoy en una pérdida en cuanto a cómo los animadores de objeto para lazo escala en con cosas como
android:layout_weight
en unaLinearLayout
de asignación de espacio en una base porcentual - Estoy en una pérdida en cuanto a cómo Fragmento C se maneja desde el principio (
GONE
?android:layout_weight
De0
? Pre-animado a una escala de 0? Otra cosa?)
@Roman Nurik señala que puede animar cualquier propiedad , incluidas las que usted mismo defina. Eso puede ayudar a resolver el problema de las posiciones cableadas, a costa de inventar su propia subclase de administrador de diseño personalizado. Eso ayuda a algunos, pero todavía estoy desconcertado por el resto de la solución de Reto.
El autor de esta entrada de Pastebin muestra un pseudocódigo tentador, básicamente diciendo que los tres fragmentos residirían inicialmente en el contenedor, con el Fragmento C oculto al principio a través de una hide()
operación de transacción. Luego, show()
C y hide()
A cuando ocurre el evento UI. Sin embargo, no veo cómo eso maneja el hecho de que B cambia de tamaño. También se basa en el hecho de que aparentemente puede agregar múltiples fragmentos al mismo contenedor, y no estoy seguro de si ese es un comportamiento confiable a largo plazo (sin mencionar que debería romperse findFragmentById()
, aunque puedo vivir con eso).
El autor de esta publicación de blog indica que Gmail no está utilizando setCustomAnimations()
en absoluto, sino que utiliza directamente animadores de objetos ("simplemente cambia el margen izquierdo de la vista raíz + cambia el ancho de la vista derecha"). Sin embargo, esta sigue siendo una solución AFAICT de dos fragmentos, y la implementación muestra una vez más las dimensiones de los cables duros en píxeles.
Continuaré desconectando de esto, así que quizás termine respondiendo esto algún día, pero realmente espero que alguien haya resuelto la solución de tres fragmentos para este escenario de animación y pueda publicar el código (o un enlace al mismo). Las animaciones en Android me dan ganas de arrancarme el pelo, y aquellos de ustedes que me han visto saben que este es un esfuerzo en gran medida infructuoso.
fuente
Respuestas:
Subí mi propuesta en github (está funcionando con todas las versiones de Android, aunque se recomienda ver la aceleración de hardware para este tipo de animaciones. Para dispositivos sin aceleración de hardware, una implementación de almacenamiento en caché de mapa de bits debería ser mejor)
El video de demostración con la animación está aquí (Causa de velocidad de fotogramas lenta de la transmisión de pantalla. El rendimiento real es muy rápido)
Uso:
Explicacion :
Creó un nuevo RelativeLayout personalizado (ThreeLayout) y 2 animaciones personalizadas (MyScalAnimation, MyTranslateAnimation)
ThreeLayout
obtiene el peso del panel izquierdo como parámetro, suponiendo que la otra vista visible lo tengaweight=1
.Entonces
new ThreeLayout(context,3)
crea una nueva vista con 3 hijos y el panel izquierdo tiene 1/3 de la pantalla total. La otra vista ocupa todo el espacio disponible.Las animaciones de Escalar y Traducir en realidad cambian el tamaño y mueven la vista y no pseudo- [escalar, mover]. Tenga en cuenta que
fillAfter(true)
no se usa en ningún lado.View2 es right_of View1
y
View3 es right_of View2
Habiendo establecido estas reglas, RelativeLayout se encarga de todo lo demás. Las animaciones alteran
margins
(en movimiento) y[width,height]
en escalaPara acceder a cada niño (para que pueda inflarlo con su Fragmento, puede llamar
A continuación se muestran las 2 animaciones.
Nivel 1
Etapa 2
fuente
View
más pequeña), pero en Gmail / Email, no obtenemos fuentes más pequeñas, solo menos palabras.scaleX
valorView
, aunque puedo estar malinterpretando las cosas.OK, aquí está mi propia solución, derivada de la aplicación Email AOSP, según la sugerencia de @ Christopher en los comentarios de la pregunta.
https://github.com/commonsguy/cw-omnibus/tree/master/Animation/ThreePane
La solución de @ weakwire recuerda a la mía, aunque usa el clásico en
Animation
lugar de los animadores, y usaRelativeLayout
reglas para imponer el posicionamiento. Desde el punto de vista de la recompensa, probablemente obtendrá la recompensa, a menos que alguien más con una solución más ingeniosa aún publique una respuesta.En pocas palabras, el
ThreePaneLayout
proyecto en ese es unaLinearLayout
subclase, diseñada para trabajar en el paisaje con tres hijos. Los anchos de esos niños se pueden establecer en el diseño XML, a través de los medios deseados: muestro el uso de pesos, pero podría tener anchos específicos establecidos por recursos de dimensión o lo que sea. El tercer hijo (Fragmento C en la pregunta) debe tener un ancho de cero.Sin embargo, en tiempo de ejecución, no importa cómo configuró originalmente los anchos, la administración del ancho se hace cargo de
ThreePaneLayout
la primera vez que usahideLeft()
para pasar de mostrar lo que la pregunta denominada Fragmentos A y B a Fragmentos B y C. En la terminología deThreePaneLayout
- que no tiene lazos específicas a fragmentos - las tres piezas sonleft
,middle
, yright
. En el momento de llamarhideLeft()
, registramos los tamaños deleft
ymiddle
y cero fuera de cualquier peso que se utilizaron en cualquiera de los tres, así que podemos controlar por completo los tamaños. En el momento dehideLeft()
establecer el tamaño deright
ser el tamaño original demiddle
.Las animaciones son dobles:
ViewPropertyAnimator
para realizar una traducción de los tres widgets a la izquierda por el ancho deleft
, usando una capa de hardwareObjectAnimator
pseudo-propiedad personalizada demiddleWidth
para cambiar elmiddle
ancho de lo que sea que comience al ancho original deleft
(es posible que sea una mejor idea usar un
AnimatorSet
yObjectAnimators
para todos estos, aunque esto funciona por ahora)(también es posible que
middleWidth
ObjectAnimator
niegue el valor de la capa de hardware, ya que eso requiere una invalidación bastante continua)( Definitivamente es posible que todavía tenga lagunas en mi comprensión de animación y que me gusten las declaraciones entre paréntesis)
El efecto neto es que se
left
desliza fuera de la pantalla, semiddle
desliza a la posición y tamaño originalesleft
y seright
traduce justo detrásmiddle
.showLeft()
simplemente invierte el proceso, con la misma combinación de animadores, solo con las direcciones invertidas.La actividad utiliza a
ThreePaneLayout
para contener un par deListFragment
widgets y aButton
. Al seleccionar algo en el fragmento izquierdo, se agrega (o actualiza el contenido) el fragmento del medio. Al seleccionar algo en el fragmento del medio, se establece el subtítulo deButton
, además, se ejecutahideLeft()
en elThreePaneLayout
. Al presionar ATRÁS, si ocultamos el lado izquierdo, se ejecutaráshowLeft()
; de lo contrario, BACK sale de la actividad. Como esto no se usaFragmentTransactions
para afectar las animaciones, estamos atascados manejando esa "pila de fichas" nosotros mismos.El proyecto vinculado anteriormente utiliza fragmentos nativos y el marco de animación nativo. Tengo otra versión del mismo proyecto que utiliza el backport de fragmentos de soporte de Android y NineOldAndroids para la animación:
https://github.com/commonsguy/cw-omnibus/tree/master/Animation/ThreePaneBC
El backport funciona bien en un Kindle Fire de primera generación, aunque la animación es un poco irregular debido a las especificaciones de hardware más bajas y la falta de soporte de aceleración de hardware. Ambas implementaciones parecen fluidas en un Nexus 7 y otras tabletas de generación actual.
Ciertamente estoy abierto a ideas sobre cómo mejorar esta solución u otras soluciones que ofrecen claras ventajas sobre lo que hice aquí (o lo que usó @weakwire).
Gracias de nuevo a todos los que han contribuido!
fuente
ListView
y presioné el botón de retroceso rápidamente y repetí, todo el diseño se desvió hacia la derecha. Comenzó a mostrar espacio extra en la columna de la izquierda. Este comportamiento se introdujo porque no dejaría que se completara la primera animación (comenzada tocando en el medioListView
) y presioné el botón Atrás. Lo arreglé reemplazandotranslationXBy
contranslationX
intranslateWidgets
method ytranslateWidgets(leftWidth, left, middle, right);
contranslateWidgets(0, left, middle, right);
inshowLeft()
method.requestLayout();
conmiddle.requestLayout();
insetMiddleWidth(int value);
method. Es posible que no afecte el rendimiento, ya que supongo que la GPU seguirá haciendo un pase de diseño completo, ¿algún comentario?Creamos una biblioteca llamada PanesLibrary que resuelve este problema. Es aún más flexible de lo que se ofreció anteriormente porque:
Puede consultarlo aquí: https://github.com/Mapsaurus/Android-PanesLibrary
Aquí hay una demostración: http://www.youtube.com/watch?v=UA-lAGVXoLU&feature=youtu.be
Básicamente, le permite agregar fácilmente cualquier número de paneles de tamaño dinámico y adjuntar fragmentos a esos paneles. ¡Esperamos que te sea útil! :)
fuente
Partiendo de uno de los ejemplos a los que se vinculó ( http://android.amberfog.com/?p=758 ), ¿qué le parece animar la
layout_weight
propiedad? De esta manera, puede animar el cambio en el peso de los 3 fragmentos juntos, Y obtiene la ventaja de que todos se deslizan juntos:Comience con un diseño simple. Como vamos a animar
layout_weight
, necesitamos unaLinearLayout
vista de raíz para los 3 paneles:Luego la clase de demostración:
Además, este ejemplo no usa FragmentTransactions para lograr las animaciones (más bien, anima las vistas a las que están unidos los fragmentos), por lo que necesitaría hacer todas las transacciones de backstack / fragment usted mismo, pero en comparación con el esfuerzo de hacer que las animaciones funcionen bien, esto no parece una mala compensación :)
Horrible video de baja resolución en acción: http://youtu.be/Zm517j3bFCo
fuente
TextView
conwrap_content
el estiramiento en sí verticalmente a medida que avanza la animación). Sin embargo, puede ser que animando el peso es cómo cambian el tamaño de lo que denominé Fragmento B. ¡Gracias!Esto no está usando fragmentos ... Es un diseño personalizado con 3 hijos. Cuando hace clic en un mensaje, compensa los 3 niños que usan
offsetLeftAndRight()
y un animador.En
JellyBean
puede habilitar "Mostrar límites de diseño" en la configuración de "Opciones de Desarrollador". Cuando se completa la animación de la diapositiva, aún puede ver que el menú izquierdo todavía está allí, pero debajo del panel central.Es similar al menú de la aplicación Fly-in de Cyril Mottier , pero con 3 elementos en lugar de 2.
Además, la
ViewPager
de los terceros niños es otra indicación de este comportamiento:ViewPager
generalmente usa Fragmentos (sé que no tienen que hacerlo, pero nunca he visto una implementación que no sea esaFragment
), y como no puedes usarFragments
dentro de otro,Fragment
los 3 niños probablemente no son fragmentos ...fuente
TranslateAnimation
, aunque estoy Seguro que hay formas de usar animadores para lograr los mismos fines. Tendré que hacer algunos experimentos sobre esto. ¡Gracias!Actualmente estoy tratando de hacer algo así, excepto que la Fragmento B escala para tomar el espacio disponible y que el panel 3 se puede abrir al mismo tiempo si hay suficiente espacio. Aquí está mi solución hasta ahora, pero no estoy seguro de si voy a seguir con ella. Espero que alguien proporcione una respuesta que muestre La forma correcta.
En lugar de usar un LinearLayout y animar el peso, uso un RelativeLayout y animar los márgenes. No estoy seguro de que sea la mejor manera porque requiere una llamada a requestLayout () en cada actualización. Sin embargo, es suave en todos mis dispositivos.
Entonces, animo el diseño, no estoy usando fragmentos de transacción. Manejo el botón de retroceso manualmente para cerrar el fragmento C si está abierto.
FragmentB use layout_toLeftOf / ToRightOf para mantenerlo alineado con los fragmentos A y C.
Cuando mi aplicación activa un evento para mostrar el fragmento C, deslizo el fragmento C y deslizo el fragmento A al mismo tiempo. (2 animaciones separadas). Inversamente, cuando el Fragmento A se abre, cierro C al mismo tiempo.
En modo vertical o en una pantalla más pequeña, uso un diseño ligeramente diferente y deslizo el Fragmento C sobre la pantalla.
Para usar el porcentaje para el ancho del Fragmento A y C, creo que tendría que calcularlo en tiempo de ejecución ... (?)
Aquí está el diseño de la actividad:
La animación para deslizar FragmentC dentro o fuera:
fuente