【问题标题】:How to Dynamically Adjust Contents of Collapsing Toolbar如何动态调整折叠工具栏的内容
【发布时间】:2018-02-19 23:18:04
【问题描述】:

我想知道是否有任何方法可以动态调整CollapsingToolbarLayout 的内容?我在折叠工具栏中有一个视图,当工具栏折叠时它会被部分覆盖:

如您所见,最顶部的徽标被遮盖了(“阅读器”中的“d”)。这是因为在我的布局的 XML 中,我将下边距设置为 20dp,无论工具栏是展开还是折叠,这个边距都保持不变。

我的想法是根据折叠工具栏是展开(使用 20dp 底部边距)还是折叠(将底部边距设置为 0dp)来动态调整底部边距。 AppBarLayout 有一个OnOffsetChangeListener,所以我认为这将是实现这一目标的好方法。

据我了解,为了调整徽标的边距,我需要以编程方式调整包含徽标的ImageViewLayoutParams。我在这里尝试过这样做:

    final ImageView logo = (ImageView) mRootView.findViewById(R.id.fragment_article_detail_logo);

    final CollapsingToolbarLayout.LayoutParams layoutParams = (CollapsingToolbarLayout.LayoutParams) logo.getLayoutParams();

    AppBarLayout appBarLayout = (AppBarLayout) mRootView.findViewById(R.id.fragment_article_detail_app_bar_layout);
    // This is the total range of the AppBarLayout
    int scrollRange = appBarLayout.getTotalScrollRange();
    // If scrollRange is negative, then that means that the app bar is completely collapsed
    final int collapsedOffset = scrollRange * -1;
    appBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
        @Override
        public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
            // If the app bar is completely collapsed, we want to remove the bottom margin
            if (verticalOffset == collapsedOffset) {
                layoutParams.setMargins(layoutParams.leftMargin, layoutParams.topMargin, layoutParams.rightMargin, 0);
                logo.setLayoutParams(layoutParams);
                logo.requestLayout();
            }
            // If the app bar is fully expanded, we'll apply the bottom margin
            else if (verticalOffset == 0){
                layoutParams.setMargins(layoutParams.leftMargin, layoutParams.topMargin, layoutParams.rightMargin, getBottomMargin());
                logo.setLayoutParams(layoutParams);
                logo.requestLayout();
            }
        }
    });

问题是走这条路,我的边距由于某种原因没有被应用。 getBottomMargin() 方法旨在返回以像素为单位的下边距量:

private int getBottomMargin() {
    Resources resources = getResources();
    return (int) TypedValue.applyDimension(
            TypedValue.COMPLEX_UNIT_DIP,
            resources.getDimension(R.dimen.app_bar_logo_bottom_margin),
            resources.getDisplayMetrics());
}

即使我在 XML 中应用了边距,并在 Java 中动态调整它们,我的徽标没有正确应用左边距或下边距:

仅供参考,问题前面的屏幕截图是在动态尝试调整徽标的边距之前。

所以我的主要问题是:我在这里做错了什么?我可以通过我正在采取的方法实现我希望的目标吗?我知道最简单的方法是将徽标的高度和宽度调整为小于wrap_content。不过,我觉得有更好的方法来做到这一点(让徽标变小似乎太容易了)。

如果它有帮助,这是我的布局 XML 的一部分,其中包含 AppBarLayoutCollapsingToolbarLayout

