web-dev-qa-db-fra.com

CollapsingToolbarLayout avec une vue personnalisée

J'essaie d'implémenter CollapsingToolbarLayout avec une vue personnalisée, mais je ne peux pas le faire:

Ce que je veux faire (désolé, je ne peux pas publier d'images, c'est donc imgur):

Développé, l'en-tête est un écran de profil avec image et titre

enter image description here

Pas développé (sur défilement), l'image et le titre seront sur la barre d'outils

enter image description here

Mais tout ce que j'ai vu ne fonctionnait pas comme je m'y attendais

Je suis novice dans ce domaine et dans les animations Lollipop, donc si quelqu'un pouvait m'aider, je vous en serais très reconnaissant!

(Je ne publie pas d'exemple de code car je n'ai rien de pertinent à publier)

21
ChargerDukes

Ma solution

J'avais le même scénario à mettre en œuvre, j'ai donc commencé avec l'exemple de chien et j'ai apporté quelques modifications pour qu'il fonctionne exactement comme vous le décrivez. Mon code peut être trouvé en tant que fork sur ce projet, voir https://github.com/hanscappelle/CoordinatorBehaviorExample

enter image description hereenter image description here

Les modifications les plus importantes concernent la mise en page:

<Android.support.design.widget.CoordinatorLayout
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/main.appbar"
    Android:layout_width="match_parent"
    Android:layout_height="wrap_content"
    Android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
    >

    <Android.support.design.widget.CollapsingToolbarLayout
        Android:id="@+id/main.collapsing"
        Android:layout_width="match_parent"
        Android:layout_height="@dimen/expanded_toolbar_height"
        app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
        >

        <FrameLayout
            Android:id="@+id/main.framelayout.title"
            Android:layout_width="match_parent"
            Android:layout_height="wrap_content"
            Android:layout_gravity="bottom"
            >

            <LinearLayout
                Android:id="@+id/main.linearlayout.title"
                Android:layout_width="wrap_content"
                Android:layout_height="wrap_content"
                Android:layout_gravity="bottom|center_horizontal"
                Android:orientation="vertical"
                Android:paddingBottom="@dimen/spacing_small"
                >

                <TextView
                    Android:layout_width="wrap_content"
                    Android:layout_height="wrap_content"
                    Android:layout_gravity="center_horizontal"
                    Android:gravity="bottom|center_horizontal"
                    Android:text="@string/tequila_name"
                    Android:textColor="@Android:color/white"
                    Android:textSize="@dimen/textsize_xlarge"
                    />

                <TextView
                    Android:layout_width="wrap_content"
                    Android:layout_height="wrap_content"
                    Android:layout_gravity="center_horizontal"
                    Android:layout_marginTop="@dimen/spacing_xxsmall"
                    Android:text="@string/tequila_tagline"
                    Android:textColor="@Android:color/white"
                    />

            </LinearLayout>
        </FrameLayout>
    </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"
    Android:scrollbars="none"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    >

    <TextView
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content"
        Android:lineSpacingExtra="@dimen/spacing_xsmall"
        Android:padding="@dimen/spacing_normal"
        Android:text="@string/lorem"
        Android:textSize="@dimen/textsize_medium"
        />

</Android.support.v4.widget.NestedScrollView>

<Android.support.v7.widget.Toolbar
    Android:id="@+id/main.toolbar"
    Android:layout_width="match_parent"
    Android:layout_height="?attr/actionBarSize"
    Android:background="@color/primary"
    app:layout_anchor="@id/main.collapsing"
    app:theme="@style/ThemeOverlay.AppCompat.Dark"
    app:title=""
    >

    <LinearLayout
        Android:layout_width="wrap_content"
        Android:layout_height="match_parent"
        Android:orientation="horizontal"
        >

        <Space
            Android:layout_width="@dimen/image_final_width"
            Android:layout_height="@dimen/image_final_width"
            />

        <TextView
            Android:id="@+id/main.textview.title"
            Android:layout_width="wrap_content"
            Android:layout_height="match_parent"
            Android:layout_marginLeft="8dp"
            Android:gravity="center_vertical"
            Android:text="@string/tequila_title"
            Android:textColor="@Android:color/white"
            Android:textSize="@dimen/textsize_large"
            />

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

<de.hdodenhof.circleimageview.CircleImageView
    Android:layout_width="@dimen/image_width"
    Android:layout_height="@dimen/image_width"
    Android:layout_gravity="top|center_horizontal"
    Android:layout_marginTop="@dimen/spacing_normal"
    Android:src="@drawable/ninja"
    app:border_color="@Android:color/white"
    app:border_width="@dimen/border_width"
    app:finalHeight="@dimen/image_final_width"
    app:finalXPosition="@dimen/spacing_small"
    app:finalYPosition="@dimen/spacing_small"
    app:finalToolbarHeight="?attr/actionBarSize"
    app:layout_behavior="saulmm.myapplication.AvatarImageBehavior"
    />

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

Et dans la classe AvatarImageBehaviour que j'ai optimisée pour déplacer uniquement l'avatar de la position d'origine vers la position configurée dans les attributs. Donc, si vous souhaitez que l'image se déplace d'un autre emplacement, déplacez-la simplement dans la mise en page. Lorsque vous le faites, assurez-vous que AppBarLayout en est toujours un frère ou qu'il ne sera pas trouvé dans le code.

