web-dev-qa-db-fra.com

Comportement personnalisé de CoordinatorLayout avec AppBarLayout

J'essaie d'obtenir un comportement similaire à celui de Telegram, sur la page des paramètres, c'est-à-dire qu'il y a un CircleImage qui, lorsque le défilement monte à gauche du titre de la barre supérieure, et celui qui défile vers le bas, se déplacent au milieu de la barre d'outils développée .

 enter image description here

Je basais mon travail sur cet exemple:

https://github.com/saulmm/CoordinatorBehaviorExample

Mais dans ce cas, le codeur d'origine recrée deux fois la barre supérieure. Je ne veux pas faire cela, le comportement par défaut de la barre supérieure est ce dont j'ai besoin et je veux aussi profiter du menu hamburger et du menu options qui sortent de la boîte.

Voici ma hiérarchie de vues:

DrawerLayout
  |
  |---CoordinatorLayout
       |--AppBarLayout
       |    |-CollapsingToolbarLayout
       |        |-ImageView (backdrop image)
       |        |-Toolbar
       |--NestedScrollView
       |--ImageView (circleimage avatar)

Comme vous pouvez le constater, je ne peux pas faire de la disposition de la barre d’outils un frère de CircleImage, je ne peux donc pas les lier ensemble avec la méthode layoutDependsOn. J'ai essayé de me lier à AppBarLayout en me basant sur le code du dépôt github, mais je ne peux vraiment pas comprendre ce qui se passe dans le code d'origine.

14
MichelReap

Mon comportement a été mis en œuvre de la même manière que celui de Saul. La plus grande différence est que j'aime mettre une vue non visible comme une Space où je voulais que l'image du cercle se termine, puis utiliser les limites de cette vue pour déterminer comment déplacer et redimensionner l'image du cercle.

public class CollapsingImageBehavior extends CoordinatorLayout.Behavior<View> {

    private final static int X = 0;
    private final static int Y = 1;
    private final static int WIDTH = 2;
    private final static int HEIGHT = 3;

    private int mTargetId;

    private int[] mView;

    private int[] mTarget;

    public CollapsingImageBehavior() {
    }

    public CollapsingImageBehavior(Context context, AttributeSet attrs) {

        if (attrs != null) {
            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CollapsingImageBehavior);
            mTargetId = a.getResourceId(R.styleable.CollapsingImageBehavior_collapsedTarget, 0);
            a.recycle();
        }

        if (mTargetId == 0) {
            throw new IllegalStateException("collapsedTarget attribute not specified on view for behavior");
        }
    }

    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
        return dependency instanceof AppBarLayout;
    }

    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {

        setup(parent, child);

        AppBarLayout appBarLayout = (AppBarLayout) dependency;

        int range = appBarLayout.getTotalScrollRange();
        float factor = -appBarLayout.getY() / range;

        int left = mView[X] + (int) (factor * (mTarget[X] - mView[X]));
        int top = mView[Y] + (int) (factor * (mTarget[Y] - mView[Y]));
        int width = mView[WIDTH] + (int) (factor * (mTarget[WIDTH] - mView[WIDTH]));
        int height = mView[HEIGHT] + (int) (factor * (mTarget[HEIGHT] - mView[HEIGHT]));

        CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
        lp.width = width;
        lp.height = height;
        child.setLayoutParams(lp);
        child.setX(left);
        child.setY(top);

        return true;
    }

    private void setup(CoordinatorLayout parent, View child) {

        if (mView != null) return;

        mView = new int[4];
        mTarget = new int[4];

        mView[X] = (int) child.getX();
        mView[Y] = (int) child.getY();
        mView[WIDTH] = child.getWidth();
        mView[HEIGHT] = child.getHeight();

        View target = parent.findViewById(mTargetId);
        if (target == null) {
            throw new IllegalStateException("target view not found");
        }

        mTarget[WIDTH] += target.getWidth();
        mTarget[HEIGHT] += target.getHeight();

        View view = target;
        while (view != parent) {
            mTarget[X] += (int) view.getX();
            mTarget[Y] += (int) view.getY();
            view = (View) view.getParent();
        }

    }
}

