当将fitsSystemWindows设置为true时,CollapsingToolbarLayout中的ViewGroup显示额外的底部填充

时间:2018-01-07 13:38:23

标签: android android-layout

我正在构建一个应用程序,并且有一个配置文件片段,显示配置文件背景图像和一些其他元素。如下面的截图:

enter image description here

我的问题是:为什么AppBarLayout结束时会有额外的底部填充?

这个片段的xml布局文件(我基本上是从Cheesesquare复制):

<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:fitsSystemWindows="true"
    >

    <android.support.design.widget.AppBarLayout
        android:id="@+id/appbar"
        android:theme="@style/ThemeOverlay.AppCompat.ActionBar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:fitsSystemWindows="true"
        >

        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/toolbar_layout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:contentScrim="@android:color/white"
            app:layout_scrollFlags="scroll|exitUntilCollapsed"
            >

            <android.support.constraint.ConstraintLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:fitsSystemWindows="true"
                >

                <ImageView
                    android:id="@+id/profile_bg"
                    android:layout_width="match_parent"
                    android:layout_height="@dimen/height_profile_background_max"
                    android:scaleType="centerCrop"
                    android:fitsSystemWindows="true"
                    app:layout_constraintTop_toTopOf="parent"
                    app:layout_constraintStart_toStartOf="parent"
                    app:layout_constraintEnd_toEndOf="parent"
                    />

                <de.hdodenhof.circleimageview.CircleImageView
                    android:id="@id/avatar"
                    android:layout_width="@dimen/avatar_size_large"
                    android:layout_height="@dimen/avatar_size_large"
                    android:layout_marginStart="@dimen/horizontal_padding_normal"
                    app:civ_border_width="2dp"
                    app:civ_border_color="@android:color/white"
                    app:civ_border_overlay="false"
                    app:layout_constraintTop_toBottomOf="@id/profile_bg"
                    app:layout_constraintBottom_toBottomOf="@id/profile_bg"
                    app:layout_constraintStart_toStartOf="parent"
                    />

                <TextView
                    android:id="@id/user_display_name"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:paddingTop="@dimen/vertical_padding_small"
                    android:layout_marginStart="@dimen/horizontal_padding_normal"
                    android:textSize="@dimen/text_size_medium"
                    android:textStyle="bold"
                    android:textColor="@color/text_color_normal"
                    app:layout_constraintTop_toBottomOf="@id/avatar"
                    app:layout_constraintStart_toStartOf="parent"
                    />

                <TextView
                    android:id="@+id/description"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:paddingTop="@dimen/vertical_padding_small"
                    android:paddingBottom="@dimen/vertical_padding_normal"
                    android:layout_marginStart="@dimen/horizontal_padding_normal"
                    android:textSize="@dimen/text_size_small"
                    android:textColor="@color/text_color_normal"
                    android:maxLines="5"
                    app:layout_constraintTop_toBottomOf="@id/user_display_name"
                    app:layout_constraintStart_toStartOf="parent"
                    />

                <View
                    android:layout_width="match_parent"
                    android:layout_height="@dimen/vertical_divider_height"
                    android:background="@color/divider_background_color"
                    app:layout_constraintStart_toStartOf="parent"
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintBottom_toBottomOf="parent"
                    />
            </android.support.constraint.ConstraintLayout>

            <android.support.v7.widget.Toolbar
                android:id="@id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:contentInsetStartWithNavigation="0dp"
                app:layout_collapseMode="pin"
                />
        </android.support.design.widget.CollapsingToolbarLayout>

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

    <android.support.v7.widget.RecyclerView
        android:id="@android:id/list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="vertical"
        android:scrollbarSize="2dp"
        android:scrollbarStyle="outsideOverlay"
        app:layoutManager="LinearLayoutManager"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        />

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

如上所示,我没有向AppBarLayout添加任何填充。

我将我的布局与Cheesesquare进行比较,发现唯一不同的是我在AppBarLayout中放置了一个复杂的ConstraintLayout。 Cheesesquare表现正常,因为它设置了某个值的layout_height