package saulmm.myapplication;

import Android.content.Context;
import Android.content.res.TypedArray;
import Android.support.design.widget.AppBarLayout;
import Android.support.design.widget.CoordinatorLayout;
import Android.util.AttributeSet;
import Android.view.View;
import de.hdodenhof.circleimageview.CircleImageView;

public class AvatarImageBehavior extends CoordinatorLayout.Behavior<CircleImageView> {

// calculated from given layout
private int startXPositionImage;
private int startYPositionImage;
private int startHeight;
private int startToolbarHeight;

private boolean initialised = false;

private float amountOfToolbarToMove;
private float amountOfImageToReduce;
private float amountToMoveXPosition;
private float amountToMoveYPosition;

// user configured params
private float finalToolbarHeight, finalXPosition, finalYPosition, finalHeight;

public AvatarImageBehavior(
        final Context context,
        final AttributeSet attrs) {

    if (attrs != null) {
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AvatarImageBehavior);
        finalXPosition = a.getDimension(R.styleable.AvatarImageBehavior_finalXPosition, 0);
        finalYPosition = a.getDimension(R.styleable.AvatarImageBehavior_finalYPosition, 0);
        finalHeight = a.getDimension(R.styleable.AvatarImageBehavior_finalHeight, 0);
        finalToolbarHeight = a.getDimension(R.styleable.AvatarImageBehavior_finalToolbarHeight, 0);
        a.recycle();
    }
}

@Override
public boolean layoutDependsOn(
        final CoordinatorLayout parent,
        final CircleImageView child,
        final View dependency) {

    return dependency instanceof AppBarLayout; // change if you want another sibling to depend on
}

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

    // make child (avatar) change in relation to dependency (toolbar) in both size and position, init with properties from layout
    initProperties(child, dependency);

    // calculate progress of movement of dependency
    float currentToolbarHeight = startToolbarHeight + dependency.getY(); // current expanded height of toolbar
    // don't go below configured min height for calculations (it does go passed the toolbar)
    currentToolbarHeight = currentToolbarHeight < finalToolbarHeight ? finalToolbarHeight : currentToolbarHeight;
    final float amountAlreadyMoved = startToolbarHeight - currentToolbarHeight;
    final float progress = 100 * amountAlreadyMoved / amountOfToolbarToMove; // how much % of expand we reached

    // update image size
    final float heightToSubtract = progress * amountOfImageToReduce / 100;
    CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
    lp.width = (int) (startHeight - heightToSubtract);
    lp.height = (int) (startHeight - heightToSubtract);
    child.setLayoutParams(lp);

    // update image position
    final float distanceXToSubtract = progress * amountToMoveXPosition / 100;
    final float distanceYToSubtract = progress * amountToMoveYPosition / 100;
    float newXPosition = startXPositionImage - distanceXToSubtract;
    //newXPosition = newXPosition < endXPosition ? endXPosition : newXPosition; // don't go passed end position
    child.setX(newXPosition);
    child.setY(startYPositionImage - distanceYToSubtract);

    return true;
}

private void initProperties(
        final CircleImageView child,
        final View dependency) {

    if (!initialised) {
        // form initial layout
        startHeight = child.getHeight();
        startXPositionImage = (int) child.getX();
        startYPositionImage = (int) child.getY();
        startToolbarHeight = dependency.getHeight();
        // some calculated fields
        amountOfToolbarToMove = startToolbarHeight - finalToolbarHeight;
        amountOfImageToReduce = startHeight - finalHeight;
        amountToMoveXPosition = startXPositionImage - finalXPosition;
        amountToMoveYPosition = startYPositionImage - finalYPosition;
        initialised = true;
    }
}
}

Sources

L'exemple le plus courant est celui avec le chien répertorié à https://github.com/saulmm/CoordinatorBehaviorExample . C'est un bon exemple mais a en effet la barre d'outils au milieu de la vue développée avec une image de fond qui se déplace également. Tout cela a été supprimé dans mon exemple.

Une autre explication se trouve à http://www.devexchanges.info/2016/03/Android-tip-custom-coordinatorlayout.html mais puisque cette image de fond de nuage/mer référencée y est également trouvée dans le exemple de chien l'un est clairement construit au-dessus de l'autre.

J'ai également trouvé cette SO question avec une prime accordée mais je n'ai pas vraiment pu trouver quelle était la solution finale Ajouter une icône avec un titre dans CollapsingToolbarLayout

Et enfin, cela devrait être une bibliothèque de travail qui fait le travail. Je l'ai vérifié mais l'image initiale n'était pas centrée et j'ai plutôt travaillé sur l'exemple de chien que j'avais regardé auparavant. Voir https://github.com/datalink747/CollapsingAvatarToolbar

Plus à lire

http://saulmm.github.io/mastering-coordinatorhttp://www.androidauthority.com/using-coordinatorlayout-Android-apps-703720/https://developer.Android.com/reference/Android/support/design/widget/CoordinatorLayout.htmlhttps://guides.codepath.com/Android/handling-scrolls-with-coordinatorlayout

29
hcpl