flexbox-layout icon indicating copy to clipboard operation
flexbox-layout copied to clipboard

canScrollHorizontally() crashes if the RecyclerView is not attached to the Window

Open martinbonnin opened this issue 6 years ago • 6 comments

We have this use case where we might call recyclerView.smoothScrollToPosition on a view that has been removed from its container. In that case, we get the following crash:

        Fatal Exception: java.lang.NullPointerException: Attempt to invoke virtual method 'int android.view.View.getWidth()' on a null object reference
        at com.google.android.flexbox.FlexboxLayoutManager.canScrollHorizontally(FlexboxLayoutManager.java:1900)
        at com.google.android.flexbox.FlexboxLayoutManager.getChildWidthMeasureSpec(FlexboxLayoutManager.java:483)
        at com.google.android.flexbox.FlexboxHelper.calculateFlexLines(FlexboxHelper.java:458)
        at com.google.android.flexbox.FlexboxHelper.calculateHorizontalFlexLines(FlexboxHelper.java:243)
        at com.google.android.flexbox.FlexboxLayoutManager.updateFlexLines(FlexboxLayoutManager.java:955)
        at com.google.android.flexbox.FlexboxLayoutManager.onLayoutChildren(FlexboxLayoutManager.java:731)
        at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:3924)
        at androidx.recyclerview.widget.RecyclerView.dispatchLayout(RecyclerView.java:3641)
        at androidx.recyclerview.widget.RecyclerView.consumePendingUpdateOperations(RecyclerView.java:1858)
        at androidx.recyclerview.widget.RecyclerView$ViewFlinger.run(RecyclerView.java:5044)

Sample code to reproduce the issue => https://github.com/martinbonnin/FlexboxTest. It mainly boils down to:

        val recyclerView = RecyclerView(this)
        recyclerView.layoutManager =  FlexboxLayoutManager(this)
        recyclerView.adapter = object: RecyclerView.Adapter<RecyclerView.ViewHolder>() {
            [....]
        }
        recyclerView.smoothScrollToPosition(500)

I understand scrolling on a non-visible View is not the most useful thing to do but I would still expect the LayoutManager to handle this case gracefully ?

martinbonnin avatar Dec 06 '18 10:12 martinbonnin

I'm seeing a very similar crash when I try to nest RecyclerViews with FlexboxLayoutManagers. If I use FlexboxLayoutManager for the top-level RecyclerView and LinearLayoutManager for the inner RecyclerViews, everything works fine. But when I change the inner layout manager to be a FlexboxLayoutManager, I get this in the logs:

java.lang.NullPointerException: Attempt to invoke virtual method 'int android.view.View.getHeight()' on a null object reference
	at com.google.android.flexbox.FlexboxLayoutManager.canScrollVertically(FlexboxLayoutManager.java:1903)
	at com.google.android.flexbox.FlexboxLayoutManager.getChildHeightMeasureSpec(FlexboxLayoutManager.java:491)
	at com.google.android.flexbox.FlexboxHelper.calculateFlexLines(FlexboxHelper.java:478)
	at com.google.android.flexbox.FlexboxHelper.calculateVerticalFlexLines(FlexboxHelper.java:318)
	at com.google.android.flexbox.FlexboxLayoutManager.updateFlexLines(FlexboxLayoutManager.java:970)
	at com.google.android.flexbox.FlexboxLayoutManager.onLayoutChildren(FlexboxLayoutManager.java:729)
	at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:3924)
	at androidx.recyclerview.widget.RecyclerView.onMeasure(RecyclerView.java:3336)
	at android.view.View.measure(View.java:18788)
	at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
	at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1465)
	at android.widget.LinearLayout.measureVertical(LinearLayout.java:748)
	at android.widget.LinearLayout.onMeasure(LinearLayout.java:630)
	at android.view.View.measure(View.java:18788)
	at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
	at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
	at androidx.cardview.widget.CardView.onMeasure(CardView.java:260)
	at android.view.View.measure(View.java:18788)
	at com.google.android.flexbox.FlexboxHelper.calculateFlexLines(FlexboxHelper.java:483)
	at com.google.android.flexbox.FlexboxHelper.calculateVerticalFlexLines(FlexboxHelper.java:318)
	at com.google.android.flexbox.FlexboxLayoutManager.updateFlexLines(FlexboxLayoutManager.java:970)
	at com.google.android.flexbox.FlexboxLayoutManager.onLayoutChildren(FlexboxLayoutManager.java:729)
	at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:3924)
	at androidx.recyclerview.widget.RecyclerView.dispatchLayout(RecyclerView.java:3641)
	at androidx.recyclerview.widget.RecyclerView.onLayout(RecyclerView.java:4194)

