I'm not sure how to ask this question. It seems easy to do but I'm not sure why it's not working. Anyways, here goes. I have this Error Banner that should be visible only if the total "Weight" for the Categories is less than or greater than 100. However my issue is that when the error banner is visible, my RecyclerView's last item is cut off, I think by the height of the error banner. It will only be fixed if I edit it once again and the keyboard is shown, the recyclerview's height is refreshed (probably).
Here's my Layout XML:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".presentation.screens.gradesetup.gradingcategories.GradingCategoriesFragment">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/layoutParent"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/layoutGradeCategoryList"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintTop_toTopOf="parent"
app:layout_constrainedHeight="true" >
<TextView
android:id="@+id/tvInvalidGradeWeightLayout"
style="@style/Default_Text_Error_Banner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/default_margin_size_16dp"
android:text="@string/error_invalid_grade_category_weight"
android:visibility="gone" />
<!-- Constraint layout_constraintHeight_max dynamically to 24% -->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvGradingCategories"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tvInfoNoData"
style="@style/Default_Text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/text_no_data_display"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:constraint_referenced_ids="layoutGradeCategoryList" />
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/layout_adjust_weight"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/default_margin_size_16dp"
android:orientation="horizontal"
app:layout_constraintBottom_toTopOf="@+id/buttonSave"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/barrier"
app:layout_constraintVertical_bias="0.01">
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/switchViewAdjustWeight"
style="@style/Default_Text_Bold"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginEnd="@dimen/screen_padding_size"
android:text="@string/text_adjust_weight"
android:theme="@style/Theme.LMS"
android:visibility="gone" />
</androidx.appcompat.widget.LinearLayoutCompat>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/default_margin_size_30dp"
android:src="@drawable/ic_plus"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:tint="@color/white" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/buttonSave"
style="@style/Primary_Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/default_margin_size_30dp"
android:enabled="false"
android:text="@string/text_save"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
As you can see I also have this Constraint Barrier, so whenever the height of the RecyclerView is longer, the SwitchMaterial("switchViewAdjustWeight") is also adjusted. I set the android:layout_constraintHeight_max dynamically based on device height.
// Dynamically set constraintMaxHeight
var constraintMaxHeight = 0
constraintMaxHeight =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val windowMetrics = activity!!.windowManager.currentWindowMetrics
val insets: Insets = windowMetrics.windowInsets
.getInsetsIgnoringVisibility(WindowInsets.Type.systemBars())
val height = windowMetrics.bounds.height() - insets.top - insets.bottom
(height * 0.7).toInt()
} else {
val displayMetrics = DisplayMetrics()
activity!!.windowManager.defaultDisplay.getMetrics(displayMetrics)
(displayMetrics.heightPixels * 0.7).toInt()
}
val constraintSet = ConstraintSet()
constraintSet.clone(layoutParent)
constraintSet.constrainMaxHeight(R.id.layoutGradeCategoryList, constraintMaxHeight)
constraintSet.applyTo(layoutParent)
In my code (kotlin): the error banner is toggled to visible if sum != 100
val result = sum == 100
binding.apply {
buttonSave.isEnabled = result
tvInvalidGradeWeightLayout.isVisible = !result
}
Target UI:
I also did change LinearLayout to ConstraintLayout However it didn't work, the Banner overlaps with the RecyclerView list :
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/layoutParent"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/layoutGradeCategoryList"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintTop_toTopOf="parent"
app:layout_constrainedHeight="true" >
<TextView
android:id="@+id/tvInvalidGradeWeightLayout"
style="@style/Default_Text_Error_Banner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/default_margin_size_16dp"
android:text="@string/error_invalid_grade_category_weight"
android:visibility="gone"
app:layout_constraintTop_toTopOf="parent"/>
<!-- Constraint layout_constraintHeight_max dynamically to 24% -->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvGradingCategories"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constrainedHeight="true"
app:layout_constraintTop_toBottomOf="@+id/tvInvalidGradeWeightLayout" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tvInfoNoData"
style="@style/Default_Text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/text_no_data_display"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:constraint_referenced_ids="layoutGradeCategoryList" />
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/layout_adjust_weight"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/default_margin_size_16dp"
android:orientation="horizontal"
app:layout_constraintBottom_toTopOf="@+id/buttonSave"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/barrier"
app:layout_constraintVertical_bias="0.01">
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/switchViewAdjustWeight"
style="@style/Default_Text_Bold"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginEnd="@dimen/screen_padding_size"
android:text="@string/text_adjust_weight"
android:theme="@style/Theme.LMS"
android:visibility="gone" />
</androidx.appcompat.widget.LinearLayoutCompat>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/default_margin_size_30dp"
android:src="@drawable/ic_plus"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:tint="@color/white" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/buttonSave"
style="@style/Primary_Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/default_margin_size_30dp"
android:enabled="false"
android:text="@string/text_save"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
This worked for me, I added getViewTreeObserver().addOnGlobalLayoutListener for layoutGradeCategoryList and trigger the request layout there.
layoutGradeCategoryList.getViewTreeObserver().addOnGlobalLayoutListener(OnGlobalLayoutListener {
layoutGradeCategoryList.requestLayout();
})
This helped me understand more on requestLayout/forceLayout/invalidate view. https://stackoverflow.com/a/42430695/3777452
Hope this helps someone else.