<android.support.design.widget.AppBarLayout
    android:id="@+id/fragment_article_detail_app_bar_layout"
    android:layout_width="match_parent"
    android:layout_height="@dimen/app_bar_height_expanded"
    android:minHeight="@dimen/app_bar_height_minimum">

    <android.support.design.widget.CollapsingToolbarLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:contentScrim="@color/colorPrimary"
        app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
        app:titleEnabled="false">

        <ImageView
            android:id="@+id/photo"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scaleType="centerCrop"
            app:layout_collapseMode="parallax"
            app:layout_collapseParallaxMultiplier="0.5" />

        <android.support.v7.widget.Toolbar
            android:id="@+id/fragment_article_detail_toolbar"
            android:layout_width="match_parent"
            android:layout_height="@dimen/toolbar_height"
            android:layout_marginLeft="@dimen/keyline_screen_edge_left"
            app:layout_collapseMode="pin" />

        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="@dimen/app_bar_height_minimum"
            android:background="@android:color/transparent"
            app:layout_collapseMode="pin">

            <ImageButton
                android:id="@+id/action_up"
                android:layout_width="wrap_content"
                android:layout_height="@dimen/toolbar_height"
                android:background="?selectableItemBackgroundBorderless"
                android:paddingLeft="@dimen/keyline_screen_edge_left"
                android:src="@drawable/ic_arrow_back" />

        </FrameLayout>

        <ImageView
            android:id="@+id/fragment_article_detail_logo"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom"
            android:layout_marginBottom="@dimen/app_bar_logo_bottom_margin"
            android:layout_marginLeft="@dimen/keyline_inner_left"
            android:contentDescription="@string/app_name"
            android:src="@drawable/logo"
            app:layout_collapseMode="pin" />

    </android.support.design.widget.CollapsingToolbarLayout>

</android.support.design.widget.AppBarLayout>

【问题讨论】:

    标签: android material-design android-collapsingtoolbarlayout


    【解决方案1】:

    实际上,我自己也能做到这一点。我对OnOffsetChangedListenergetBottomMargin() 都做了一些更改:

    mLogo = (ImageView) mRootView.findViewById(R.id.fragment_article_detail_logo);
    final CollapsingToolbarLayout.LayoutParams layoutParams = (CollapsingToolbarLayout.LayoutParams) mLogo.getLayoutParams();
    
    AppBarLayout appBarLayout = (AppBarLayout) mRootView.findViewById(R.id.fragment_article_detail_app_bar_layout);
    appBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
        @Override
        public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
    
            // If toolbar is collapsed, we'll remove the bottom margin
            if (Math.abs(verticalOffset) == appBarLayout.getTotalScrollRange()){
                layoutParams.setMargins(layoutParams.leftMargin, layoutParams.topMargin, layoutParams.rightMargin, 0);
                mLogo.setLayoutParams(layoutParams);
            }
    
            /*
            Otherwise, we'll add the bottom margin back in. I could have kept just as a plain
            else statement. However, scrolling started to feel jittery since this was being
            called each time app bar is being expanded. The additional if() has been added to
            ensure that the bottom margin is only added once.
             */
            else if (layoutParams.bottomMargin != getBottomMargin()) {
                layoutParams.setMargins(layoutParams.leftMargin, layoutParams.topMargin, layoutParams.rightMargin, getBottomMargin());
                mLogo.setLayoutParams(layoutParams);
            }
        }
    });
    

    我之前尝试的问题在于:

    int scrollRange = appBarLayout.getTotalScrollRange();
    final int collapsedOffset = scrollRange * -1;
    

    这些变量是在 onOffsetChanged() 之外设置的,或者更确切地说是在将 OnOffsetChangedListener 添加到 AppBarLayout 之前设置的。我对 Android 框架如何布置 UI 并没有非常明确的理解。但是,我确实知道的一件事是,它会多次尝试布局 UI,直到一切都正确定位。当我调试我之前的尝试时,scrollRange 始终为 0(这使得 collapsedOffset 本身也为 0)。我的猜测是,当我设置这些变量时,应用栏还没有完全布置好,所以这可能是导致它们始终为 0 的原因。不过我对此并不完全确定。

    我所做的另一项额外更改是getBottomMargin()

    private int getBottomMargin() {
    
        return getResources().getDimensionPixelSize(R.dimen.app_bar_logo_bottom_margin);
    }
    

    我不确定为什么其他版本的 getBottomMargin() 不起作用。但无论出于何种原因,它使底部边距比实际应该的大得多。

    这些是我对之前的实现所做的最重要的更改。如果有人对如何提高效率或使底部边距的调整更平滑过渡有任何想法,请提供您的意见。希望这可以帮助其他有需要的人:)

    【讨论】:

      猜你喜欢
      • 2016-05-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多