Also relevant is that the inner RecyclerView is using wrap_content for its dimensions. If I use a constant size for both dimensions, the crash disappears (though I can't scroll the contents of the inner RecyclerViews).

Incidentally, I'm using inner RecyclerView + FlexboxLayoutManager instead of a FlexboxLayout for each of the outer-level items so that I can use a shared RecycledViewPool.

zizibaloob avatar Dec 07 '18 00:12 zizibaloob

Hi @martinbonnin,

Thanks for the report. I checked your repository, but the activity_main only has a top-level ConstraintLayout.

And the app didn't crash when I launched the app. Could you confirm if the app really crashes?

thagikura avatar Dec 07 '18 07:12 thagikura

@thagikura thanks for looking into this. The crash happens on a Nexus 9 tablet with Android 6.0. I tried to reproduce on a Pixel 3 XL but the crash did not happen indeed. So I guess it depends the framework version :-/.

main_activity.xml is really empty on purpose, the trick is to create a RecyclerView programmatically and not attach it to any parent.

martinbonnin avatar Dec 07 '18 13:12 martinbonnin

I'm seeing a very similar crash when I try to nest RecyclerViews with FlexboxLayoutManagers. If I use FlexboxLayoutManager for the top-level RecyclerView and LinearLayoutManager for the inner RecyclerViews, everything works fine. But when I change the inner layout manager to be a FlexboxLayoutManager, I get this in the logs:

java.lang.NullPointerException: Attempt to invoke virtual method 'int android.view.View.getHeight()' on a null object reference
	at com.google.android.flexbox.FlexboxLayoutManager.canScrollVertically(FlexboxLayoutManager.java:1903)
	at com.google.android.flexbox.FlexboxLayoutManager.getChildHeightMeasureSpec(FlexboxLayoutManager.java:491)
	at com.google.android.flexbox.FlexboxHelper.calculateFlexLines(FlexboxHelper.java:478)
	at com.google.android.flexbox.FlexboxHelper.calculateVerticalFlexLines(FlexboxHelper.java:318)
	at com.google.android.flexbox.FlexboxLayoutManager.updateFlexLines(FlexboxLayoutManager.java:970)
	at com.google.android.flexbox.FlexboxLayoutManager.onLayoutChildren(FlexboxLayoutManager.java:729)
	at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:3924)
	at androidx.recyclerview.widget.RecyclerView.onMeasure(RecyclerView.java:3336)
	at android.view.View.measure(View.java:18788)
	at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
	at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1465)
	at android.widget.LinearLayout.measureVertical(LinearLayout.java:748)
	at android.widget.LinearLayout.onMeasure(LinearLayout.java:630)
	at android.view.View.measure(View.java:18788)
	at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
	at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
	at androidx.cardview.widget.CardView.onMeasure(CardView.java:260)
	at android.view.View.measure(View.java:18788)
	at com.google.android.flexbox.FlexboxHelper.calculateFlexLines(FlexboxHelper.java:483)
	at com.google.android.flexbox.FlexboxHelper.calculateVerticalFlexLines(FlexboxHelper.java:318)
	at com.google.android.flexbox.FlexboxLayoutManager.updateFlexLines(FlexboxLayoutManager.java:970)
	at com.google.android.flexbox.FlexboxLayoutManager.onLayoutChildren(FlexboxLayoutManager.java:729)
	at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:3924)
	at androidx.recyclerview.widget.RecyclerView.dispatchLayout(RecyclerView.java:3641)
	at androidx.recyclerview.widget.RecyclerView.onLayout(RecyclerView.java:4194)

Also relevant is that the inner RecyclerView is using wrap_content for its dimensions. If I use a constant size for both dimensions, the crash disappears (though I can't scroll the contents of the inner RecyclerViews).

Incidentally, I'm using inner RecyclerView + FlexboxLayoutManager instead of a FlexboxLayout for each of the outer-level items so that I can use a shared RecycledViewPool.

have you managed to solve this issue? I'm getting the same.

bgorkowy avatar Feb 14 '19 14:02 bgorkowy

@bgorkowy I workaround'd the issue for now

martinbonnin avatar Feb 14 '19 15:02 martinbonnin

A workaround is to clone the library and perform a check on line 1901 @Override public boolean canScrollHorizontally() { if (mFlexWrap == FlexWrap.NOWRAP) { return isMainAxisDirectionHorizontal(); } else { return !isMainAxisDirectionHorizontal() || getWidth() > (mParent != null ? mParent.getWidth() : 0); } } I created a PR for the fix here

KryptKode avatar Apr 19 '19 03:04 KryptKode