¿Cómo imitar el comportamiento de las fases 3 de la parte inferior de Google Maps?

Fondo

Me asignan a hacer una interfaz de usuario que se comporta de forma similar a cómo Google Maps muestra una hoja inferior para un resultado encontrado.

Tiene tres fases diferentes:

  1. Contenido inferior. El área superior sigue siendo táctil y no desplazará nada en la parte inferior
  2. Contenido de pantalla completa, mientras que el área superior tiene un encabezado grande.
  3. Contenido de pantalla completa, mientras que el área superior tiene sólo la barra de herramientas.

Esto es lo que estoy hablando en Google Maps:

Introduzca aquí la descripción de la imagen

El problema

La cosa es, la hoja inferior no es una parte de la biblioteca de diseño todavía (aunque se pidió, aquí ).

No sólo eso, pero la interfaz de usuario parece bastante compleja y necesita el manejo de la barra de herramientas en múltiples fases.

Lo que he intentado

He encontrado una buena (suficiente) biblioteca para la hoja inferior ( aquí ), y agregó contenido a su fragmento de muestra, para tener sobre las mismas vistas que se muestra en muestras de diseño de materiales (como aquí ), para tener un CollapsingToolbarLayout que se hará cargo De las fases 2 + 3.

En la aplicación que estoy haciendo, también tengo que mover un icono a medida que se desplaza, pero creo que si tengo éxito con el resto, esto debería ser fácil. Aquí está el código:

Fragment_my.xml

<?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout android:id="@+id/main_content" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.design.widget.AppBarLayout android:id="@+id/appbar" android:layout_width="match_parent" android:layout_height="@dimen/detail_backdrop_height" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"> <android.support.design.widget.CollapsingToolbarLayout android:id="@+id/collapsing_toolbar" android:layout_width="match_parent" android:layout_height="match_parent" app:contentScrim="?attr/colorPrimary" app:expandedTitleMarginEnd="64dp" app:expandedTitleMarginStart="48dp" app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"> <ImageView android:id="@+id/backdrop" android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="centerCrop" app:layout_collapseMode="parallax"/> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" app:layout_collapseMode="pin" app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/> </android.support.design.widget.CollapsingToolbarLayout> </android.support.design.widget.AppBarLayout> <android.support.v4.widget.NestedScrollView android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:paddingTop="24dp"> <android.support.v7.widget.CardView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="@dimen/card_margin"> <LinearLayout style="@style/Widget.CardContent" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Info" android:textAppearance="@style/TextAppearance.AppCompat.Title"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/cheese_ipsum"/> </LinearLayout> </android.support.v7.widget.CardView> <android.support.v7.widget.CardView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="@dimen/card_margin" android:layout_marginLeft="@dimen/card_margin" android:layout_marginRight="@dimen/card_margin"> <LinearLayout style="@style/Widget.CardContent" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Friends" android:textAppearance="@style/TextAppearance.AppCompat.Title"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/cheese_ipsum"/> </LinearLayout> </android.support.v7.widget.CardView> <android.support.v7.widget.CardView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="@dimen/card_margin" android:layout_marginLeft="@dimen/card_margin" android:layout_marginRight="@dimen/card_margin"> <LinearLayout style="@style/Widget.CardContent" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Related" android:textAppearance="@style/TextAppearance.AppCompat.Title"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/cheese_ipsum"/> </LinearLayout> </android.support.v7.widget.CardView> </LinearLayout> </android.support.v4.widget.NestedScrollView> <android.support.design.widget.FloatingActionButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="@dimen/fab_margin" android:clickable="true" android:src="@android:drawable/ic_menu_send" app:layout_anchor="@id/appbar" app:layout_anchorGravity="bottom|right|end"/> </android.support.design.widget.CoordinatorLayout> 

MyFragment.java

 public class MyFragment extends BottomSheetFragment { @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { final View view = inflater.inflate(R.layout.fragment_my, container, false); view.setMinimumHeight(getResources().getDisplayMetrics().heightPixels); CollapsingToolbarLayout collapsingToolbar = (CollapsingToolbarLayout) view.findViewById(R.id.collapsing_toolbar); collapsingToolbar.setTitle("AAA"); final Toolbar toolbar = (Toolbar) view.findViewById(R.id.toolbar); final AppCompatActivity activity = (AppCompatActivity) getActivity(); activity.setSupportActionBar(toolbar); activity.getSupportActionBar().setDisplayHomeAsUpEnabled(true); //toolbar.setNavigationIcon(R.drawable.abc_ic_ab_back_mtrl_am_alpha); toolbar.setNavigationOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { NavUtils.navigateUpFromSameTask(getActivity()); } }); final ImageView imageView = (ImageView) view.findViewById(R.id.backdrop); Glide.with(this).load(R.drawable.cheese_1).centerCrop().into(imageView); return view; } } 

BottomSheetFragmentActivity.java

 public final class BottomSheetFragmentActivity extends AppCompatActivity { protected BottomSheetLayout bottomSheetLayout; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_bottom_sheet_fragment); bottomSheetLayout = (BottomSheetLayout) findViewById(R.id.bottomsheet); findViewById(R.id.bottomsheet_fragment_button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new MyFragment().show(getSupportFragmentManager(), R.id.bottomsheet); } }); bottomSheetLayout.setShouldDimContentView(false); bottomSheetLayout.setPeekOnDismiss(true); bottomSheetLayout.setPeekSheetTranslation(200); bottomSheetLayout.setInterceptContentTouch(false); bottomSheetLayout.setDefaultViewTransformer(new BaseViewTransformer() { @Override public void transformView(final float translation, final float maxTranslation, final float peekedTranslation, final BottomSheetLayout parent, final View view) { Log.d("AppLog", "translation:" + translation + " maxTranslation:" + maxTranslation + " peekedTranslation:" + peekedTranslation); } }); } } 

