Could not load classes when bind jar

I want to bind this lib to my project https://github.com/nhaarman/ListViewAnimations

However, there are a lot of warning and errors.
I try to use

<remove-node path="/api/package[@name='com.nineoldandroids.animation']/class[@name='PropertyValuesHolder.FloatPropertyValuesHolder']/constructor[@name='PropertyValuesHolder.FloatPropertyValuesHolder' and count(parameter)=2 and parameter[1][@type='java.lang.String'] and parameter[2][@type='com.nineoldandroids.animation.FloatKeyframeSet']]"/>

  <remove-node  path="/api/package[@name='com.nineoldandroids.animation']/class[@name='PropertyValuesHolder.IntPropertyValuesHolder']/constructor[@name='PropertyValuesHolder.IntPropertyValuesHolder' and count(parameter)=2 and parameter[1][@type='java.lang.String'] and parameter[2][@type='com.nineoldandroids.animation.IntKeyframeSet']]" />  

  <remove-node path="/api/package[@name='com.nineoldandroids.animation']/class[@name='PropertyValuesHolder.IntPropertyValuesHolder']/constructor[@name='PropertyValuesHolder.IntPropertyValuesHolder' and count(parameter)=2 and parameter[1][@type='com.nineoldandroids.util.Property'] and parameter[2][@type='com.nineoldandroids.animation.IntKeyframeSet']]" />

  <remove-node path="/api/package[@name='com.nineoldandroids.animation']/class[@name='PropertyValuesHolder.FloatPropertyValuesHolder']/constructor[@name='PropertyValuesHolder.FloatPropertyValuesHolder' and count(parameter)=2 and parameter[1][@type='com.nineoldandroids.util.Property'] and parameter[2][@type='com.nineoldandroids.animation.FloatKeyframeSet']]" />  

to remove the errors from nineoldandroids but there emerges more due to dependency. Can anyone help me bind this jar in Xamarin?

Tagged:

Best Answer

