Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MvxListView activated indicator doesn't work on 3.0.13 #481

Closed
ze-pequeno opened this issue Oct 29, 2013 · 21 comments
Closed

MvxListView activated indicator doesn't work on 3.0.13 #481

ze-pequeno opened this issue Oct 29, 2013 · 21 comments
Labels
s/needs-more-info Needs more information from the reporter

Comments

@ze-pequeno
Copy link

I have a simple layout with MvxListView :

<Mvx.MvxListView
   android:id="@+id/listview"
   android:layout_width="fill_parent"
   android:layout_height="wrap_content"
   local:MvxItemTemplate="@layout/listview_item"
   local:MvxBind="ItemsSource Items; ItemClick SelectedItemCommand" />

My item template layout is simple to :

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:local="http://schemas.android.com/apk/res-auto"
    android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:minHeight="32dp"
    android:background="@drawable/listview_item_activated"
    android:gravity="center_vertical">
    <include
        layout="@layout/items_content" />
</LinearLayout>

And my activated drawable resource :

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:state_activated="true" android:drawable="@drawable/core_listview_activated"/>
  <item android:drawable="@drawable/core_listview_unactivated"/>
</selector>

I have enabled the single mode on my ListView with ChoiceMode property. With MvvmCross v3.0.12, when I click on item, activated drawable resource is apply (in my case draw an arrow to the right to indicate my selected detail item). But, since I upgrade to v3.0.13, it is no longer apply.

Any idea of the problem, or do I need to change something to make it work with upgrade ?

@slodge
Copy link
Contributor

slodge commented Oct 29, 2013

The only change I'm aware of in this area is that we've allowed checkable items - see the checked state in https://github.com/MvvmCross/MvvmCross-Tutorials/blob/master/ApiExamples/ApiExamples.Droid/Resources/Layout/Test_Spinner.axml

The change for this was 6ab5dbe

There's no direct change to the activated state here - but maybe android does something funky internally (checked is pre-honeycomb and activated is later)

Possible Workarounds/solutions:

  • maybe use checked instead of activated?
  • maybe use a custom Imvxlistitemview class instead of mvxbaselistitemview/mvxlistitemview (I don't think the core mvx can support checked list items without mvxbaselistitemview implementing icheckable

@ze-pequeno
Copy link
Author

Checked state does not work better ...

How can I declare and use a custom IMvxListItemView ?

@ze-pequeno
Copy link
Author

Do I have to write a custom Adapter and override a method like GetView, GetBindableView or CreateBindableView ?

@slodge
Copy link
Contributor

slodge commented Oct 30, 2013

Do I have to write a custom Adapter?

Yes

and override a method like GetView, GetBindableView or CreateBindableView ?

Just CreateBindableView

@ze-pequeno
Copy link
Author

Thank you I make a test as soon as possible and I give you feedback.

@ze-pequeno
Copy link
Author

I think test this solution is actually not possible. MvxAdapter.CreateBindableView must return a MvxListItemView. So I can't return a custom IMvxListItemView. I'm wrong ?

@slodge
Copy link
Contributor

slodge commented Nov 2, 2013

Arg - that's a pain - sorry - I will add a CreateBindableListItemView that returns the interface in 3.0.14 (which is next few days)

Until then, the only thing you can do is to override GetBindableView too

@slodge
Copy link
Contributor

slodge commented Nov 2, 2013

I.e.

    protected override View GetBindableView(View convertView, object dataContext, int templateId)
    {
        if (templateId == 0)
        {
            // no template seen - so use a standard string view from Android and use ToString()
            return GetSimpleView(convertView, dataContext);
        }

        // we have a templateid so lets use bind and inflate on it :)
        var viewToUse = convertView as IMvxListItemView;
        if (viewToUse != null)
        {
            if (viewToUse.TemplateId != templateId)
            {
                viewToUse = null;
            }
        }

        if (viewToUse == null)
        {
            viewToUse = YOURCreateBindableView(dataContext, templateId);
        }
        else
        {
            BindBindableView(dataContext, viewToUse);
        }

        return viewToUse as View;
    }

@ze-pequeno
Copy link
Author

Thank's for your update in 3.0.14. I try override GetBindableView too.

@ze-pequeno
Copy link
Author

I confirm that my problem comes with the change 6ab5dbe.

I created three classes to have the same code as in version 3.0.12 and my list indicator work.

ListItemView :

public class ListItemView
        : BaseListItemView, IMvxListItemView
    {
        private readonly int _templateId;

        public ListItemView(Context context,
                               IMvxLayoutInflater layoutInflater,
                               object dataContext,
                               int templateId)
            : base(context, layoutInflater, dataContext)
        {
            _templateId = templateId;
            Content = AndroidBindingContext.BindingInflate(templateId, this);
        }

        public int TemplateId
        {
            get { return _templateId; }
        }
    }

BaseListItemView :

    public abstract class BaseListItemView
        : FrameLayout, IMvxBindingContextOwner
    {
        private readonly IMvxAndroidBindingContext _bindingContext;

        protected BaseListItemView(Context context, IMvxLayoutInflater layoutInflater, object dataContext)
            : base(context)
        {
            _bindingContext = new MvxAndroidBindingContext(context, layoutInflater, dataContext);
        }

        protected IMvxAndroidBindingContext AndroidBindingContext
        {
            get { return _bindingContext; }
        }

        public IMvxBindingContext BindingContext
        {
            get { return _bindingContext; }
            set { throw new NotImplementedException("BindingContext is readonly in the list item"); }
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                this.ClearAllBindings();
            }

            base.Dispose(disposing);
        }

        protected View Content { get; set; }

        public object DataContext
        {
            get { return _bindingContext.DataContext; }
            set { _bindingContext.DataContext = value; }
        }
    }