<android.support.design.widget.AppBarLayout
    android:id="@+id/appbar"
    android:layout_width="match_parent"
    android:layout_height="@dimen/detail_backdrop_height"
    android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
    android:fitsSystemWindows="true">

当我将layout_height的AppBarLayout设置为wrap_content时,因为标题中的self intro部分可能是很多单词而应该是wrap_content以确保每个句子都可以显示在屏幕上。

我想知道为什么这个额外的填充出现了?还有,我发现这个额外填充的高度似乎与statusBar的高度相同。它看起来像是与fitsSystemWindow属性相关的内容。如果有人能告诉我如何去除这个恼人的底部填充物,那将是很好的。

PS: 我也为这个区域尝试了一些其他ViewGroup布局,例如LinearLayoutFramelayout,它们都是一样的,所以我很确定它与我使用的ConstraintLayout无关。 / p>

8 个答案:

答案 0 :(得分:6)

此问题可能由此commit

引起

我用@Bogdan Zurac的评论方式以编程方式修复了它。

mCollapseContentLy = findViewById(R.id.ly_collapse_content);
mCollapseContentLy.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        int measuredHeight = mCollapseContentLy.getMeasuredHeight();
        ViewGroup.LayoutParams lp = mCollapsingToolbarLayout.getLayoutParams();
        layoutParams.height = measuredHeight;
        mCollapsingToolbarLayout.setLayoutParams(lp);
        mCollapseContentLy.getViewTreeObserver().removeGlobalOnLayoutListener(this);
    }
});

接受后编辑

创建一个新的类扩展CollapsingToolbarLayout并覆盖onMeasure方法

package android.support.design.widget;

import android.content.Context;
import android.util.AttributeSet;

public class GodCollapsingToolbarLayout extends CollapsingToolbarLayout {

    public GodCollapsingToolbarLayout(Context context) {
        this(context, null);
    }

    public GodCollapsingToolbarLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public GodCollapsingToolbarLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        final int mode = MeasureSpec.getMode(heightMeasureSpec);
        final int topInset = mLastInsets != null ? mLastInsets.getSystemWindowInsetTop() : 0;
        if (mode == MeasureSpec.UNSPECIFIED && topInset > 0) {
            // fix the bottom empty padding
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(
                    getMeasuredHeight() - topInset, MeasureSpec.EXACTLY);
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }
}

答案 1 :(得分:2)

我最近遇到了类似的问题,这是由于插件,因为在状态栏后面绘制了折叠工具栏,它提供了状态栏的额外高度,为了避免这个问题,我找到了更好的解决方案,而不是计算折叠工具栏的高度,让工具栏使用插图

ViewCompat.setOnApplyWindowInsetsListener(appBarLayout) { v, insets ->
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            toolBar.setPadding(0, insets.systemWindowInsetTop, 0, 0)
        }
        insets.consumeSystemWindowInsets()
    }

这件事有效,而不是计算高度 我建议通过以下链接

Ian Lakes article

Chris Banes talk

试一试

答案 2 :(得分:1)

这在技术上与上面 user3193413 的 answer 相同,但具有未弃用的调用:

ViewCompat.setOnApplyWindowInsetsListener(app_bar_layout) { _, insets ->
    val topInset = insets.getInsets(WindowInsetsCompat.Type.systemBars()).top
    (toolbar as ViewGroup.MarginLayoutParams).topMargin = topInset
    WindowInsetsCompat.CONSUMED
        }

答案 3 :(得分:0)

看起来你的ConstraintLayout中有一个错误的<View>

这一个:

<View
    android:layout_width="match_parent"
    android:layout_height="@dimen/vertical_divider_height"
    android:background="@color/divider_background_color"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent" />

如果需要,您可以删除该视图 - 看起来它只提供填充或充当分隔符。

此外,您不需要为嵌套视图指定android:fitsSystemWindows="true"。仅在顶级视图中应用该属性,它将正常工作。

我在处理布局时经常使用的一些提示是:

1)在您开发过程中,我发现使您的视图/布局颜色变暗是有帮助的;像粉红色,石灰绿色,霓虹紫色等......这样,你可以看到你的布局明显的问题,并迅速识别出有问题的作品。在它看起来很好的位置后,你可以将颜色恢复正常。

2)通过enabling on-device options through Developer Mode.启用“show layout bounds”这样,您可以在设计XML布局时更轻松地查看布局的填充和边距。

希望有所帮助!

答案 4 :(得分:0)

您是否尝试在dev选项中打开布局边界并查看此填充背后的视图,或使用布局检查器?

答案 5 :(得分:0)

创建一个扩展CollapsingToolbarLayout的新类并覆盖onMeasure方法

public class FixCollapsingToolbarLayout extends CollapsingToolbarLayout {


    public FixCollapsingToolbarLayout(Context context) {
        super(context);
    }

    public FixCollapsingToolbarLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public FixCollapsingToolbarLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        try {
            Field fs = this.getClass().getSuperclass().getDeclaredField("mLastInsets");
            fs.setAccessible(true);
            WindowInsetsCompat mLastInsets = (WindowInsetsCompat) fs.get(this);
            final int mode = MeasureSpec.getMode(heightMeasureSpec);
            int topInset = mLastInsets != null ? mLastInsets.getSystemWindowInsetTop() : 0;
            if (mode == MeasureSpec.UNSPECIFIED && topInset > 0) {
                // fix the bottom empty padding
                heightMeasureSpec = MeasureSpec.makeMeasureSpec(
                        getMeasuredHeight() - topInset, MeasureSpec.EXACTLY);
                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            }
        } catch (Exception e) {
            LogUtils.e(e);
        }
    }
}

答案 6 :(得分:0)

根据@Akshay回答,这是我的解决方案: 在xml文件中,添加android:fitsSystemWindows =“ true” 至: 1. CoordinatorLayout 2. AppBarLayout 3. ImageView(或要在状态栏后面看到的其他视图。在我的情况下,这是ConstraintLayout)

在“活动”中添加@Akshay答案,但如下进行编辑(用Kotlin编写):

 ViewCompat.setOnApplyWindowInsetsListener(appBarLayout) { _, insets ->
        // Instead of
        // toolbar.setPadding(0, insets.systemWindowInsetTop, 0, 0)
        (toolbar.layoutParams as ViewGroup.MarginLayoutParams).topMargin = insets.systemWindowInsetTop
        insets.consumeSystemWindowInsets()
    }

现在工具栏也将具有适当的高度。

答案 7 :(得分:0)

似乎接受的答案不再起作用。 您可以通过将mLastInsetslastInsets交换来解决此问题。但是,这是一个私有字段,需要通过反射使其可访问。 这是一个Kotlin示例:

package xxx

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;

import androidx.core.view.WindowInsetsCompat;

import com.google.android.material.appbar.CollapsingToolbarLayout;

import java.lang.reflect.Field;
import java.util.Objects;

public class FixCollapsingToolbarLayout extends CollapsingToolbarLayout {


    public FixCollapsingToolbarLayout(Context context) {
        super(context);
    }

    public FixCollapsingToolbarLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public FixCollapsingToolbarLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        try {
            Field fs = Objects.requireNonNull(this.getClass().getSuperclass()).getDeclaredField("lastInsets");
            fs.setAccessible(true);
            WindowInsetsCompat mLastInsets = (WindowInsetsCompat) fs.get(this);
            final int mode = MeasureSpec.getMode(heightMeasureSpec);
            int topInset = mLastInsets != null ? mLastInsets.getSystemWindowInsetTop() : 0;
            if (mode == MeasureSpec.UNSPECIFIED && topInset > 0) {
                // fix the bottom empty padding
                heightMeasureSpec = MeasureSpec.makeMeasureSpec(
                        getMeasuredHeight() - topInset, MeasureSpec.EXACTLY);
                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            }
        } catch (Exception e) {
            Log.e("Toolbar", "FixCollapsingToolbarLayout Error", e);
        }
    }
}