Casi funciona bien. El único problema es la transición de # 3 a # 2:

Introduzca aquí la descripción de la imagen

La pregunta

¿Qué hay de malo en el código? ¿Qué puedo hacer para lograr el comportamiento requerido?

Nota : Lea las ediciones en la parte inferior


OK, he encontrado una manera de hacerlo, pero tuve que cambiar el código de varias clases, por lo que la hoja inferior sabría del estado de la appBarLayout (ampliado o no), e ignorar el desplazamiento en caso de que sea No expandido:

BottomSheetLayout.java

Campos añadidos:

 private AppBarLayout mAppBarLayout; private OnOffsetChangedListener mOnOffsetChangedListener; private int mAppBarLayoutOffset; 

Init () – añadió esto:

  mOnOffsetChangedListener = new OnOffsetChangedListener() { @Override public void onOffsetChanged(final AppBarLayout appBarLayout, final int verticalOffset) { mAppBarLayoutOffset = verticalOffset; } }; 

Función agregada para establecer el appBarLayout:

 public void setAppBarLayout(final AppBarLayout appBarLayout) { if (mAppBarLayout == appBarLayout) return; if (mAppBarLayout != null) mAppBarLayout.removeOnOffsetChangedListener(mOnOffsetChangedListener); mAppBarLayout = appBarLayout; mAppBarLayout.addOnOffsetChangedListener(mOnOffsetChangedListener); } 

OnDetachedFromWindow () – añadió esto:

  if (mAppBarLayout != null) mAppBarLayout.removeOnOffsetChangedListener(mOnOffsetChangedListener); 

