Comment imiter le comportement en 3 phases de la feuille de fond de Google Maps?

Contexte

Je suis chargé de créer une interface utilisateur qui se comporte de la même manière que Google Maps affiche une feuille de fond pour un résultat trouvé.

Il comporte trois phases différentes:

  1. Contenu de fond. La partie supérieure rest touchable et ne défile rien en bas
  2. Contenu en plein écran, alors que la zone supérieure comporte un en-tête de grande taille.
  3. Contenu en plein écran, tandis que la zone supérieure ne contient que la barre d’outils.

Voici ce dont je parle sur Google Maps:

Entrez la description de l'image ici

Le problème

En fait, la feuille du bas ne fait pas encore partie de la bibliothèque de conception (bien qu’elle ait été demandée ici ).

De plus, l’interface utilisateur semble assez complexe et nécessite une gestion de la barre d’outils en plusieurs phases.

Ce que j’ai essayé

J’ai trouvé une bonne bibliothèque (suffisante) pour la feuille du bas ( ici ) et ajouté du contenu à son exemple de fragment pour avoir à peu près les mêmes vues que celles affichées sur les exemples de conception de matériel (comme ici ). des phases 2 + 3.

Dans l’application que je crée, je dois également déplacer une icône pendant que vous faites défiler, mais je pense que si je réussis avec le rest, cela devrait être facile. Voici le code:

fragment_my.xml

                                

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().getDisplayMesortingcs().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); } }); } } 

Cela fonctionne presque bien. Le seul problème est la transition de # 3 à # 2:

entrer la description de l'image ici

La question

Quel est le problème avec le code? Que puis-je faire pour obtenir le comportement requirejs?

Note : Lisez les modifications en bas


OK, j’ai trouvé un moyen de le faire, mais je devais changer le code de plusieurs classes, de sorte que la feuille du bas connaisse l’état de appBarLayout (développé ou non) et ignore le défilement au cas où il serait non étendu:

BottomSheetLayout.java

Champs ajoutés:

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

init () – a ajouté ceci:

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

Fonction ajoutée pour définir appBarLayout:

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

onDetachedFromWindow () – a ajouté ceci:

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

onTouchEvent () – a ajouté ceci:

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

Ce sont les principaux changements. Maintenant pour ce qui les définit:

MyFragment.java

onCreateView () – a ajouté ceci:

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

J’ai aussi ajouté cette fonction:

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

Voici comment l’activité raconte le fragment à propos de appBarLayout:

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

Le projet est maintenant disponible sur GitHub:

https://github.com/AndroidDeveloperLB/ThreePhasesBottomSheet

J’espère qu’il n’y a pas de bugs.


La solution a malheureusement des bogues, donc je ne marquerai pas cette réponse comme étant correcte:

  1. Cela ne fonctionne que sur Android 6 et supérieur. D’autres ont un comportement étrange de montrer la feuille inférieure dilatée pendant une infime fraction de temps, chaque fois qu’elle est affichée.
  2. Les changements d’orientation ne sauvegardent pas l’état du défilement, donc je l’ai désactivé.
  3. Problème rare de pouvoir faire défiler le contenu de la feuille du bas alors qu’il est encore réduit (en bas)
  4. Si un clavier était affiché auparavant, la feuille du bas pourrait devenir en plein écran lorsque vous essayez de regarder.

Si quelqu’un peut aider, faites-le.


Pour le numéro 1, j’ai essayé d’append un correctif en définissant la visibilité sur INVISIBLE lorsque la feuille du bas n’est pas encore visible, mais cela ne fonctionne pas toujours, surtout si un clavier est affiché.


Pour le problème n ° 1, j’ai trouvé comment résoudre ce problème, en encapsulant (dans “fragment_my.xml”) le CoordinatorLayout avec n’importe quelle vue que vous souhaitez utiliser (j’ai utilisé FrameLayout) et en y ajoutant une vue complète. le (je viens de mettre “View”), en tant que tel:

    <...CollapsingToolbarLayout ... 

Il a probablement confondu la bottomSheet lorsque j'avais la vue sur le CoordinatorLayout. J'ai mis à jour le projet, mais quand même, s'il y a un moyen d'avoir une solution plus agréable, j'aimerais le savoir.


Au cours des derniers mois, Google a publié sa propre classe bottomSheet, mais comme je l'ai constaté, il y a beaucoup de problèmes, alors je ne peux même pas l'essayer.

BIG UPDATE

Parce qu’il y avait comme 4 ou 5 questions sur le même sujet, MAIS avec des exigences différentes, et j’ai essayé de répondre à tous, mais un administrateur non poli les a supprimés / fermés, me faisant créer un ticket pour chacun éviter le “copier coller” Je vous laisse un lien vers la réponse complète dans laquelle vous pouvez trouver toutes les explications sur la façon d’obtenir un comportement complet comme Google Maps.


Répondre à votre question

Comment imiter le comportement en 3 phases de la feuille de fond de Google Maps?

Avec la bibliothèque de support 23.x.x +, vous pouvez le faire en modifiant la valeur par défaut de BottomSheetBehavior , en ajoutant une statistique supplémentaire en procédant comme suit:

  1. Créez une classe Java et étendez-la à partir de CoordinatorLayout.Behavior
  2. Copiez le code de BottomSheetBehavior fichier BottomSheetBehavior par défaut vers le nouveau.
  3. Modifiez la méthode clampViewPositionVertical avec le code suivant:

     @Override public int clampViewPositionVertical(View child, int top, int dy) { return constrain(top, mMinOffset, mHideable ? mParentHeight : mMaxOffset); } int constrain(int amount, int low, int high) { return amount < low ? low : (amount > high ? high : amount); } 
  4. Ajouter un nouvel état:

     public static final int STATE_ANCHOR_POINT = X; 
  5. Modifiez les méthodes suivantes: onLayoutChild , onStopNestedScroll , BottomSheetBehavior from(V view) et setState (facultatif)

Je vais append ces méthodes modifiées et un lien vers le projet d’exemple .

Et voici à quoi ça ressemble:
[ CustomBottomSheetBehavior ]

Avez-vous essayé ceci? http://android-developers.blogspot.in/2016/02/android-support-library-232.html?m=1 Ici, il est dit que nous pouvons simplement spécifier un comportement de la feuille inférieure.

METTRE À JOUR:

Fondamentalement, le lien indique

En attachant un BottomSheetBehavior à un enfant Vue d’un CoordinatorLayout (c’est-à-dire en ajoutant app: layout_behavior = “android.support.design.widget.BottomSheetBehavior”), vous obtenez automatiquement la détection tactile appropriée entre cinq états:

 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 atsortingbute (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 atsortingbute), 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 vous souhaitez recevoir des rappels de modifications d’état, vous pouvez append 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 } }); 

Alors que BottomSheetBehavior capture le cas de la feuille inférieure persistante, cette version fournit également un BottomSheetDialog et un BottomSheetDialogFragment pour remplir le cas d’utilisation des feuilles de fond modales. Remplacez simplement AppCompatDialog ou AppCompatDialogFragment par leurs équivalents de bas de page pour que votre boîte de dialog soit conçue comme une feuille inférieure.