Bug 60052 - RecyclerView.NestedScrollingEnabled calls parent View class instead of RecyclerView implementation because it's missing setNestedScrollingEnabled bindings.
Summary: RecyclerView.NestedScrollingEnabled calls parent View class instead of Recycl...
Status: CONFIRMED
Alias: None
Product: Components
Classification: Xamarin
Component: Xamarin Components ()
Version: Production (addons.xamarin.com)
Hardware: PC Mac OS
: High critical
Target Milestone: ---
Assignee: Jon Dick
URL:
Depends on:
Blocks:
 
Reported: 2017-10-09 09:51 UTC by jzeferino
Modified: 2018-01-26 16:00 UTC (History)
5 users (show)

Tags:
Is this bug a regression?: ---
Last known good build:


Attachments
Project to replicate (358.03 KB, application/zip)
2017-10-09 09:51 UTC, jzeferino
Details
The working sample in API 23 (398.49 KB, application/zip)
2017-10-09 20:54 UTC, jzeferino
Details


Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report for Bug 60052 on Developer Community or GitHub if you have new information to add and do not yet see a matching new report.

If the latest results still closely match this report, you can use the original description:

  • Export the original title and description: Developer Community HTML or GitHub Markdown
  • Copy the title and description into the new report. Adjust them to be up-to-date if needed.
  • Add your new information.

In special cases on GitHub you might also want the comments: GitHub Markdown with public comments

Related Links:
Status:
CONFIRMED

Description jzeferino 2017-10-09 09:51:58 UTC
Created attachment 25216 [details]
Project to replicate

I have a project that was targeting android API 23, and updated it recently to android API 25.

When setting the property recyclerView.NestedScrollingEnabled this exception is thrown when running the application in API 16, 19 (At least I've tested in this two). It don't happen when running in API 23, or 25 (i've tested in this ones).

Its easy to replicate. I've created a Xamarin Native project using Visual Studio for Mac default template and added that line in the BrowseFragment. (see attached source)

This issue have some high priority for the projection in question since the project is already in production and we need to update the android API two 25 since its a client request.

Here is more detail about the exception.

Java.Lang.NoSuchMethodError: no method with name='setNestedScrollingEnabled' signature='(Z)V' in class Landroid/view/View;
  at java.lang.NoSuchMethodError: no method with name='setNestedScrollingEnabled' signature='(Z)V' in class Landroid/view/View;
  at at md5b698fe94198e0054eea3bc7821208de1.BrowseFragment.n_onCreateView(Native Method)
  at at md5b698fe94198e0054eea3bc7821208de1.BrowseFragment.onCreateView(BrowseFragment.java:40)
  at at android.support.v4.app.Fragment.performCreateView(Fragment.java:2248)
  at at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1340)
  at at android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1569)
  at at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1636)
  at at android.support.v4.app.BackStackRecord.executeOps(BackStackRecord.java:758)
  at at android.support.v4.app.FragmentManagerImpl.executeOps(FragmentManager.java:2415)
  at at android.support.v4.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2201)
  at at android.support.v4.app.FragmentManagerImpl.optimizeAndExecuteOps(FragmentManager.java:2155)
  at at android.support.v4.app.FragmentManagerImpl.execSingleAction(FragmentManager.java:2034)
  at at android.support.v4.app.BackStackRecord.commitNowAllowingStateLoss(BackStackRecord.java:626)
  at at android.support.v4.app.FragmentStatePagerAdapter.finishUpdate(FragmentStatePagerAdapter.java:166)
  at at android.support.v4.view.ViewPager.populate(ViewPager.java:1268)
  at at android.support.v4.view.ViewPager.populate(ViewPager.java:1116)
  at at android.support.v4.view.ViewPager.onMeasure(ViewPager.java:1642)
  at at android.view.View.measure(View.java:16497)
  at at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5125)
  at at android.support.design.widget.CoordinatorLayout.onMeasureChild(CoordinatorLayout.java:714)
  at at android.support.design.widget.HeaderScrollingViewBehavior.onMeasureChild(HeaderScrollingViewBehavior.java:90)
  at at android.support.design.widget.AppBarLayout$ScrollingViewBehavior.onMeasureChild(AppBarLayout.java:1397)
  at at android.support.design.widget.CoordinatorLayout.onMeasure(CoordinatorLayout.java:784)
  at at android.view.View.measure(View.java:16497)
  at at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5125)
  at at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
  at at android.support.v7.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:139)
  at at android.view.View.measure(View.java:16497)
  at at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5125)
  at at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
  at at android.view.View.measure(View.java:16497)
  at at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5125)
  at at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
  at at android.view.View.measure(View.java:16497)
  at at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5125)
  at at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1404)
  at at android.widget.LinearLayout.measureVertical(LinearLayout.java:695)
  at at android.widget.LinearLayout.onMeasure(LinearLayout.java:588)
  at at android.view.View.measure(View.java:16497)
  at at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5125)
  at at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
  at at com.android.internal.policy.impl.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:2291)
  at at android.view.View.measure(View.java:16497)
  at at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:1916)
  at at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1113)
  at at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1295)
  at at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1000)
  at at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5670)
  at at android.view.Choreographer$CallbackRecord.run(Choreographer.java:761)
  at at android.view.Choreographer.doCallbacks(Choreographer.java:574)
  at at android.view.Choreographer.doFrame(Choreographer.java:544)
  at at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:747)
  at at android.os.Handler.handleCallback(Handler.java:733)
  at at android.os.Handler.dispatchMessage(Handler.java:95)
  at at android.os.Looper.loop(Looper.java:136)
  at at android.app.ActivityThread.main(ActivityThread.java:5017)
  at at java.lang.reflect.Method.invokeNative(Native Method)
  at at java.lang.reflect.Method.invoke(Method.java:515)
  at at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
  at at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
  at at dalvik.system.NativeStart.main(Native Method)


