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
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!
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);
}
}
}
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);
}
}
}
Based on @Akshay answer this is my solution:
In the xml file add android:fitsSystemWindows="true" to:
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.
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
did you try turning the layout bounds on in dev option and see which view is behind this padding, or use Layout inspector?