CollapsingToolbarLayout with a custom view

前端 未结 1 1350
清酒与你
清酒与你 2020-12-22 22:21

I\'m trying to implement the CollapsingToolbarLayout with a custom view, but I\'m unable to do it :

What I want to do (sorry I can\'t post images so it\'s on imgur)

相关标签:
1条回答
  • 2020-12-22 22:43

    My Solution

    I had the same scenario to implement so I started with the dog example and made some changes for it to work exactly like you describe. My code can be found as a fork on that project, see https://github.com/hanscappelle/CoordinatorBehaviorExample

    Most important changes are in the layout:

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

    And in the AvatarImageBehaviour class that I optimised for only moving the avatar from the original position to the position configured in the attributes. So if you want the image to move from another location just move it within the layout. When you do so make sure the AppBarLayout is still a sibling of it or it won't be found in 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

    Most common example is the one with the dog listed at https://github.com/saulmm/CoordinatorBehaviorExample . It's a good example but indeed has the toolbar in the middle of the expanded view with a backdrop image that also moves. All that was removed in my example.

    Another explanation is found at http://www.devexchanges.info/2016/03/android-tip-custom-coordinatorlayout.html but since that cloud/sea backdrop image referenced there is also found in the dog example one is clearly build on top of the other.

    I also found this SO question with a bounty awarded but couldn't really find out what the final solution was Add icon with title in CollapsingToolbarLayout

    And finally this should be a working library that does the work. I've checked it out but the initial image wasn't centered and I rather worked on the dog example that I had looked at before. See https://github.com/datalink747/CollapsingAvatarToolbar

    More to read

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

    0 讨论(0)
提交回复
热议问题