Expand / Collapse Animación de la barra de herramientas de Lollipop (Telegram app)

Estoy tratando de averiguar cómo se hace la animación de expand / collapse de la barra de herramientas. Si echa un vistazo a la configuración de la aplicación Telegram, verá que hay un listview 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 hay 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.

Me di cuenta del mismo comportamiento en la aplicación de calendario de Google, cuando se abre el Spinner (no creo que sea un spinner, pero parece): La barra de herramientas se expande y cuando se desplaza hacia arriba, colapsar.

Sólo para aclarar: No necesito el método QuickReturn. Sé que probablemente Telegram app está usando algo similar. El método exacto que necesito es el efecto de la aplicación Google Calendar. He intentado con

android:animateLayoutChanges="true" 

Y el método de expansión funciona bastante bien. Pero obviamente, si desplazo el ListView, la barra de herramientas no se derrumbará.

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

Si no hay ninguno, creo que iré con el GestureListener . Esperemos que tenga 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. Revise la respuesta de joaquín

Aquí es cómo lo hice, probablemente hay muchas otras soluciones, pero esta funcionó para mí.

  1. En primer lugar, tiene que utilizar una Toolbar con un fondo transparente. La Toolbar expansión y colapso es en realidad una falsa que está bajo la Toolbar transparente. (Se puede ver en la primera captura de pantalla – la que tiene los márgenes – que esto es también cómo lo hicieron en Telegram).

    Sólo mantenemos la Toolbar de Toolbar actual para el NavigationIcon y el MenuItem Desbordamiento.

    1. Barra de herramientas transparente - 2. Cabecera expandida - 3. Colapsar el encabezado

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

    Así que tienes que crear un diseño para este encabezado en un archivo separado que podría verse así:

      <!-- The headerView layout. Includes the fake Toolbar & the FloatingActionButton --> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content"> <RelativeLayout android:id="@+id/header_container" android:layout_width="match_parent" android:layout_height="@dimen/header_height" android:layout_marginBottom="3dp" android:background="@android:color/holo_blue_dark"> <RelativeLayout android:id="@+id/header_infos_container" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:padding="16dp"> <ImageView android:id="@+id/header_picture" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginRight="8dp" android:src="@android:drawable/ic_dialog_info" /> <TextView android:id="@+id/header_title" style="@style/TextAppearance.AppCompat.Title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_toRightOf="@+id/header_picture" android:text="Toolbar Title" android:textColor="@android:color/white" /> <TextView android:id="@+id/header_subtitle" style="@style/TextAppearance.AppCompat.Subhead" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/header_title" android:layout_toRightOf="@+id/header_picture" android:text="Toolbar Subtitle" android:textColor="@android:color/white" /> </RelativeLayout> </RelativeLayout> <FloatingActionButton android:id="@+id/header_fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|right" android:layout_margin="10dp" android:src="@drawable/ic_open_in_browser"/> </FrameLayout> 

    (Tenga en cuenta que puede utilizar márgenes negativos / relleno para el fab para ser straddling 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 probé, 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.

EDIT 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ó minHeaderHeight;
  2. Cambié el valor de traducción de Y aplicado a la cabecera Vista de:

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

    a :

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

    La expresión anterior 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.

Ahora todo es más sencillo con la nueva Biblioteca de soporte de diseño de Android .

Puede lograr exactamente este efecto siguiendo este tutorial del usuario de Suleiman , basado en el trabajo de Chris Banes para la aplicación de ejemplo de Cheesesquare .

EDITAR
Algunos usuarios preguntaron si pueden usar la misma idea pero con un icono. Saulmm Github usuario intentó algo así usando las mismas bibliotecas de soporte

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

Introduzca aquí la descripción de la imagen

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 del reciclador incluyen esto también

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

  <!-- AppBarLayout allows your Toolbar and other views (such as tabs provided by TabLayout) to react to scroll events in a sibling view marked with a ScrollingViewBehavior.--> <android.support.design.widget.AppBarLayout android:id="@+id/appbar" android:layout_width="match_parent" android:layout_height="wrap_content" android:fitsSystemWindows="true"> <!-- specify tag app:layout_scrollFlags --> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:layout_scrollFlags="scroll|enterAlways"/> <!-- specify tag app:layout_scrollFlags --> <android.support.design.widget.TabLayout android:id="@+id/tabLayout" android:scrollbars="horizontal" android:layout_below="@+id/toolbar" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?attr/colorPrimary" app:layout_scrollFlags="scroll|enterAlways"/> <!-- app:layout_collapseMode="pin" will help to pin this view at top when scroll --> <TextView android:layout_width="match_parent" android:layout_height="50dp" android:text="Title" android:gravity="center" app:layout_collapseMode="pin" /> </android.support.design.widget.AppBarLayout> <!-- This will be your scrolling view. app:layout_behavior="@string/appbar_scrolling_view_behavior" tag connects this features --> <android.support.v7.widget.RecyclerView android:id="@+id/list" app:layout_behavior="@string/appbar_scrolling_view_behavior" android:layout_width="match_parent" android:layout_height="match_parent"> </android.support.v7.widget.RecyclerView> </android.support.design.widget.CoordinatorLayout> 

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 debe ser así

  <resources> <!-- Base application theme. --> <style name="AppTheme" parent="Theme.AppCompat.NoActionBar"> </style> </resources> 

Esta es mi implementación:

CollapsedHeaderHeight y expandedHeaderHeight se definen en otro lugar, con la función getAnimationProgress puedo obtener el avance de Expand / Collapse, base en este valor hago mi animación y mostrar / ocultar 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 } } 
  • Gaveta de navegación: Gmail vs AppCompatv7 v21
  • Barra de herramientas de superposición transparente de Android
  • Cómo puedo colocar el menú de desbordamiento debajo de la barra de herramientas en lugar del menú de desbordamiento para superponer la barra de aplicaciones
  • Cambiar el color de la barra de estado con AppCompat ActionBarActivity
  • Android: Estilo ActionMode en AppCompat-v7 con la barra de herramientas
  • Android: ¿Cómo / Tutorial para cambiar ActionBar a ActionBarCompat (Barra de herramientas)?
  • ¿Por qué el título no está centrado en la barra de herramientas?
  • Adición de dos barras de herramientas AppCompat con diferentes temas
  • ¿Cómo hacer clic en las vistas detrás de una barra de herramientas?
  • Cuándo establecer una barra de herramientas como una barra de acción cuando se utiliza AppCompat v21?
  • Los botones de la barra de herramientas no responden al tacto cuando está abierto un cajón de navegación
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.