[mono-android] FindViewById returning a ViewGroupInvoker

Jonathan Pryor jonp at xamarin.com
Thu Feb 23 02:28:48 UTC 2012


On Feb 22, 2012, at 10:50 AM, Chris Tacke wrote:
> Later on in my app, I'd like to get hold of this MapView so I can zoom in on it, but I'm having difficulty.
> 
> My first attempt returned null:
> 
>            var m = m_layout.FindViewById<MapView>(Resource.Id.DriverMap);
...
> So I tried this:
> var m = m_layout.FindViewById(Resource.Id.DriverMap);

This doesn't make sense to me. :-/

View.FindViewById<T>(int) is (incorrectly!) this:

	public T FindViewById<T> (int id)
	{
		object view = this.FindViewById (id);

		return (T)view;
	}

I have no idea why View.FindViewById<T>(int) would return null while View.FindViewById(int) returns a non-null instance. I'd expect an InvalidCastException, since FindViewById<T>(int) is calling FindViewById(int)...

As I said, that implementation is incorrect. (Thanks for finding the bug! I fixed Activity but forgot about View...) It will be fixed in the 4.2 series so that FindViewById<MapView>() will work as expected. In the mean time...

> I run this:
> 
> var mt = m.GetType().Name;
> 
> And I get back "ViewGroupInvoker"

The problem here is a leaky abstraction: at present, our Java instance wrapper logic only knows about types from android.jar/Mono.Android.dll, and MapView isn't in android.jar, so it's not known. So we check the base type of MapView, find ViewGroup, which we _do_ know about, but it's abstract. "Fortunately" our plumbing generates "Invoker" types for abstract types and interfaces, so we construct a ViewGroupInvoker to hold thew MapView, as a MapView IS-A ViewGroup, and we return the invoker.

This still isn't entirely useful to you. The missing part you want/need is the Extensions.JavaCast<T>() extension method:

	http://androidapi.xamarin.com/?link=M:Android.Runtime.Extensions.JavaCast{TResult}

Extensions.JavaCast<T>() is used to traverse (and type-check) the Java type system when the Mono for Android type system is dealing with incomplete information. You could thus do:

	MapView m = m_layout.FindViewById(Resource.Id.DriverMap).JavaCast<MapView>();

This would return a (far more useful) MapView instance instead of a ViewGroupInvoker instance.

Thanks,
 - Jon



More information about the Monodroid mailing list