Here is my VS for mac information:

=== Visual Studio Community 2017 for Mac ===

Version 7.1.5 (build 2)
Runtime:
	Mono 5.2.0.224 (d15-3/14f2c81) (64-bit)
	GTK+ 2.24.23 (Raleigh theme)

=== Xamarin.Android ===

Version: 7.4.5.1 (Visual Studio Community)
Android SDK: /Users/zeferino/Library/Developer/Xamarin/android-sdk-macosx
	Supported Android versions:
		4.0.3 (API level 15)
		4.2   (API level 17)
		4.4   (API level 19)
		5.1   (API level 22)
		6.0   (API level 23)
		7.0   (API level 24)
		7.1   (API level 25)

SDK Tools Version: 26.0.2
SDK Platform Tools Version: 26.0.0
SDK Build Tools Version: 26.0.1

Java SDK: /usr
java version "1.8.0_121"
Java(TM) SE Runtime Environment (build 1.8.0_121-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)

Android Designer EPL code available here:
https://github.com/xamarin/AndroidDesigner.EPL

=== Operating System ===

Mac OS X 10.12.6
Darwin 16.7.0 Darwin Kernel Version 16.7.0
    Thu Jun 15 17:36:27 PDT 2017
    root:xnu-3789.70.16~2/RELEASE_X86_64 x86_64
Comment 1 Jon Douglas [MSFT] 2017-10-09 15:10:06 UTC
The problem is that this API was introduced in API 21 as per the documentation:

https://developer.android.com/reference/android/view/View.html#setNestedScrollingEnabled(boolean)

So basically this is expected behavior because you are trying to "setNestedScrollingEnabled" on a "View" object when this method does not actually exist on < API 21 platforms. Thus you would need to use a runtime conditional to check what API you are on and set it if it's >= API 21:

https://developer.xamarin.com/guides/android/application_fundamentals/understanding_android_api_levels/#runtimechecks

This is a similar answer that should help you out:

https://stackoverflow.com/a/43450158/1048571
Comment 2 jzeferino 2017-10-09 18:28:34 UTC
Im not calling this in a View.

