Android CollapsingToolbarLayout 与自定义视图

Posted

技术标签:

【中文标题】Android CollapsingToolbarLayout 与自定义视图【英文标题】:Android CollapsingToolbarLayout with custom View 【发布时间】:2015-09-02 08:55:09 【问题描述】:

我正在关注 Cheesesquare 示例项目以了解新的设计材料库。

我想知道是否有办法使用带有 ImageView、标题和副标题的自定义视图(如 Telegram),而不是 CollapsingToolbarLayout 小部件提供的简单标题。

谢谢。

【问题讨论】:

您是否尝试过先包含ImageViewCollapsingToolbarLayout,然后尝试包含2 个TextViews 的LinearLayout 我试过了,但我的目的是用我的 CustomView 替换标题,以保留工具栏中标题的动画。 @shkschneider 你能解释一下吗。我想用 【参考方案1】:

我遇到了同样的问题,并花了很多时间试图找到解决方案。我的解决方案是在CollapsingToolbarLayout 中添加折叠视图(ImageView 和 TextView),然后在代码中处理转换。这种方式比从 CollapsingToolbarLayout 扩展更灵活、更简单。

首先,您需要在 CollapsingToolbarLayout 中添加具有视差属性的视图:

        <ImageView
            android:layout_
            android:layout_
            android:paddingTop:"80dp"
            android:src="@drawable/icon"
            app:layout_collapseMode="parallax"
            app:layout_collapseParallaxMultiplier="0.8"/> //set vertical transition here

然后在OnOffsetchangeListner 的帮助下设置视图的缩放:

  private static final float SCALE_MINIMUM=0.5f;
  appBarLayout.setOnWorkingOffsetChange(new  ControllableAppBarLayout.OnWorkingOffsetChange() 
        @Override
        public void onOffsetChange(int offSet, float collapseDistance) 
            imageView.setScaleX(1 + (collapseDistance * SCALE_MINIMUM));
            imageView.setScaleY(1 + (collapseDistance * SCALE_MINIMUM));

            textView.setScaleX(1 + (collapseDistance * SCALE_MINIMUM));
            textView.setScaleY(1 + (collapseDistance * SCALE_MINIMUM));

            // You can also setTransitionY/X, setAlpha, setColor etc.
        
    );

不知何故,默认的offsetChangedListener 对我来说不能正常工作(你可能还是应该先用默认的监听器试试),所以我使用了来自https://gist.github.com/blipinsk/3f8fb37209de6d3eea99 的ControllableAppBarLayout 并添加了以下内容:

private OnWorkingOffsetChange onWorkingOffsetChange;

@Override
public void onOffsetChanged(AppBarLayout appBarLayout, int i) 
    if (!isInEditMode()) 
        onWorkingOffsetChange.onOffsetChange(i, (float) i / appBarLayout.getTotalScrollRange());
    


public void setOnWorkingOffsetChange(OnWorkingOffsetChange listener) 
    this.onWorkingOffsetChange = listener;


public interface OnWorkingOffsetChange 
    void onOffsetChange(int offSet, float collapseDistance);

唯一的问题是,您需要设置 app:contentScrim="#00000000"(透明) 对于您的CollapsingToolbarLayout,因此当工具栏折叠时您的视图仍然可见。如果你真的需要折叠背景效果,我相信你可以通过在OffsetChangeListener 中设置背景 ImageView 的 alpha 来“伪造”它。 ;)

【讨论】:

请您再解释一下。我有同样的问题,当我向上滚动时,我只想在CollapsingToolBarLayout 的中心使用一个图标和一个标题,而不是图标必须飞到工具栏的左侧,通常应用程序图标存在并且标题在右侧图标。【参考方案2】:

从小部件本身来看,似乎没有直接启用此功能的方法,就像可以将自定义视图添加到工具栏一样。

但是,您可以尝试做的是打开CollapsingToolbarLayout.class 的源代码并查看CollapsingTextHelper.class 是如何用于设置标题的。您可以尝试通过从CollapsingToolbarLayout 扩展来制作自己的小部件。

如果您以前没有创建过自定义组件/视图,这些链接可以帮助您创建它们: Custom Views, Custom Components

我还没有尝试过,但实际上我正在考虑尝试实现与您正在寻找的类似解决方案。到目前为止,我将遵循的步骤:

    attrs.xml中的字幕设置创建自定义属性 通过扩展原始MyCollapsingToolbarLayout 创建您自己的MyCollapsingToolbarLayout。 确保在构造函数中调用super,这样原始组件将保持不变。 通过将新的CollapsingTextHelper 添加到您的组件来创建subtitleTextHelper。 覆盖 onDraw 以实际绘制您的字幕。 使用您的字幕属性(默认样式等,可能是固定的字幕文本)更新包含您的 CollapingsToolbarLayout 的布局。 在包含您的CollapsingToolbarActivity 中应用更改。 (将CollapsingToolbarlayout 转换为MyCollapingsToolbarLayout、设置字幕、额外的自定义设置等)。 交叉手指,测试。

