xamarin / AndroidX

AndroidX bindings for .NET for Android
MIT License
173 stars 42 forks source link

"Specified cast is not valid" when using SetOnItemSelectedListener of NavigationBarView #884

Closed genis-bosch-ypsomed closed 1 month ago

genis-bosch-ypsomed commented 1 month ago

Android application type

Android for .NET (net6.0-android, etc.)

Affected platform version

.NET 6.0.100

Description

When setting an OnItemSelectedListener for a NavigationBarView, the execution fails with the following error:

System.InvalidCastException: Specified cast is not valid.
   at Google.Android.Material.Navigation.NavigationBarView.SetOnItemSelectedListener(IOnItemSelectedListener listener) in C:\a\_work\1\s\generated\com.google.android.material.material\obj\Release\net6.0-android\generated\src\Google.Android.Material.Navigation.NavigationBarView.cs:line 1916
   at Droid.Components.CoreFeatures.BaseNavigation.AppPresenter.SetBottomNavigationViewAsync(MvxTabItemPresentationAttribute attribute) in Droid/Components/CoreFeatures/BaseNavigation/AppPresenter.cs:line 330
   at Droid.Components.CoreFeatures.BaseNavigation.AppPresenter.AddToBottomNavigationOrShowFragment(Type viewType, IMvxPresentationAttribute attribute, MvxViewModelRequest request) in .Droid/Components/CoreFeatures/BaseNavigation/AppPresenter.cs:line 415
   at MvvmCross.Base.MvxMainThreadAsyncDispatcher.<>c__DisplayClass1_0.<<ExecuteOnMainThreadAsync>b__0>d.MoveNext() in /_/MvvmCross/Base/MvxMainThreadAsyncDispatcher.cs:line 34
--- End of stack trace from previous location ---
   at System.Threading.Tasks.Task.<>c.<ThrowAsync>b__128_0(Object state)
   at Android.App.SyncContext.<>c__DisplayClass2_0.<Post>b__0()
   at Java.Lang.Thread.RunnableImplementor.Run()
   at Java.Lang.IRunnableInvoker.n_Run(IntPtr jnienv, IntPtr native__this)
   at Android.Runtime.JNINativeWrapper.Wrap_JniMarshal_PP_V(_JniMarshal_PP_V callback, IntPtr jnienv, IntPtr klazz)

The same method worked for .NET Standard 2.1.

Steps to Reproduce

1 - Create a NavigationBarView, with .NET 6 and Xamarin.Google.Android.Material 1.10.0.3. 2 - Create an implementation of NavigationBarView.IOnItemSelectedListener. 3 - Set the OnItemSelectedListener by calling the _navigationBarView.SetOnItemSelectedListener(...)

Did you find any workaround?

No

Relevant log output

System.InvalidCastException: Specified cast is not valid.
   at Google.Android.Material.Navigation.NavigationBarView.SetOnItemSelectedListener(IOnItemSelectedListener listener) in C:\a\_work\1\s\generated\com.google.android.material.material\obj\Release\net6.0-android\generated\src\Google.Android.Material.Navigation.NavigationBarView.cs:line 1916
   at Droid.Components.CoreFeatures.BaseNavigation.AppPresenter.SetBottomNavigationViewAsync(MvxTabItemPresentationAttribute attribute) in Droid/Components/CoreFeatures/BaseNavigation/AppPresenter.cs:line 330
   at Droid.Components.CoreFeatures.BaseNavigation.AppPresenter.AddToBottomNavigationOrShowFragment(Type viewType, IMvxPresentationAttribute attribute, MvxViewModelRequest request) in .Droid/Components/CoreFeatures/BaseNavigation/AppPresenter.cs:line 415
   at MvvmCross.Base.MvxMainThreadAsyncDispatcher.<>c__DisplayClass1_0.<<ExecuteOnMainThreadAsync>b__0>d.MoveNext() in /_/MvvmCross/Base/MvxMainThreadAsyncDispatcher.cs:line 34
