Expandir / Contraer la animación de la barra de herramientas de Lollipop (aplicación Telegram)

Estoy intentando descubrir cómo se hace la animación de expandir / colapsar de la barra de herramientas. Si echas un vistazo a la configuración de la aplicación Telegram, verás que hay una vista de lista y la barra de herramientas. Cuando se desplaza hacia abajo, la barra de herramientas se contrae y cuando se desplaza hacia arriba se expande. También está la animación de la foto de perfil y la FAB. ¿Alguien tiene alguna pista sobre eso? ¿Crees que construyeron todas las animaciones encima? Tal vez me falta algo de las nuevas API o la biblioteca de soporte.

Observé el mismo comportamiento en la aplicación de calendario de Google, cuando abres el Spinner (no creo que sea un spinner, pero parece): la barra de herramientas se expande y cuando te desplazas hacia arriba, colapsará.

Para aclarar: no necesito el método QuickReturn. Sé que probablemente la aplicación Telegram esté usando algo similar. El método exacto que necesito es el efecto de la aplicación Google Calendar. Lo he intentado con

android:animateLayoutChanges="true" 

y el método de expansión funciona bastante bien. Pero obviamente, si me desplazo hacia arriba en ListView, la barra de herramientas no se contrae.

También he pensado en agregar un GestureListener pero quiero saber si hay alguna API o métodos más simples para lograr esto.

Si no hay ninguno, creo que iré con GestureListener . Esperemos tener un efecto suave de la animación.

¡Gracias!

Editar:

Desde el lanzamiento de la biblioteca de soporte de Android Design, hay una solución más fácil. Compruebe la respuesta de joaquin

Así es como lo hice, probablemente haya muchas otras soluciones, pero esta funcionó para mí.

  1. En primer lugar, debe usar una Toolbar con un fondo transparente. La Toolbar expansión y contracción es en realidad una falsa que se encuentra debajo de la Toolbar transparente. (Puede ver en la primera captura de pantalla a continuación, la que tiene los márgenes, que así es como lo hicieron en Telegram).

    Solo conservamos la Toolbar de Toolbar actual para NavigationIcon y desbordamiento MenuItem .

    1. Barra de herramientas transparente - 2. Encabezado expandido - 3. Cabecera colapsado

  2. Todo lo que está en el rectángulo rojo en la segunda captura de pantalla (es decir, la Toolbar falsa y FloatingActionButton ) es en realidad un encabezado que se agrega a la configuración ListView (o ScrollView ).

    Por lo tanto, debe crear un diseño para este encabezado en un archivo separado que se vea así:

                 

    (Tenga en cuenta que puede usar márgenes negativos / relleno para que el fab se ubique en 2 Views )

  3. Ahora viene la parte interesante. Para animar la expansión de nuestra Toolbar falsa, implementamos ListView onScrollListener .

     // The height of your fully expanded header view (same than in the xml layout) int headerHeight = getResources().getDimensionPixelSize(R.dimen.header_height); // The height of your fully collapsed header view. Actually the Toolbar height (56dp) int minHeaderHeight = getResources().getDimensionPixelSize(R.dimen.action_bar_height); // The left margin of the Toolbar title (according to specs, 72dp) int toolbarTitleLeftMargin = getResources().getDimensionPixelSize(R.dimen.toolbar_left_margin); // Added after edit int minHeaderTranslation; private ListView listView; // Header views private View headerView; private RelativeLayout headerContainer; private TextView headerTitle; private TextView headerSubtitle; private FloatingActionButton headerFab; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.listview_fragment, container, false); listView = rootView.findViewById(R.id.listview); // Init the headerHeight and minHeaderTranslation values headerHeight = getResources().getDimensionPixelSize(R.dimen.header_height); minHeaderTranslation = -headerHeight + getResources().getDimensionPixelOffset(R.dimen.action_bar_height); // Inflate your header view headerView = inflater.inflate(R.layout.header_view, listview, false); // Retrieve the header views headerContainer = (RelativeLayout) headerView.findViewById(R.id.header_container); headerTitle = (TextView) headerView.findViewById(R.id.header_title); headerSubtitle = (TextView) headerView.findViewById(R.id.header_subtitle); headerFab = (TextView) headerView.findViewById(R.id.header_fab);; // Add the headerView to your listView listView.addHeaderView(headerView, null, false); // Set the onScrollListener listView.setOnScrollListener(this); // ... return rootView; } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { // Do nothing } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { Integer scrollY = getScrollY(view); // This will collapse the header when scrolling, until its height reaches // the toolbar height headerView.setTranslationY(Math.max(0, scrollY + minHeaderTranslation)); // Scroll ratio (0 <= ratio <= 1). // The ratio value is 0 when the header is completely expanded, // 1 when it is completely collapsed float offset = 1 - Math.max( (float) (-minHeaderTranslation - scrollY) / -minHeaderTranslation, 0f); // Now that we have this ratio, we only have to apply translations, scales, // alpha, etc. to the header views // For instance, this will move the toolbar title & subtitle on the X axis // from its original position when the ListView will be completely scrolled // down, to the Toolbar title position when it will be scrolled up. headerTitle.setTranslationX(toolbarTitleLeftMargin * offset); headerSubtitle.setTranslationX(toolbarTitleLeftMargin * offset); // Or we can make the FAB disappear when the ListView is scrolled headerFab.setAlpha(1 - offset); } // Method that allows us to get the scroll Y position of the ListView public int getScrollY(AbsListView view) { View c = view.getChildAt(0); if (c == null) return 0; int firstVisiblePosition = view.getFirstVisiblePosition(); int top = c.getTop(); int headerHeight = 0; if (firstVisiblePosition >= 1) headerHeight = this.headerHeight; return -top + firstVisiblePosition * c.getHeight() + headerHeight; } 

