带有自定义视图的 CollapsingToolbarLayout

Posted

技术标签:

【中文标题】带有自定义视图的 CollapsingToolbarLayout【英文标题】:CollapsingToolbarLayout with a custom view 【发布时间】:2015-12-29 06:40:42 【问题描述】:

我正在尝试使用自定义视图实现 CollapsingToolbarLayout,但我无法做到:

我想做什么(对不起,我不能发布图片,所以它在 imgur 上):

展开后,标题是带有图像和标题的个人资料屏幕

不展开(滚动),图像和标题将在工具栏上

但我看到的一切都没有按预期工作

我是这个和棒棒糖动画的新手,所以如果有人可以帮助我,我将非常感激!

(我没有发布示例代码,因为我没有相关内容)

【问题讨论】:

【参考方案1】:

我的解决方案

我有相同的场景要实现,所以我从 dog 示例开始,并对其进行了一些更改,使其与您描述的完全一样。我的代码可以作为该项目的 fork 找到,请参阅 https://github.com/hanscappelle/CoordinatorBehaviorExample

最重要的变化在于布局:

<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_
android:layout_
>

<android.support.design.widget.AppBarLayout
    android:id="@+id/main.appbar"
    android:layout_
    android:layout_
    android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
    >

    <android.support.design.widget.CollapsingToolbarLayout
        android:id="@+id/main.collapsing"
        android:layout_
        android:layout_
        app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
        >

        <FrameLayout
            android:id="@+id/main.framelayout.title"
            android:layout_
            android:layout_
            android:layout_gravity="bottom"
            >

            <LinearLayout
                android:id="@+id/main.linearlayout.title"
                android:layout_
                android:layout_
                android:layout_gravity="bottom|center_horizontal"
                android:orientation="vertical"
                android:paddingBottom="@dimen/spacing_small"
                >

                <TextView
                    android:layout_
                    android:layout_
                    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_
                    android:layout_
                    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_
    android:layout_
    android:scrollbars="none"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    >

    <TextView
        android:layout_
        android:layout_
        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_
    android:layout_
    android:background="@color/primary"
    app:layout_anchor="@id/main.collapsing"
    app:theme="@style/ThemeOverlay.AppCompat.Dark"
    app:title=""
    >

    <LinearLayout
        android:layout_
        android:layout_
        android:orientation="horizontal"
        >

        <Space
            android:layout_
            android:layout_
            />

        <TextView
            android:id="@+id/main.textview.title"
            android:layout_
            android:layout_
            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_
    android:layout_
    android:layout_gravity="top|center_horizontal"
    android:layout_marginTop="@dimen/spacing_normal"
    android:src="@drawable/ninja"
    app:border_color="@android:color/white"
    app:border_
    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>

在我优化的 AvatarImageBehaviour 类中,仅将头像从原始位置移动到属性中配置的位置。因此,如果您希望图像从另一个位置移动,只需在布局内移动它即可。当您这样做时,请确保 AppBarLayout 仍然是它的兄弟,否则将不会在代码中找到它。

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;
    


来源

最常见的例子是https://github.com/saulmm/CoordinatorBehaviorExample 列出的狗。这是一个很好的例子,但确实在扩展视图的中间有工具栏,背景图像也会移动。在我的示例中删除了所有内容。

在http://www.devexchanges.info/2016/03/android-tip-custom-coordinatorlayout.html 可以找到另一种解释,但由于在狗示例中也发现了引用的云/海背景图像,因此显然是建立在另一个之上。

我还发现这个 SO 问题获得了赏金,但无法真正找出最终解决方案是什么 Add icon with title in CollapsingToolbarLayout

最后,这应该是一个可以完成工作的工作库。我已经检查过了,但最初的图像没有居中,我宁愿研究我以前看过的狗的例子。见https://github.com/datalink747/CollapsingAvatarToolbar

阅读更多

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

【讨论】:

嗨,我在使用 layout_behavior 时遇到了问题。如何使用来自不同包的 Behavior 子类?当我从外部包(utils)中使用它时,它会抛出 Class-Not-Found Exception。我使用了完整的包名。但它在同一个包中工作。你能告诉我如何从不同的包中使用它吗? 很好的例子,升级到 androidx 组件仍然有效!

以上是关于带有自定义视图的 CollapsingToolbarLayout的主要内容,如果未能解决你的问题,请参考以下文章

带有波纹动画的Android自定义视图边缘裁剪

带有自定义表格视图单元格的表格视图在滚动时消失

使用带有 TextField 的自定义表格视图单元格关闭数字键盘

SwiftUI 自定义视图接受带有 ID 的视图?

带有 nib 子视图的自定义 UIVIew 无

带有 IB 的表格视图的自定义标题