BaseAdapter :

    class BaseAdapter : MvxAdapter
    {
        public BaseAdapter(Context context) : base(context)
        {

        }

        public BaseAdapter(Context context, IMvxAndroidBindingContext bindingContext) : base(context, bindingContext)
        {

        }

        protected override View GetBindableView(View convertView, object dataContext, int templateId)
        {
            if (templateId == 0)
            {
                // no template seen - so use a standard string view from Android and use ToString()
                return GetSimpleView(convertView, dataContext);
            }

            // we have a templateid so lets use bind and inflate on it :)
            var viewToUse = convertView as IMvxListItemView;
            if (viewToUse != null)
            {
                if (viewToUse.TemplateId != templateId)
                {
                    viewToUse = null;
                }
            }

            if (viewToUse == null)
            {
                viewToUse = CustomCreateBindableView(dataContext, templateId);
            }
            else
            {
                BindBindableView(dataContext, viewToUse);
            }

            return viewToUse as View;
        }

        protected ListItemView CustomCreateBindableView(object dataContext, int templateId)
        {
            return new ListItemView(Context, BindingContext.LayoutInflater, dataContext, templateId);
        }
    }

Sorry, but I do not understand why this change has broken this feature. Maybe you could guide me and then I can help you understand why. Do you want a test VS solution ?

Thank's for your time

@slodge
Copy link
Contributor

slodge commented Nov 2, 2013

Sorry - but i don't know either - there must be some code in listview or adapterview which checks for checkable and treats it specially - somewhere deep in http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.3_r2.1/android/widget/ListView.java/

Will look into this a bit further when on a PC (currently on iPad)

@slodge
Copy link
Contributor

slodge commented Nov 3, 2013

Much easier to see on a PC...

The code starting line 1899 in http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.3_r2.1/android/widget/ListView.java/ shows:

        if (mChoiceMode != CHOICE_MODE_NONE && mCheckStates != null) {
            if (child instanceof Checkable) {
                ((Checkable) child).setChecked(mCheckStates.get(position));
            } else if (getContext().getApplicationInfo().targetSdkVersion
                    >= android.os.Build.VERSION_CODES.HONEYCOMB) {
                child.setActivated(mCheckStates.get(position));
            }
        }

So I'm guessing that's it.

To work around this I'm considering Activated to the checked set handler in BaseListViewItem:

        set
        {
            var contentCheckable = ContentCheckable;
            if (contentCheckable == null)
            {
                _checked = value;

                // since we don't have genuinely checked content, then use FirstChild activation instead
                // see https://github.com/MvvmCross/MvvmCross/issues/481
                var firstChild = FirstChild;
                if (firstChild != null)
                    if (Context.ApplicationInfo.TargetSdkVersion >= Android.OS.BuildVersionCodes.Honeycomb)
                        firstChild.Activated = value;
                return;
            }

            contentCheckable.Checked = value;
        }

What do you think?

@ze-pequeno
Copy link
Author

I just tested this solution and it works. So if I understand on Android > Honeycomb, when I mark an item as Checked, Activated too ? There is no BC break ?

@slodge
Copy link
Contributor

slodge commented Nov 3, 2013

when I mark an item as Checked, Activated too ?

Afaik, the logic is either Checked or Activated:

    if (mChoiceMode != CHOICE_MODE_NONE && mCheckStates != null) {
        if (child instanceof Checkable) {
            ((Checkable) child).setChecked(mCheckStates.get(position));
        } else if (getContext().getApplicationInfo().targetSdkVersion
                >= android.os.Build.VERSION_CODES.HONEYCOMB) {
            child.setActivated(mCheckStates.get(position));
        }
    }

There is no BC break ?

I don't know what this means

@ze-pequeno
Copy link
Author

I meant that this change does not break the existing code for other users of MvvmCross ?

@slodge
Copy link
Contributor

slodge commented Nov 4, 2013

I've tried to get this code in there tonight

The problem I'm hitting is that to compile this I seem to need to use SDK version 3.1 or greater on the PC (and v4.0 or greater on the Mac).

This seems to be forcing users to use these SDKs too.

This shouldn't be an issue in the longer term - I think we can shift to force users to compile with a modern SDK (they can still target old SDK versions) but I need a little longer to get my head straight on this - and possibly to then update all the samples and N+1s when i release it.

Stuart

slodge added a commit that referenced this issue Nov 4, 2013
@ze-pequeno
Copy link
Author

Maybe you need time to include this on new MvvmCross release. At the moment I have created a compatibility ListView for my needs as in 3.0.12. Take your time to fix it. Thank's again.

@helado
Copy link

helado commented Nov 26, 2013

Good thing I came across this thread while having the same problem on 3.0.13, just one question -> if the ChoiceMode = ChoiceMode.Single then should the list update itself automatically or should it be done manually via the adapter? right now I need to change it manually

csteeg added a commit to csteeg/MvvmCross that referenced this issue Dec 11, 2013
MvvmCross#481

Android.OS.Build.VERSION.SdkInt looks at the runtime sdk version, whereas the TargetSdkVersion looks at the version what's used to build the project with
@slodge
Copy link
Contributor

slodge commented Feb 2, 2014

I believe this is fixed in 3.1.1-beta5

Closing - it can be reopened if people tell me it is still broken

@slodge slodge closed this as completed Feb 2, 2014
@ze-pequeno
Copy link
Author

I try to check this soon and give you feedback. Thank's

@ze-pequeno
Copy link
Author

Issue seems to be fixed in 3.1.1-beta5.

Thank's for your work

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
s/needs-more-info Needs more information from the reporter
Development

No branches or pull requests

3 participants