Tenga en cuenta que hay algunas partes de este código que no he probado, así que siéntase libre de resaltar los errores. Pero, en general, sé que esta solución funciona, aunque estoy seguro de que se puede mejorar.

EDICION 2:

Hubo algunos errores en el código anterior (que no probé hasta hoy …), así que cambié algunas líneas para que funcione:

  1. Introduje otra variable, minHeaderTranslation, que reemplazó a minHeaderHeight;
  2. Cambié el valor de traducción Y aplicado al encabezado Vista desde:

      headerView.setTranslationY(Math.max(-scrollY, minHeaderTranslation)); 

    a :

      headerView.setTranslationY(Math.max(0, scrollY + minHeaderTranslation)); 

    La expresión previa no funcionaba en absoluto, lo siento por eso …

  3. El cálculo de la relación también cambió, de modo que ahora evoluciona desde la parte inferior de la barra de herramientas (en lugar de la parte superior de la pantalla) hasta el encabezado expandido completo.

También echa un vistazo a CollapsingTitleLayout escrito por Chris Banes en el equipo de Android: https://plus.google.com/+ChrisBanes/posts/J9Fwbc15BHN

enter image description here

Código: https://gist.github.com/chrisbanes/91ac8a20acfbdc410a68

Utilice la biblioteca de soporte de diseño http://android-developers.blogspot.in/2015/05/android-design-support-library.html

incluir esto en build.gradle

 compile 'com.android.support:design:22.2.0' compile 'com.android.support:appcompat-v7:22.2.+' 

para la vista de reciclado incluir esto también

 compile 'com.android.support:recyclerview-v7:22.2.0' 

               

Su actividad debe extender AppCompatActivity

 public class YourActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.your_layout); //set toolbar Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); } } 

El tema de tu aplicación debería ser así

      

Esta es mi implementación:

collapsedHeaderHeight y expandedHeaderHeight están definidos en otro lugar, con la función getAnimationProgress puedo obtener el progreso de Expandir / Contraer, getAnimationProgress en este valor hago mi animación y muestro / escondo el encabezado real.

  listForumPosts.setOnScrollListener(new AbsListView.OnScrollListener() { /** * @return [0,1], 0 means header expanded, 1 means header collapsed */ private float getAnimationProgress(AbsListView view, int firstVisibleItem) { if (firstVisibleItem > 0) return 1; // should not exceed 1 return Math.min( -view.getChildAt(0).getTop() / (float) (expandedHeaderHeight - collapsedHeaderHeight), 1); } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { // at render beginning, the view could be empty! if (view.getChildCount() > 0) { float animationProgress = getAnimationProgress(view, firstVisibleItem); imgForumHeaderAvatar.setAlpha(1-animationProgress); if (animationProgress == 1) { layoutForumHeader.setVisibility(View.VISIBLE); } else { layoutForumHeader.setVisibility(View.GONE); } } } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { // do nothing } }