'Im calling it in a RecyclerView witch comes from Android.Support.V7.
Comment 3 Jon Douglas [MSFT] 2017-10-09 18:33:31 UTC
(In reply to jzeferino from comment #2)
> Im not calling this in a View.
> 
> 'Im calling it in a RecyclerView witch comes from Android.Support.V7.

RecyclerView is a View and thus calls the View method.

java.lang.Object
   ↳	android.view.View
 	   ↳	android.view.ViewGroup
 	 	   ↳	android.support.v7.widget.RecyclerView
Comment 4 jzeferino 2017-10-09 20:53:46 UTC
Yes, RecyclerView extends View.

But as you can see from the source code, RecyclerView has its own implementation of setNestedScrollingEnabled:


@Override
public void setNestedScrollingEnabled(boolean enabled) {
    getScrollingChildHelper().setNestedScrollingEnabled(enabled);
}

and the getScrollingChildHelper().setNestedScrollingEnabled(enabled):

public void setNestedScrollingEnabled(boolean enabled) {
        if (mIsNestedScrollingEnabled) {
            ViewCompat.stopNestedScroll(mView);
        }
        mIsNestedScrollingEnabled = enabled;
    }

This methods uses the View (mView) to call the stopNestedScroll but its wrapped in a ViewCompat call witch means that I don't need to care if its < API 21. ViewCompat deals with it. (At least its supposed to)

------------------------ Important note ------------------------

As you can see in the exception:  Java.Lang.NoSuchMethodError: no method with name='setNestedScrollingEnabled'

Its says that setNestedScrollingEnabled don't exist. But it exists in the RecyclerView it overrides it.

------------------------ Important note 2 ------------------------

This exact same code was working (in API 16,19 and 23,22,25) before I bumped to API 25.
This exact same code was working in API 23 and support 23.4.0.1.

I attached another sample project targeting API 23 with the same code as the other sample that proves it.
Comment 5 jzeferino 2017-10-09 20:54:47 UTC
Created attachment 25231 [details]
The working sample in API 23
Comment 6 Jon Douglas [MSFT] 2017-10-09 21:31:00 UTC
Hi jzeferino,

Thanks for your notes. I can CONFIRM this is a binding issue.

I believe this is a "RecyclerView v7 binding" bug in this case. Here is the difference between your projects:

https://i.imgur.com/HlP6dVm.png

Notice it is calling the "View" method. This is wrong. It needs to call the RecyclerView setNestedScrollingEnabled item instead. My initial comments reflect the view observation. However this observation is incorrect as RecyclerView uses ViewCompat internally with it's override method.

Here is the correct working behavior:

https://i.imgur.com/L9ceGAS.png

Assigning this to our components team as the v25/v26 versions of this NuGet are missing bindings for RecyclerView methods that are included in the v23 versions of the NuGet. Also changing the title for more clarity. REOPENING this bug.

Here's v23 including this binding:

      [Register("setNestedScrollingEnabled", "(Z)V", "GetSetNestedScrollingEnabled_ZHandler")] set
      {
        if (RecyclerView.id_setNestedScrollingEnabled_Z == IntPtr.Zero)
          RecyclerView.id_setNestedScrollingEnabled_Z = JNIEnv.GetMethodID(RecyclerView.class_ref, "setNestedScrollingEnabled", "(Z)V");
        try
        {
          JValue* parms = (JValue*) __untypedstackalloc((int) checked (1U * unchecked ((uint) sizeof (JValue))));
          *parms = new JValue(value);
          if (this.GetType() == this.ThresholdType)
            JNIEnv.CallVoidMethod(this.Handle, RecyclerView.id_setNestedScrollingEnabled_Z, parms);
          else
            JNIEnv.CallNonvirtualVoidMethod(this.Handle, this.ThresholdClass, JNIEnv.GetMethodID(this.ThresholdClass, "setNestedScrollingEnabled", "(Z)V"), parms);
        }
        finally
        {
        }
      }

However v25 and v26 are missing this binding and perhaps even more.
Comment 7 Jon Douglas [MSFT] 2017-10-09 21:31:50 UTC
CONFIRMING this issue after a thorough investigation. You can see missing bindings in v25/v26 of https://www.nuget.org/packages/Xamarin.Android.Support.v7.RecyclerView/ whereas previous versions include proper bindings.
Comment 8 jzeferino 2017-10-10 09:06:56 UTC
Good, thats what I was expecting.

@Jon, when this is fixed, should I expect to have the fix in the 25.4.0.3 support version, or in any other version?

Any prevision for the release date?
Comment 9 Jon Douglas [MSFT] 2017-10-10 15:36:10 UTC
(In reply to jzeferino from comment #8)
> Good, thats what I was expecting.
> 
> @Jon, when this is fixed, should I expect to have the fix in the 25.4.0.3
> support version, or in any other version?
> 
> Any prevision for the release date?

You would most likely expect it to be fixed in the latest version of support libraries i.e v26.X.X. However due to the nature of this bug, it might be worth republishing to v25.X.X. I do not at this time have any idea how long it will take to fix.
Comment 10 jzeferino 2017-10-10 15:45:10 UTC
@Jon, I really need this fix to be in 25.X.X, but in terms of nuget I don't know if it will be possible to release a 25.X.X because since a 26.X.Xbeta was already published.

Can you talk with the team to do that (possible)?
Comment 11 jzeferino 2017-11-27 18:39:19 UTC
Any update on this?
Comment 12 Jon Dick 2018-01-04 14:41:54 UTC
Ok, we've dug into this further and we now understand exactly _why_ this is happening, unfortunately fixing it beyond this one specific case is a bit more complicated.

Basically, we compile the bindings against API > 21 in this case, at which point View.setNestedScrollingEnabled does actually exist, so we omit generating a c# binding override for this method on our RecyclerView binding.

This in theory is perfectly ok.  When View.SetNestedScrollingEnabled (the C# binding method) gets called at runtime, we lookup the jmethodID, which doesn't exist on API < 21 at runtime and causes a runtime error.  On API > 21, the jmethodID does exist and we can carry on our way with calling InvokeMethod.

The fix for this specific case is to manually add bindings for the method(s) in question on RecyclerView.

While you wait for this fix to be added, you could probably get away with doing something like this:

https://gist.github.com/Redth/fb991f97bb078ac8551f2df2f06d9286

Then you can simply use the RvUtil wrapper to invoke the methods on the recycler view:

```
  var rvUtil = new RvUtil (myRecyclerViewInstance);
  rvUtil.SetNestedScrollingEnabled (false);
```
Comment 13 jzeferino 2018-01-04 15:39:25 UTC
Hi there Jon, thanks for the clear explanation and workaround.

I've noticed that this issue didn't occur when I'm using a class that extends RecyclerView instead of RecyclerView directly. 

That was the workaround I've used until today.
Comment 14 Thomas D 2018-01-05 13:41:51 UTC
I'm having the exact same issue with SwipeRefreshLayout.
Redth's workaround (altered the RvUtil) seems to work for now.
Comment 15 jzeferino 2018-01-26 16:00:36 UTC
@Thomas D what API are you targeting and what is the version of your support libraries?