Android 带有自定义视图的 CollapsingToolbarLayout

Android CollapsingToolbarLayout with custom View

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

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

谢谢。

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

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

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

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

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

现在就去看看。

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

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

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            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,因此当工具栏折叠时您的视图仍然可见。如果你真的需要折叠背景效果,我相信你可以 "fake" 通过在 OffsetChangeListener 中设置背景 ImageView 的 alpha 来做到这一点。 ;)

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

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

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            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;
}