Answers

  • la.tanblala.tanbla USMember ✭✭
    edited December 2015

    See below.

  • la.tanblala.tanbla USMember ✭✭
    edited December 2015

    Replace your metadata.xml file with this content:

    <metadata>
    
      <attr path="/api/package[@name='com.nineoldandroids.animation']/class[@name='IntKeyframeSet']" name="visibility">public</attr>
      <attr path="/api/package[@name='com.nineoldandroids.animation']/class[@name='FloatKeyframeSet']" name="visibility">public</attr>
      <attr path="/api/package[@name='com.nineoldandroids.animation']/class[@name='KeyframeSet']" name="visibility">public</attr>
    
      <attr path="/api/package[@name='com.nineoldandroids.animation']/class[@name='AnimatorSet']/method[@name='setDuration' and count(parameter)=1 and parameter[1][@type='long']]" name="return">com.nineoldandroids.animation.Animator</attr>
      <attr path="/api/package[@name='com.nineoldandroids.animation']/class[@name='ValueAnimator']/method[@name='setDuration' and count(parameter)=1 and parameter[1][@type='long']]" name="return">com.nineoldandroids.animation.Animator</attr>
    
    </metadata>
    

    When binding, I've found the remove-node tag will not usually solve the underlying problem so I try to avoid using it when possible.

    For example, in your case the problem is that the generator is not creating C# classes for IntKeyframeSet and FloatKeyframeSet. We know that they should exist because they are both in api.xml, however their visibility attribute is empty. It needs to be public so we set that in metadata.xml. When we do that, we get the same error except for the class KeyframeSet, which they both inherit from. So we also need to make that public.

    At this point you would be getting errors about the class AnimatorSet and ValueAnimator not implementing the method setDuration from Animator. This is odd because both classes do have a setDuration method that takes a long. The problem is that Animator.setDuration returns Animator but AnimatorSet.setDuration returns AnimatorSet and ValueAnimator.setDuration returns ValueAnimator. I guess that is legal inheritance in Java, but not so for C#. The solution is to set the return type for AnimatorSet.setDuration and ValueAnimator.setDuration to Animator so that both method signatures match Animator.setDuration exactly.

    This will remove all the errors from the project but it is a little less than ideal, assuming that setDuration is meant to be called by you, the library user. I imagine that the reason setDuration returns the calling object is to allow chaining and since it is returning Animator in all subclasses and not the appropriate subclass of Animator you will lose out on some of the functions the subclass has over Animator. The only way I can think of to solve this, is to rename the c# method Animator.SetDuration to something else (like Animator._SetDuration) as well as AnimatorSet.SetDuration and ValueAnimator.SetDuration. Then you can add an addition the creates a SetDuration function for each of those classes and casts it to the right class.

    To do this, add the following to metadata.xml:

      <attr path="/api/package[@name='com.nineoldandroids.animation']/class[@name='Animator']/method[@name='setDuration' and count(parameter)=1 and parameter[1][@type='long']]" name="managedName">_SetDuration</attr>
      <attr path="/api/package[@name='com.nineoldandroids.animation']/class[@name='AnimatorSet']/method[@name='setDuration' and count(parameter)=1 and parameter[1][@type='long']]" name="managedName">_SetDuration</attr>
      <attr path="/api/package[@name='com.nineoldandroids.animation']/class[@name='ValueAnimator']/method[@name='setDuration' and count(parameter)=1 and parameter[1][@type='long']]" name="managedName">_SetDuration</attr>
    

    and create a C# file in the Additions folder::

    using System;
    
    namespace Com.Nineoldandroids.Animation
    {
        public abstract partial class Animator
        {
            public Animator SetDuration(long p0)
            {
                return _SetDuration (p0);
            }
        }
    
        public sealed partial class AnimatorSet
        {
            public new AnimatorSet SetDuration(long p0)
            {
                return (AnimatorSet)_SetDuration (p0);
            }
        }
    
        public partial class ValueAnimator
        {
            public new ValueAnimator SetDuration (long p0)
            {
                return (ValueAnimator)_SetDuration (p0);
            }
        }
    }
    

    That builds for me, but I can't test the binding on an actual application. Let me know if that works for you or if you have any questions. Sorry for the wall of text. :smiley: There isn't a lot of documentation that I could find on this and I haven't been able to get any help on this forum so I wanted to post some of what I've learned for the rest of the community.

    Edit:

    One other thing. _SetDuration will probably come up in IntelliSense and that may bother you. If you want to fix that, you could probably change the visibility for just the setDuration to protected with the following additions to metadata.xml:

    <attr path="/api/package[@name='com.nineoldandroids.animation']/class[@name='Animator']/method[@name='setDuration' and count(parameter)=1 and parameter[1][@type='long']]" name="visibility">protected</attr>
      <attr path="/api/package[@name='com.nineoldandroids.animation']/class[@name='AnimatorSet']/method[@name='setDuration' and count(parameter)=1 and parameter[1][@type='long']]" name="visibility">protected</attr>
      <attr path="/api/package[@name='com.nineoldandroids.animation']/class[@name='ValueAnimator']/method[@name='setDuration' and count(parameter)=1 and parameter[1][@type='long']]" name="visibility">protected</attr>
    

    Just tested and the binding project builds and my app detects SetDuration and is unable to access _SetDuration. I am getting quite a few Java errors though involving packages not existing. But that is out side of the scope of my knowledge as well as the scope of binding. Seems like it has to do with Mono.

  • YingnanWangYingnanWang USMember

    @la.tanbla Thanks a lot for your detailed explanation :smile:

    Do you set the lib-core and the nineoldandroids jars' Build Action as EmbeddedJar or EmbeddedReferenceJar?

  • YingnanWangYingnanWang USMember

    At first, I set lib-core and nineoldandroids jar build action as EmbeddedReferenceJar. All errors are removed. However still exist 26 warnings and when I test in my app, the class I want to use is not detected due to those warning. Such as:

    D:\Summer\1Project\Zhongxin\ListViewAnimationTest\ListViewLibs\BINDINGSGENERATOR: Warning BG8102: Class Com.Nhaarman.Listviewanimations.Itemmanipulation.Animateaddition.AnimateAdditionAdapter has unknown base type com.nhaarman.listviewanimations.BaseAdapterDecorator. (BG8102) (ListViewLibs)

    So I expose all jar to C# by setting the build action to EmbeddedJar and use @la.tanbla's code in the metadata.xml to remove the errors due to nineoldandroids:

    '<attr path="/api/package[@name='com.nineoldandroids.animation']/class[@name='IntKeyframeSet']" name="visibility">public</attr>
      <attr path="/api/package[@name='com.nineoldandroids.animation']/class[@name='FloatKeyframeSet']" name="visibility">public</attr>
      <attr path="/api/package[@name='com.nineoldandroids.animation']/class[@name='KeyframeSet']" name="visibility">public</attr>  
      <attr path="/api/package[@name='com.nineoldandroids.animation']/class[@name='AnimatorSet']/method[@name='setDuration' and count(parameter)=1 and parameter[1][@type='long']]" name="return">com.nineoldandroids.animation.Animator</attr>
      <attr path="/api/package[@name='com.nineoldandroids.animation']/class[@name='ValueAnimator']/method[@name='setDuration' and count(parameter)=1 and parameter[1][@type='long']]" name="return">com.nineoldandroids.animation.Animator</attr>'
    

    Then I use remove node to remove the "DragDrop" class I don't need:

    <remove-node path="/api/package[@name='com.nhaarman.listviewanimations.itemmanipulation.dragdrop']/interface[@name='DragAndDropListViewWrapper']"/>

    Now there are only two errors:

    D:\Summer\1Project\Zhongxin\ListViewAnimationTest\ListViewLibs\obj\Debug\generated\src\Com.Nhaarman.Listviewanimations.BaseAdapterDecorator.cs(32,32): Error CS0535: 'Com.Nhaarman.Listviewanimations.BaseAdapterDecorator' does not implement interface member 'Com.Nhaarman.Listviewanimations.Util.IListViewWrapperSetter.SetListViewWrapper(Com.Nhaarman.Listviewanimations.Util.IListViewWrapper)' (CS0535) (ListViewLibs) D:\Summer\1Project\Zhongxin\ListViewAnimationTest\ListViewLibs\obj\Debug\generated\src\Com.Nhaarman.Listviewanimations.Util.AbsListViewWrapper.cs(23,23): Error CS0738: 'Com.Nhaarman.Listviewanimations.Util.AbsListViewWrapper' does not implement interface member 'Com.Nhaarman.Listviewanimations.Util.IListViewWrapper.ListView'. 'Com.Nhaarman.Listviewanimations.Util.AbsListViewWrapper.ListView' cannot implement 'Com.Nhaarman.Listviewanimations.Util.IListViewWrapper.ListView' because it does not have the matching return type of 'Android.Views.ViewGroup'. (CS0738) (ListViewLibs)

    For the second error, I use:
    <attr path="/api/package[@name='com.nhaarman.listviewanimations.util']/class[@name='AbsListViewWrapper']/method[@name='getListView']" name="managedReturn">Android.Views.ViewGroup</attr>
    to remove the inconsist return type.

    For the first error, I find this post: https://forums.xamarin.com/discussion/35713/xamarin-jar-binding-library-metadata-xml-tips-for-missing-abstract-method

    My understand is that there is a conflict of the parameter type when implement the interface so generate this error. So I check the code of the lib-core.

    In the ListViewWrapperSetter.java:

    package com.nhaarman.listviewanimations.util; import android.support.annotation.NonNull; public interface ListViewWrapperSetter { void setListViewWrapper(@NonNull final ListViewWrapper listViewWrapper); }

    And in the BaseAdapterDecorator.java:

    @Override public void setListViewWrapper(@NonNull final ListViewWrapper listViewWrapper) { mListViewWrapper = listViewWrapper; if (mDecoratedBaseAdapter instanceof ListViewWrapperSetter) { ((ListViewWrapperSetter) mDecoratedBaseAdapter).setListViewWrapper(listViewWrapper); } } public void setAbsListView(@NonNull final AbsListView absListView) { setListViewWrapper(new AbsListViewWrapper(absListView)); //AbsListViewWrapper implements ListViewWrapper }
    I don't see any parameter conflict in the implementation. So I try to use:

    <attr path="/api/package[@name='com.nhaarman.listviewanimations']/class[@name='BaseAdapterDecorator']/method[@name='setListViewWrapper' and count(parameter)=1 and parameter[1][@type='com.nhaarman.listviewanimations.util.ListViewWrapper']]/parameter[1]" name="managedType">Java.Lang.Object</attr>

    to solve but it still exist.

    I don't know why it cannot be implemented. Any ideas about that?

  • YingnanWangYingnanWang USMember
    edited December 2015

    So follow this post: https://forums.xamarin.com/discussion/comment/109796/#Comment_109796 I should remove the method use:

    <remove-node path="/api/package[@name='com.nhaarman.listviewanimations']/class[@name='BaseAdapterDecorator']/method[@name='setListViewWrapper' and count(parameter)=1 and parameter[1][@type='com.nhaarman.listviewanimations.util.ListViewWrapper']]"/>

    and add a partial class to redeclare this method. How can I write a partial class for this setListViewWrapper method in BaseAdapterDecorator?

  • YingnanWangYingnanWang USMember

    @la.tanbla Thank you very much! It eventually works!

Sign In or Register to comment.