现在去看看。

【讨论】:

这是一项非常艰苦的工作。 CollapsingToolbarLayout 是一个糟糕的小部件。 @StingRay5 你能帮我在 onDraw() 里面做什么吗?或者请分享您的代码。【参考方案3】:

将稍微编辑 Christopher 的答案,以展示如何让自定义视图在折叠时不消失:

首先,您需要在 CollapsingToolbarLayout 中添加具有视差属性的视图:

        <ImageView
            android:layout_
            android:layout_
            android:paddingTop:"80dp"
            android:src="@drawable/icon"
            app:layout_collapseMode="parallax"
            app:layout_collapseParallaxMultiplier="0.8"/> //set vertical transition here

改为以编程方式添加自定义视图,它不会在折叠时消失。例如,这是一个包含标题和副标题的视图:

    final FrameLayout frameLayout = new FrameLayout(mActivity);
    FrameLayout.LayoutParams frameLayoutParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
            FrameLayout.LayoutParams.MATCH_PARENT);
    frameLayout.setLayoutParams(frameLayoutParams);


    // Create new LinearLayout
    final LinearLayout linearLayout = new LinearLayout(mActivity);
    frameLayoutParams =new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, dpToPixels(78));
    frameLayoutParams.gravity = Gravity.BOTTOM;
    linearLayout.setLayoutParams(frameLayoutParams);
    linearLayout.setOrientation(LinearLayout.VERTICAL);


    // Add textviews
    final TextView textView1 = new TextView(mActivity);
    LinearLayout.LayoutParams linearLayoutParams =new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
            LinearLayout.LayoutParams.WRAP_CONTENT);
    frameLayoutParams.gravity = Gravity.BOTTOM;
    textView1.setLayoutParams(linearLayoutParams);
    textView1.setText("Title");
    textView1.setTextColor(ContextCompat.getColor(mActivity, R.color.colorWhite));
    textView1.setTextSize(TypedValue.COMPLEX_UNIT_SP, 40);
    linearLayout.addView(textView1);


    final TextView textView2 = new TextView(mActivity);
    linearLayoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
            LinearLayout.LayoutParams.WRAP_CONTENT);
    textView2.setLayoutParams(linearLayoutParams);
    textView2.setText("Subtitle");
    textView2.setTextColor(ContextCompat.getColor(mActivity, R.color.colorWhite));
    textView2.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20);
    linearLayout.addView(textView2);

    frameLayout.addView(linearLayout);


    collapsingToolbar.addView(frameLayout);
    final float SCALE_MIN=0.4f;
    AppBarLayout appBarLayout = (AppBarLayout) mActivity.findViewById(R.id.appBarLayout);
    appBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() 
        @Override
        public void onOffsetChanged(AppBarLayout appBarLayout, int offSet) 
            float collapsedRatio = (float) offSet / appBarLayout.getTotalScrollRange();
            linearLayout.setScaleX(1 + (collapsedRatio * SCALE_MIN));
            linearLayout.setScaleY(1 + (collapsedRatio * SCALE_MIN));
            FrameLayout.LayoutParams frameLayoutParams =new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, dpToPixels(78));
            frameLayoutParams.gravity = Gravity.BOTTOM;
            frameLayoutParams.setMargins(Math.round(dpToPixels(48) * (1+collapsedRatio)), 0, 0, Math.round(dpToPixels(15) * collapsedRatio));
            linearLayout.setLayoutParams(frameLayoutParams);
            // You can also setTransitionY/X, setAlpha, setColor etc.
        
    );

/////

float lastCollapsedRatio = -2;

////

private int dpToPixels(int padding_in_dp)
    final float scale = getResources().getDisplayMetrics().density;
    int padding_in_px = (int) (padding_in_dp * scale + 0.5f);
    return padding_in_px;

【讨论】:

绝招。除了我突然让我的日志充满了以下内容。关于如何避免这种情况的任何想法? (啊,为什么 SO cmets 不能支持换行符?)requestLayout() improperly called by android.support.design.widget.CollapsingToolbarLayouta9c7244 V.ED..... ......I. 0,0-1440,405 #7f1100fb app:id/collapsingToolbarLayout during layout: running second layout passrequestLayout() improperly called by android.widget.LinearLayout4058f2d V.E...... ......I. 168,132-1440,405 during layout: running second layout pass 我添加了一个检查以查看collapsedRatio 是否真的发生了变化,并且只在这些情况下进行缩放。这使得所有关于第二次布局传递的抱怨都消失了。 我已经有一段时间没有使用这个代码了。但我确实记得遇到过。你在哪里添加支票?我可以更新我的答案。 @RoySolberg

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

CollapsingToolbar, Viewpager 有 recyclerview 和 NestedScrollview

如何确定 CollapsingToolbar 已折叠?

使用 CollapsingToolbar 滚动时如何更新工具栏

如何在CollapsingToolbar中使用Map的点击事件

AppBarLayout 上的 EdgeEffect

带有标签的Flutter SliverAppBar覆盖内容