8

I have a recyclerview with 2 or more viewholders,

  1. 1st Viewholder is just top heading banner
  2. 2nd vlewholder is for booking widget which has 3 dropdowns/detail menus including CalendarView -- All in one viewhodler.

Issue is CalendarView is quite big in height and when the CalendarView is slightly off the screen and I collapse/gone the calendar it is collapsing but still taking space just like below screenshots. But Strange thing is that its occurring only in Android 8 & 9.

Initial Screenshot:

enter image description here

Issue Screenshot (After Collapsing CV):

enter image description here

Relevant Code is below:

Viewholder Item's ClickListener:

    val clickListener = View.OnClickListener {
        // irrelevant code //
        for (type in AllOptionsEnum.values()) {
                // Expand Selected and Collapse Others
            val detailView = this.root.findViewById<ViewGroup>(type.detailViewGroupId)
            val labelView = this.root.findViewById<TextView>(type.labelViewId)
            val checkBox = this.root.findViewById<CheckBox>(type.checkBoxId)

            if (detailView.visibility == View.VISIBLE) {
                // hide detailed view
                detailView.setVisible(show = false)
                checkBox.isChecked = false
            } else {
                // show
                detailView.setVisible(show = (type.labelViewGroupId == it.id))
                checkBox.isChecked = show = (type.labelViewGroupId == it.id)
                
            }
                
        // irrelevant code //
        }
    }

Edit 1: Extension Func:

    fun View.setVisible(show: Boolean = true, invisible: Boolean = false) {
        if (show) this.visibility = View.VISIBLE
        else this.visibility = if (invisible) View.INVISIBLE else View.GONE
    }

Edit 2:

I have tried another solution i.e. to remove the recyclerview completely and re-constructed the whole screen using nestedscrollview but it also has same effects.

Edit 3:

I have tried below code also but same result

                // show detailView
                if(type==Enums.StayWithUsWidgetType.TYPE_CALENDAR){
                    // show detailview and children
                    this.calendarDetailView.children.forEach {
                        it.visible()
                    }
                }


                // Hide detailView and children 
                if(type==Enums.StayWithUsWidgetType.TYPE_CALENDAR){
                    // hide all
                    this.calendarDetailView.children.forEach {
                        it.gone()
                    }
                }
Nadeem Iqbal
  • 2,357
  • 1
  • 28
  • 43

2 Answers2

3

I am not sure how Android Pie (2018) is handling visibility changes in layouts, particularly with the CalendarView which has a complex layout hierarchy.

But do note that "Android P visibilityawareimagebutton.setVisibility can only be called from the same library group" is clear:

Best practice: android:visibility="Gone"

Using visibility gone means your view is does not take up any space on your layout while "invisible" will take up unnecessary space on your layout

So double-check your detailView.setVisible() function. Ideally, it should look like:

fun View.setVisible(show: Boolean) {
    this.visibility = if (show) View.VISIBLE else View.GONE
}

So, when you see setVisible(show = false), it is equivalent to setting visibility = View.GONE on that view. This is, however, an assumption, as the implementation of this method is not provided in your question.


A possible alternative solution to test would be to wrap the CalendarView in another layout such as LinearLayout or FrameLayout, and then set the visibility of this wrapper layout instead of the CalendarView itself.
That might help ensure the layout is redrawn correctly when visibility changes.

For example:

<LinearLayout
    android:id="@+id/calendarViewContainer"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <CalendarView
        android:id="@+id/calendarView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</LinearLayout>

In your click listener, you would now toggle visibility of calendarViewContainer instead of calendarView:

val detailView = this.root.findViewById<ViewGroup>(if (type == AllOptionsEnum.CALENDAR) R.id.calendarViewContainer else type.detailViewGroupId)

If the problem still persists, another workaround is to use requestLayout() method after changing the visibility to GONE. That will tell the Android system to measure and layout the view hierarchy again, which might fix your issue.

detailView.setVisible(show = false)
detailView.requestLayout()

That is not the most efficient way since it could lead to unnecessary layout passes, but it might be necessary to work around this specific issue on Android Pie (9).


Regarding Edit 3:

// hide detailView
if(type==Enums.StayWithUsWidgetType.TYPE_CALENDAR){
    // hide detailview and children
    this.calendarDetailView.children.forEach {
        it.visible()
    }
}
// Hide detailView and children 
if(type==Enums.StayWithUsWidgetType.TYPE_CALENDAR){
    // hide all
    this.calendarDetailView.children.forEach {
        it.gone()
    }
}

I see you are attempting to hide each child of the calendarDetailView (which I assume is the ViewGroup that contains your CalendarView) individually. However, it is still not clear if you are setting the visibility of the calendarDetailView itself to GONE.

I would prefer to see this code, where the calendarDetailView itself is also set to GONE:

if(type == Enums.StayWithUsWidgetType.TYPE_CALENDAR){
    // hide all children
    this.calendarDetailView.children.forEach {
        it.visibility = View.GONE
    }

    // then hide the parent itself
    this.calendarDetailView.visibility = View.GONE
}
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
0

You can try with setting the Calenderview height programatically like below that can help you in showing extra space of that view.


if(type==Enums.StayWithUsWidgetType.TYPE_CALENDAR){
                    // setup height when needed
                    this.calendarDetailView.children.forEach {
                        calendarDetailView.setLayoutParams(new LayoutParams(MATCH_PARENT, WRAP_CONTENT));
                    }
                }