我正在构建一个应用程序,并且有一个配置文件片段,显示配置文件背景图像和一些其他元素。如下面的截图:
我的问题是:为什么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布局,例如LinearLayout
或Framelayout
,它们都是一样的,所以我很确定它与我使用的ConstraintLayout
无关。 / p>
答案 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()
}
这件事有效,而不是计算高度 我建议通过以下链接
试一试
答案 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)
似乎接受的答案不再起作用。
您可以通过将mLastInsets
与lastInsets
交换来解决此问题。但是,这是一个私有字段,需要通过反射使其可访问。
这是一个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);
}
}
}