Calendar View在Android 8和9中仅在Recyclerview ViewHolder内显示/隐藏的问题

huangapple go评论82阅读模式
英文:

Calendar View show/gone inside Recyclerview ViewHolder Issue on Android 8, 9 only

问题

  1. 我有一个具有2个或更多个视图持有者的 RecyclerView。

  2. 第一个视图持有者只是顶部的横幅。

  3. 第二个视图持有者是用于预订小部件的,其中包括3个下拉菜单/详细菜单,包括 CalendarView,全部位于一个视图持有者中。

  4. 问题是 CalendarView 的高度相当大,当 CalendarView 稍微超出屏幕并且我折叠/隐藏日历时,它会折叠,但仍然占据空间,就像下面的截图一样。但奇怪的是,这只发生在 Android 8 和 9 中。

  5. 初始截图:

    Calendar View在Android 8和9中仅在Recyclerview ViewHolder内显示/隐藏的问题

  6. 问题截图 (折叠 CV 后):

    Calendar View在Android 8和9中仅在Recyclerview ViewHolder内显示/隐藏的问题

  7. 相关代码如下:

    视图持有者项的点击监听器:

     val clickListener = View.OnClickListener {
         // 不相关的代码 //
         for (type in AllOptionsEnum.values()) {
                 // 展开选定的项目并折叠其他项目
             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) {
                 // 隐藏详细视图
                 detailView.setVisible(show = false)
                 checkBox.isChecked = false
             } else {
                 // 显示
                 detailView.setVisible(show = (type.labelViewGroupId == it.id))
                 checkBox.isChecked = show = (type.labelViewGroupId == it.id)
    
             }
    
         // 不相关的代码 //
         }
     }
    

    编辑 1:

     扩展函数:
    
     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
     }
    

    编辑 2:

    我尝试了另一种解决方案,即完全删除 RecyclerView 并使用嵌套滚动视图重新构建整个屏幕,但效果也相同。

    编辑 3:

    我也尝试了以下代码,但结果相同

     // 显示详细视图
     if(type==Enums.StayWithUsWidgetType.TYPE_CALENDAR){
         // 显示详细视图及其子视图
         this.calendarDetailView.children.forEach {
             it.visible()
         }
     }
    
     // 隐藏详细视图及其子视图
     if(type==Enums.StayWithUsWidgetType.TYPE_CALENDAR){
         // 隐藏所有
         this.calendarDetailView.children.forEach {
             it.gone()
         }
     }
    
英文:

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 &amp; 9.

Initial Screenshot:

Calendar View在Android 8和9中仅在Recyclerview ViewHolder内显示/隐藏的问题

Issue Screenshot (After Collapsing CV):

Calendar View在Android 8和9中仅在Recyclerview ViewHolder内显示/隐藏的问题

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&lt;ViewGroup&gt;(type.detailViewGroupId)
            val labelView = this.root.findViewById&lt;TextView&gt;(type.labelViewId)
            val checkBox = this.root.findViewById&lt;CheckBox&gt;(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()
                    }
                }

答案1

得分: 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 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
}
英文:

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=&quot;Gone&quot;
>
> 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:

&lt;LinearLayout
    android:id=&quot;@+id/calendarViewContainer&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;wrap_content&quot;&gt;

    &lt;CalendarView
        android:id=&quot;@+id/calendarView&quot;
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;wrap_content&quot; /&gt;
&lt;/LinearLayout&gt;

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

val detailView = this.root.findViewById&lt;ViewGroup&gt;(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:

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

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
}

答案2

得分: 0

你可以尝试通过以下方式以编程方式设置日历视图的高度,这可以帮助您显示该视图的额外空间。

if (type == Enums.StayWithUsWidgetType.TYPE_CALENDAR) {
    // 需要时设置高度
    this.calendarDetailView.children.forEach {
        calendarDetailView.setLayoutParams(new LayoutParams(MATCH_PARENT, WRAP_CONTENT));
    }
}
英文:

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

huangapple
  • 本文由 发表于 2023年7月28日 05:11:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/76783434.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定