webentwicklung-frage-antwort-db.com.de

Benutzerdefiniertes Verhalten von CoordinatorLayout mit AppBarLayout

Ich versuche, ein ähnliches Verhalten wie das von Telegram zu erreichen. Auf der Einstellungsseite gibt es ein CircleImage, das beim Scrollen nach oben links vom Topbar-Titel und beim Scrollen nach unten in die Mitte des erweiterten AppBarLayout-Bereichs geht .

 enter image description here

Ich habe meine Arbeit an diesem Beispiel vorgenommen:

https://github.com/saulmm/CoordinatorBehaviorExample

In diesem Fall erstellt der ursprüngliche Coder die Topbar jedoch zweimal neu. Ich möchte das nicht tun, das Standardverhalten der Topbar ist das, was ich brauche, und ich möchte auch das Hamburger Menü und das Optionsmenü nutzen, die aus der Box kommen.

Dies ist meine Ansichtshierarchie:

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

Wie Sie sehen, kann ich das Toolbar-Layout nicht zu einem gleichgeordneten Element von CircleImage machen, sodass ich sie nicht mit der layoutDependsOn-Methode verknüpfen kann. Ich habe versucht, an AppBarLayout zu binden, wobei mein Code auf dem Github-Repo basiert, aber um ehrlich zu sein, kann ich nicht verstehen, was im ursprünglichen Code passiert.

14
MichelReap

Mein Verhalten wurde auf die gleiche Weise wie bei Saul umgesetzt. Der größte Unterschied besteht darin, dass ich gerne eine nicht sichtbare Ansicht wie eine Space einfügen möchte, in der das Kreisbild enden soll. Anschließend können Sie anhand der Grenzen dieser Ansicht bestimmen, wie das Kreisbild verschoben und seine Größe geändert werden soll.

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

    }
}

Und hier ist das Layout. Eine wichtige Sache, die ich herausgefunden habe, ist, dass für die Kreisbildansicht eine Höheneinstellung erforderlich ist, damit sie im ausgeblendeten Modus auf der Symbolleiste angezeigt wird. Andernfalls wäre sie hinter der Symbolleiste und nicht dargestellt.

<?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>

Das gesamte Demo-Projekt finden Sie unter https://github.com/klarson2/CustomCoordinatorLayoutBehavior

23
kris larson

Eine Möglichkeit wäre, eine benutzerdefinierte Ansicht für Ihre ToolBar zu erstellen und den roten Punkt in der ToolBar auszublenden, wenn sie erweitert ist, und stattdessen eine ImageView mit dem roten Punkt anzuzeigen (der ausgeblendet ist, wenn die Symbolleiste ausgeblendet ist). 

Unter dieser Antwort können Sie sehen, wie Sie einer ToolBar eine benutzerdefinierte Ansicht hinzufügen: https://stackoverflow.com/a/27859966/5052976

Danach erstellen Sie einfach eine ImageView, die sichtbar ist, wenn die ToolBar erweitert wird.

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;
    }
}
});

Leider kann ich das jetzt nicht testen, aber ich denke es sollte funktionieren ;-)

0
Katharina