Et voici la mise en page. Une chose importante que j'ai découverte est que la vue de l'image circulaire doit avoir un ensemble d'altitude de sorte qu'elle se trouve au sommet de la barre d'outils en mode réduit, sinon elle serait derrière la barre d'outils et non affichée.

<?xml version="1.0" encoding="utf-8"?>
<Android.support.design.widget.CoordinatorLayout
    xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:app="http://schemas.Android.com/apk/res-auto"
    xmlns:tools="http://schemas.Android.com/tools"
    Android:id="@+id/coordinator_layout"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:fitsSystemWindows="true"
    tools:context="com.krislarson.customcoordinatorlayoutbehavior.ScrollingActivity">

    <Android.support.design.widget.AppBarLayout
        Android:id="@+id/app_bar"
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content"
        Android:fitsSystemWindows="true"
        Android:theme="@style/AppTheme.AppBarOverlay">

        <Android.support.design.widget.CollapsingToolbarLayout
            Android:id="@+id/toolbar_layout"
            Android:layout_width="match_parent"
            Android:layout_height="280dp"
            Android:minHeight="108dp"
            Android:fitsSystemWindows="true"
            app:title="Abby"
            app:contentScrim="?attr/colorPrimary"
            app:expandedTitleGravity="center_horizontal"
            app:expandedTitleMarginTop="140dp"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">

            <ImageView
                Android:id="@+id/background"
                Android:layout_width="match_parent"
                Android:layout_height="match_parent"
                Android:src="@drawable/sunset"
                app:layout_collapseMode="parallax"
                Android:scaleType="centerCrop"/>

            <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/AppTheme.PopupOverlay">

                <Space
                    Android:id="@+id/circle_collapsed_target"
                    Android:layout_width="40dp"
                    Android:layout_height="40dp"/>

            </Android.support.v7.widget.Toolbar>


        </Android.support.design.widget.CollapsingToolbarLayout>

    </Android.support.design.widget.AppBarLayout>

    <include layout="@layout/content_scrolling"/>

    <de.hdodenhof.circleimageview.CircleImageView
        Android:id="@+id/circle_image_view"
        Android:layout_width="120dp"
        Android:layout_height="120dp"
        Android:src="@drawable/abby"
        Android:layout_marginTop="220dp"
        Android:layout_gravity="top|center_horizontal"
        Android:elevation="8dp"
        app:border_color="@Android:color/black"
        app:border_width="2dp"
        app:collapsedTarget="@id/circle_collapsed_target"
        app:layout_behavior="com.krislarson.customcoordinatorlayoutbehavior.CollapsingImageBehavior"/>

</Android.support.design.widget.CoordinatorLayout>

Vous pouvez voir le projet de démonstration dans son intégralité à https://github.com/klarson2/CustomCoordinatorLayoutBehavior

23
kris larson

Une possibilité serait de créer une vue personnalisée pour votre ToolBar et de masquer le point rouge dans la ToolBar si elle est développée et d'afficher une ImageView avec le point rouge à la place (masqué lorsque la barre d'outils est réduite). 

Vous pouvez voir comment ajouter une vue personnalisée à une ToolBar à cette réponse: https://stackoverflow.com/a/27859966/5052976

Cela fait, créez simplement une ImageView visible lorsque la ToolBar est développée.

final CollapsingToolbarLayout collapsingToolbarLayout = (CollapsingToolbarLayout) findViewById(R.id.collapsingToolbarLayout);
AppBarLayout appBarLayout = (AppBarLayout) findViewById(R.id.appBarLayout);
appBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
boolean isShow = false;
int scrollRange = -1;

@Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
    if (scrollRange == -1) {
        scrollRange = appBarLayout.getTotalScrollRange();
    }
    if (scrollRange + verticalOffset == 0) {
        //show toolbar dot and hide imageview dot
        isShow = true;
    } else if(isShow) {
        //hide toolbar dot and show imageview dot
        isShow = false;
    }
}
});

Malheureusement, je ne peux pas tester cela maintenant mais je pense que cela devrait fonctionner ;-)

0
Katharina