Bug 30764 - LayoutInflater.Factory and Factory2 setters should be virtual
Summary: LayoutInflater.Factory and Factory2 setters should be virtual
Status: ASSIGNED
Alias: None
Product: Android
Classification: Xamarin
Component: BCL Class Libraries ()
Version: 5.2
Hardware: PC Windows
: High normal
Target Milestone: ---
Assignee: dean.ellis
URL:
Depends on:
Blocks:
 
Reported: 2015-06-04 00:13 UTC by Jeremy Kolb
Modified: 2016-12-12 05:05 UTC (History)
4 users (show)

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

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 30764 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:
ASSIGNED

Description Jeremy Kolb 2015-06-04 00:13:39 UTC
I'm trying to implement my own LayoutInflater and I can't override setFactory and setFactory2.  They are supposed to be virtual:

    public void setFactory(Factory factory) {
        if (mFactorySet) {
            throw new IllegalStateException("A factory has already been set on this LayoutInflater");
        }
        if (factory == null) {
            throw new NullPointerException("Given factory can not be null");
        }
        mFactorySet = true;
        if (mFactory == null) {
            mFactory = factory;
        } else {
            mFactory = new FactoryMerger(factory, null, mFactory, mFactory2);
        }
    }

    /**
     * Like {@link #setFactory}, but allows you to set a {@link Factory2}
     * interface.
     */
    public void setFactory2(Factory2 factory) {
        if (mFactorySet) {
            throw new IllegalStateException("A factory has already been set on this LayoutInflater");
        }
        if (factory == null) {
            throw new NullPointerException("Given factory can not be null");
        }
        mFactorySet = true;
        if (mFactory == null) {
            mFactory = mFactory2 = factory;
        } else {
            mFactory = mFactory2 = new FactoryMerger(factory, factory, mFactory, mFactory2);
        }
    }
Comment 1 Jeremy Kolb 2015-06-04 14:39:23 UTC
Is there at least a workaround?  Maybe I'm missing something...
Comment 2 Jonathan Pryor 2015-06-04 15:24:33 UTC
@Dean: Yet another generator bug, wherein property getter and setters are being tied together even when one is virtual and the other isn't. (In this case, setFactory() is virtual, while getFactory() is non-virtual.)

These methods shouldn't be merged, but we'll need to merge them -- for compatibility -- AND separate them, e.g.

    partial class LayoutInflater {
        public LayoutInfator.Factory Factory {
            get {...}
            set {SetFactory (value);}
        }
        public virtual SetFactory (LayoutInflator.Factory factory)
        {
            ...
        }
    }
Comment 3 Jonathan Pryor 2015-06-04 15:28:31 UTC
> Is there at least a workaround?

Yes: [Export]

http://developer.xamarin.com/guides/android/advanced_topics/java_integration_overview/working_with_jni/#ExportAttribute_and_ExportFieldAttribute

    class MyInflater : LayoutInflater {
        [Export]
        public void setFactory (LayoutInflater.Factory factory)
        {
            ...
            base.Factory = factory;
        }
    }

I'm assuming you need to use the base.Factory property setter because, again, getFactory() is non-virtual, so I don't know how you'd update that value without calling the base class's setFactory() method...
Comment 4 Jeremy Kolb 2015-06-04 15:42:29 UTC
Thanks for the help as always Jon.  This is blocking some MvvmCross improvements.

I'll try [Export].  Will that effectively mark the setter as override?

I'm not quite sure what you mean by the last bit but I do need to call into the base from the overridden method.  I just need to make sure that if other people call the Factory setter that it gets into my method.
Comment 5 Jonathan Pryor 2015-06-04 15:55:37 UTC
> Will that effectively mark the setter as override

Yes, because Java only cares if the method has the same name and argument list (and return type, taking covariance into consideration). If the name and signature matches, it's an override.
Comment 6 Jeremy Kolb 2015-10-07 14:52:48 UTC
@Dean is this fixed yet?
Comment 7 dean.ellis 2015-10-08 09:44:38 UTC
Sorry jkolb, I've been working on the msbuild improvements recently , I haven't managed to get to this one yet. Will try to asap.
Comment 8 Jeremy Kolb 2016-03-21 19:36:37 UTC
Is this fixed yet?
Comment 9 Sven-Michael Stübe 2016-03-25 19:20:58 UTC
That is not working as described. Here is my test code.

public class TestLayoutInflater : LayoutInflater
{
	public TestLayoutInflater(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
	{
	}

	public TestLayoutInflater(Context context) : base(context)
	{
	}

	public TestLayoutInflater(LayoutInflater original, Context newContext) : base(original, newContext)
	{
	}

	public override LayoutInflater CloneInContext(Context newContext)
	{
		return new TestLayoutInflater(this, newContext);
	}

	[Export]
	public void setFactory(LayoutInflater.IFactory factory)
	{
		base.Factory = factory;
	}
}

Usage: 
var newInflater = new TestLayoutInflater(this);
newInflater.Factory = MyViewFactory.Instance;

I tried [Export("setFactory")] and [Export].

Current version:
Xamarin.Android
Version: 6.0.99.544 (Enterprise Edition)
Android SDK: C:\Users\sv\AppData\Local\Android\android-sdk
	Supported Android versions:
		2.3   (API level 10)
		4.0.3 (API level 15)
		4.4   (API level 19)
		5.0   (API level 21)
		5.1   (API level 22)
		6.0   (API level 23)

SDK Tools Version: 24.4.1
SDK Platform Tools Version: 23.0.1
SDK Build Tools Version: 23.0.1

Java SDK: C:\Program Files (x86)\Java\jdk1.7.0_71
java version "1.7.0_71"
Java(TM) SE Runtime Environment (build 1.7.0_71-b14)
Java HotSpot(TM) Client VM (build 24.71-b01, mixed mode, sharing)
Comment 10 Jeremy Kolb 2016-09-22 20:32:01 UTC
Please fix this.