--- End of stack trace from previous location ---
   at System.Threading.Tasks.Task.<>c.<ThrowAsync>b__128_0(Object state)
   at Android.App.SyncContext.<>c__DisplayClass2_0.<Post>b__0()
   at Java.Lang.Thread.RunnableImplementor.Run()
   at Java.Lang.IRunnableInvoker.n_Run(IntPtr jnienv, IntPtr native__this)
   at Android.Runtime.JNINativeWrapper.Wrap_JniMarshal_PP_V(_JniMarshal_PP_V callback, IntPtr jnienv, IntPtr klazz)
jpobst commented 1 month ago

This looks like the line that is causing the InvalidCastException:

__args [0] = new JniArgumentValue ((listener == null) ? IntPtr.Zero : ((global::Java.Lang.Object) listener).Handle);

My guess is your implementation of NavigationBarView.IOnItemSelectedListener does not inherit from Java.Lang.Object, which is required.

gmck commented 1 month ago

@genis-bosch-ypsomed

The following is a cut-down version that I’ve been using with X.A, Net7 and Net8. See any of the NavigationGraph examples at https://github.com/gmck for more details. I'm using Xamarin.Google.Android.Material 1.10.0.3.

I'm pretty sure it changed from the original implementation a couple of years back, but I now can't remember what the old way was.

NavigationBarView.IOnItemSelectedListener

bottomNavigationView!.ItemSelected += BottomNavigationView_ItemSelected!;

private void BottomNavigationView_ItemSelected(object sender, NavigationBarView.ItemSelectedEventArgs e) { … ... navController!.Navigate(e.Item.ItemId, null, navOptions); }

genis-bosch-ypsomed commented 1 month ago

Thank you for your helpful answers. From what I see, we weren't using the Java.Lang.Object which should be the issue. I will try this one and come back for feedback.

gmck commented 1 month ago

@genis-bosch-ypsomed

What do you get when you right-click NavigationView.IOnNavigationItemSelectedListener and then go to Definition

Mine has JavaObject.

public interface IOnNavigationItemSelectedListener : IJavaObject, IDisposable, IJavaPeerable
{
    [Register("onNavigationItemSelected", "(Landroid/view/MenuItem;)Z", "GetOnNavigationItemSelected_Landroid_view_MenuItem_Handler:Google.Android.Material.Navigation.NavigationView/IOnNavigationItemSelectedListenerInvoker, Xamarin.Google.Android.Material")]
    bool OnNavigationItemSelected(IMenuItem menuItem);
}

Does your main activity inherit from AppCompatActivity?

gmck commented 1 month ago

@genis-bosch-ypsomed

I had a bit of a brain fade last night when I posted the above. The above should have been about NavigationBarView.IOnItemSelectedListener, not NavigationView.

It should have read right-click on NavigationBarView.IOnItemSelectedListener

[Register("com/google/android/material/navigation/NavigationBarView$OnItemSelectedListener", "", "Google.Android.Material.Navigation.NavigationBarView/IOnItemSelectedListenerInvoker")]
public interface IOnItemSelectedListener : IJavaObject, IDisposable, IJavaPeerable
{
    [Register("onNavigationItemSelected", "(Landroid/view/MenuItem;)Z", "GetOnNavigationItemSelected_Landroid_view_MenuItem_Handler:Google.Android.Material.Navigation.NavigationBarView/IOnItemSelectedListenerInvoker, Xamarin.Google.Android.Material")]
    bool OnNavigationItemSelected(IMenuItem p0);
}

If you look at the IOnItemSelectedListenerInvoker below that, you'll see it inherits from Java.Lang.Object. If I remember correctly the change I referred to was introduced in Material 1.4.0

genis-bosch-ypsomed commented 1 month ago

Thanks @gmck and @jpobst, both solutions worked. We ended using the ItemSelected event as it allows us to directly work on the handler without the need of a new class. I will close this issue.