ViewGroup inside CollapsingToolbarLayout show extra bottom padding when set fitsSystemWindows to be true

前端 未结 7 2334
有刺的猬
有刺的猬 2021-02-08 15:05

I am building an application and there is a profile fragment which shows a profile background image and some other elements. Like the screenshot below:

My quest

相关标签:
7条回答
  • 2021-02-08 15:44

    It looks like there is an erroneous <View> inside your ConstraintLayout.

    This one:

    <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" />
    

    You can remove that view if you want - it looks like it is only supplying padding or acting as a divider.

    Also, you don't need to specify android:fitsSystemWindows="true" for your nested views. Apply that attribute at the top-level view only and it will work fine.

    A couple of tips that I use regularly when working with layouts are:

    1) While you are developing, I find it helpful to make your views/layouts obscene colors; like hot pink, lime green, neon purple, etc... This way, you can see obvious issues with your layout and quickly identify the offending piece. After it looks nice positionally, then you can turn the colors back to normal.

    2) Turn on "show layout bounds" by enabling on-device options through Developer Mode. This way, you can more easily see layouts' padding and margins while designing your XML layouts.

    Hope that helps!

    0 讨论(0)
  • 2021-02-08 15:57

    create a new class extend CollapsingToolbarLayout and override the onMeasure method

    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);
            }
        }
    }
    
    0 讨论(0)
  • It seems that the accepted answer is not working anymore. You can fix it by exchanging mLastInsets with lastInsets. However it is a private field and needs to be made accessible with reflection. Here is a Kotlin example:

    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);
            }
        }
    }
    
    0 讨论(0)
  • 2021-02-08 15:59

    Based on @Akshay answer this is my solution:

    In the xml file add android:fitsSystemWindows="true" to:

    1. CoordinatorLayout
    2. AppBarLayout
    3. ImageView (or other view you want to see behind the status bar. In my case it was a ConstraintLayout)

    In the Activity add @Akshay answer but edit it as follow (written in Kotlin):

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

    Now the toolbar will have the proper height as well.

    0 讨论(0)
  • 2021-02-08 15:59

    I recently faced a similar issue, this was due to the insets,as the collapsing toolbar is drawn behind the statusbar , it provides the additional height of the status bar, in order to avoid this issue, I found a better solution , instead of a calculating a height of collapse toolbar, let the toolbar consume the insets

    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()
        }
    

    This thing worked , instead of calculating the height I would recommend to go through the following links

    Ian Lakes article

    Chris Banes talk

    Give it a try

    0 讨论(0)
  • 2021-02-08 15:59

    did you try turning the layout bounds on in dev option and see which view is behind this padding, or use Layout inspector?

    0 讨论(0)
提交回复
热议问题