OnTouchEvent () – añadió esto:

  ... if (bottomSheetOwnsTouch) { if (state == State.EXPANDED && scrollingDown && mAppBarLayout != null && mAppBarLayoutOffset != 0) { event.offsetLocation(0, sheetTranslation - getHeight()); getSheetView().dispatchTouchEvent(event); return true; } ... 

Esos fueron los principales cambios. Ahora para qué los fija:

MyFragment.java

OnCreateView () – añadió esto:

  mBottomSheetLayout.setAppBarLayout((AppBarLayout) view.findViewById(R.id.appbar)); 

También he añadido esta función:

  public void setBottomSheetLayout(final BottomSheetLayout bottomSheetLayout) { mBottomSheetLayout = bottomSheetLayout; } 

Ahora esto es cómo la actividad dice el fragmento sobre el appBarLayout:

  final MyFragment myFragment = new MyFragment(); myFragment.setBottomSheetLayout(bottomSheetLayout); myFragment.show(getSupportFragmentManager(), R.id.bottomsheet); 

El proyecto ya está disponible en GitHub:

https://github.com/AndroidDeveloperLB/ThreePhasesBottomSheet

Espero que no tenga ningún bicho.


La solución tiene errores, por desgracia, así que no voy a marcar esta respuesta como la correcta:

  1. Sólo funciona bien en Android 6 y superior. Otros tienen un comportamiento extraño de mostrar la hoja inferior ampliada por una pequeña fracción de un tiempo, cada vez que se muestra.
  2. Los cambios de orientación no guardan en absoluto el estado del desplazamiento, por lo que lo deshabilité.
  3. Raro problema de ser capaz de desplazarse dentro del contenido de la hoja inferior mientras todavía está colapsado (en la parte inferior)
  4. Si un teclado se mostró antes, la hoja inferior podría llegar a ser de pantalla completa cuando se intenta ser echado un vistazo.

Si alguien puede ayudar con ella, por favor.


Para el problema # 1, he intentado agregar una corrección estableciendo la visibilidad a INVISIBLE cuando la hoja inferior no se mira todavía, pero no siempre funciona, especialmente si se muestra un teclado.


Para el problema # 1, he encontrado la forma de arreglarlo, simplemente envolviendo (en "fragment_my.xml") el CoordinatorLayout con cualquier vista que desee utilizar (he utilizado FrameLayout), y también poner una vista de tamaño completo en Lo (sólo pongo "View"), como tal:

 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <!--This full sized view, together with the FrameLayout above, are used to handle some weird UI issues on pre-Android-6 --> <View android:layout_width="match_parent" android:layout_height="match_parent"/> <...CollapsingToolbarLayout ... 

Probablemente confundía el bottomSheet cuando tenía el CoordinatorLayout siendo su punto de vista. He actualizado el proyecto, pero aún así, si hay alguna manera de tener una solución más agradable, me gustaría saber sobre él.


En los últimos meses, Google ha publicado su propia clase bottomSheet, pero como he encontrado, tiene muchos problemas, por lo que ni siquiera puedo probarlo.

GRAN ACTUALIZACIÓN

Debido a que había como 4 o 5 pregunta sobre el mismo tema, pero con diferentes requisitos, y traté de responder a todos ellos, pero un administrador no cortesía eliminado / cerró, me hizo crear un boleto para cada uno y cambiarlos a Evitar "copiar pegar" Te voy a dejar un enlace a la respuesta completa en donde se puede encontrar toda la explicación sobre cómo obtener un comportamiento completo como Google Maps.


Respondiendo a tu pregunta

¿Cómo imitar el comportamiento de las fases 3 de la parte inferior de Google Maps?

Con la biblioteca de soporte 23.x.x + puede hacerlo modificando el BottomSheetBehavior defecto, agregando una stat más con los siguientes pasos:

  1. Cree una clase Java y extiéndala desde CoordinatorLayout.Behavior<V>
  2. Copie el código de la pasta desde el archivo BottomSheetBehavior predeterminado a su nuevo.
  3. Modifique el método clampViewPositionVertical con el siguiente código:
  4. Añadir un nuevo estado:

     public static final int STATE_ANCHOR_POINT = X; 
  5. Modifique los métodos siguientes: onLayoutChild , onStopNestedScroll , BottomSheetBehavior<V> from(V view) y setState (opcional)

Voy a agregar los métodos modificados y un enlace al proyecto de ejemplo .

Y aquí es cómo se ve su aspecto:
[ CustomBottomSheetBehavior ]

¿Lo intentaste? http://android-developers.blogspot.in/2016/02/android-support-library-232.html?m=1 Aquí se dice que sólo podemos especificar un comportamiento de diseño de hoja inferior.

ACTUALIZAR:

Básicamente, el enlace estados-

Al adjuntar BottomSheetBehavior a un niño View of a CoordinatorLayout (es decir, añadiendo app: layout_behavior = "android.support.design.widget.BottomSheetBehavior"), obtendrá automáticamente la detección de toque apropiada para la transición entre cinco estados:

 STATE_COLLAPSED: this collapsed state is the default and shows just a portion of the layout along the bottom. The height can be controlled with the app:behavior_peekHeight attribute (defaults to 0) STATE_DRAGGING: the intermediate state while the user is directly dragging the bottom sheet up or down STATE_SETTLING: that brief time between when the View is released and settling into its final position STATE_EXPANDED: the fully expanded state of the bottom sheet, where either the whole bottom sheet is visible (if its height is less than the containing CoordinatorLayout) or the entire CoordinatorLayout is filled STATE_HIDDEN: disabled by default (and enabled with the app:behavior_hideable attribute), enabling this allows users to swipe down on the bottom sheet to completely hide the bottom sheet Keep in mind that scrolling containers in your bottom sheet must support nested scrolling (for example, NestedScrollView, RecyclerView, or ListView/ScrollView on API 21+). 

Si desea recibir devoluciones de llamada de los cambios de estado, puede agregar un BottomSheetCallback:

 // The View with the BottomSheetBehavior View bottomSheet = coordinatorLayout.findViewById(R.id.bottom_sheet); BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomSheet); behavior.setBottomSheetCallback(new BottomSheetCallback() { @Override public void onStateChanged(@NonNull View bottomSheet, int newState) { // React to state change } @Override public void onSlide(@NonNull View bottomSheet, float slideOffset) { // React to dragging events } }); 

Mientras BottomSheetBehavior captura el caso persistente de la hoja inferior, esta versión también proporciona BottomSheetDialog y BottomSheetDialogFragment para llenar el caso de uso de las hojas inferiores. Simplemente reemplace AppCompatDialog o AppCompatDialogFragment con sus equivalentes de hoja inferior para tener su diálogo denominado como una hoja inferior.

  • El contenido vertical se alinea en Android EditText
  • No puedo desplazar mi diseño en multi-ventana en Nougat 7.0
  • El conjunto de Android View.GONE no libera espacio en listview
  • Android: ¿se puede ajustar la altura de SlidingDrawer con wrap_content?
  • Error al analizar XML: prefijo no enlazado con barra de herramientas sdk 21
  • Androide diseño biblioteca barra de herramientas sombra
  • El botón de acción flotante no funciona sobre ListView
  • Utilizar interfaces para constantes de espacio de nombres en Android
  • ¿Cómo puedo diseñar un diseño más grande que la pantalla del teléfono?
  • Borde de elementos de la fila de tabla en android
  • Cómo cambiar las animaciones utilizadas por el mecanismo de animateLayoutChanges?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.