how to enable/disable single listview items?

3,717 views
Skip to first unread message

Giuseppe

unread,
Dec 20, 2011, 6:48:14 AM12/20/11
to android...@googlegroups.com
hello all.

I might be missing something really simple here, but anyway:

I have a layout with a ListView, binded with its itemSource attribute to an ArrayListObservable<MyItemClass>. The layout for a single item consists of a RelativeLayout containing an ImageView, a custom TextView and a CheckBox. All of those have binding:source, :text and :checked attributes, binded to the same Observables in MyItemClass.

I'd like to add a further 'enabled' BooleanObservable in MyItemClass, and reflect that into the enabled/clickable/selectable state of the corresponding listview item.

How should I proceed?

egandro egandro

unread,
Dec 20, 2011, 7:41:52 AM12/20/11
to AndroidBinding
You want to disable specific items in one list?

e.g. item1, item2, item3 and you just want to show item1 and item3
depending on a given bool state?

Andy Tsui

unread,
Dec 20, 2011, 9:03:58 AM12/20/11
to android...@googlegroups.com
Suppose your itemTemplate is:

<LinearLayout binding:enabled="Enabled">
    <...>

This should work.

But I don't think by default ListView can set individual item to be non-selectable if you use the Choice Mode in ListView. In such case, you have to make your own custom item template (and since your source is ArrayList, it should be fine) and iterate through to know which is "selected":

<LinearLayout>
    <CheckBox binding:checked="Selected" binding:enabled="Enabled">
    ...
</LinearLayout>

In this case, you don't need to set the "ChoiceMode" in ListView, since everything is handled in child level.

Andy

Andy Tsui

unread,
Dec 20, 2011, 9:05:21 AM12/20/11
to android...@googlegroups.com
Moreover, if you opt for hiding the item,  I think

<LinearLayout binding:visibility="Enabled">

on itemTemplate should be fine. (Well, please do test it, I am not 100% sure it will not do any weird thing on ListView)

Andy

Giuseppe Piscopo

unread,
Dec 20, 2011, 9:53:18 AM12/20/11
to android...@googlegroups.com, Andy Tsui
Nella citazione in data martedì 20 dicembre 2011 15:05:21, Andy Tsui ha
scritto:
> <mailto:piscopo.giuse...@gmail.com>> wrote:
> > hello all.
> >
> > I might be missing something really simple here, but anyway:
> >
> > I have a layout with a ListView, binded with its itemSource
> attribute to an
> > ArrayListObservable<MyItemClass>. The layout for a single
> item consists of
> > a RelativeLayout containing an ImageView, a custom TextView
> and a CheckBox.
> > All of those have binding:source, :text and :checked
> attributes, binded to
> > the same Observables in MyItemClass.
> >
> > I'd like to add a further 'enabled' BooleanObservable in
> MyItemClass, and
> > reflect that into the enabled/clickable/selectable state of the
> > corresponding listview item.
> >
> > How should I proceed?
>
>
>

So, to be clear: my purpose is to show all items anyway, but to leave
some of them "enabled" (i.e. as they usually are), and some of them
"disabled".
By "enabled" I mean that the single item (and all its contained Views)
can receive clicks and has its "default" look.
By "disabled" I mean that it cannot receive clicks and it shows some
kind of "disabled" look, e.g. greyed, whatever.

So I my understanding, reading your replies, is that I should try to
set following attribute:

binding:enabled="Enabled"

at the item ViewGroup level. Hence something like:

my_item_layout.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:binding="http://www.gueei.com/android-binding/"
binding:enabled="itemEnabled"
style="@style/list_item_container_layout">

<ImageView
android:id="@+id/listItemImageView"
binding:source="image"
style="@style/list_item_image" />
<TextView
android:id="@+id/listItemTextView"
binding:text="title"
style="@style/list_item_text" />
<CheckBox
android:id="@+id/listItemCheckBox"
binding:checked="checked"
style="@style/list_item_check" />
</RelativeLayout>

I did not get what you said regarding choiceMode. Anyway I'll try with
this and let you know the outcomes.

Thanks. Cheers

Giuseppe

unread,
Dec 20, 2011, 4:36:01 PM12/20/11
to android...@googlegroups.com, Andy Tsui
In order to share my current findings:

So, the outcomes are negative :-\
I tried adding the following 3 attributes:

       binding:clickable="enabled"
       binding:focusable="enabled"
       binding:enabled="enabled"

to the RelativeLayout container, and at the same time to each of the contained Views (ImageView, TextView, CheckBox).
I know not all of them are supported, but it was just a proof.

I made so that the "enabled" BooleanObservable is always false. No matter which attributes I use, every list item is still clickable anyway.

I'm currently trying to extend gueei.binding.collections.CollectionAdapter in order to add some custom behavior, by overriding "areAllItemsEnabled" and "isEnabled". When I'm done I'll try to instantiate such an adapter as a public field of my ViewModel, and then bind it to my ListView in XML as in the following:

    <ListView
        android:id="@+id/my_listview"
        binding:adapter="my_listAdapter"
        [...] />

Giuseppe

unread,
Dec 20, 2011, 5:43:21 PM12/20/11
to android...@googlegroups.com, Andy Tsui
Okay. Extending CollectionAdapter worked. Here some excerpts of what I've done:

** IEnabled.java
public interface IEnabled
{
    public boolean isEnabled();
}

** EnabledCollectionAdapter.java
public class EnabledCollectionAdapter extends CollectionAdapter
{
    public EnabledCollectionAdapter(Context context, IObservableCollection<? extends IEnabled> collection,
        int layoutId, int dropDownLayoutId) throws Exception
    {
        super(context, collection, layoutId, dropDownLayoutId);
    }

    @Override
    public boolean areAllItemsEnabled()
    {
        return false;
    }

    @Override
    public boolean isEnabled(int position)
    {
        if (position >= mCollection.size())
        {
            throw new ArrayIndexOutOfBoundsException("isEnabled: position (" + position + ") >= collection size ("
                + mCollection.size() + ")");
        }

        IEnabled item = (IEnabled) mCollection.getItem(position);

        return item.isEnabled();
    }
}

** MyViewModel.java
public class MyViewModel
{
    public public EnabledCollectionAdapter my_listAdapter;
    private ArrayListObservable<MyItemObservable> _myListItems;

    public MyViewModel(....)
    {
        [...]
        // Instantiate binded Observables
        _myListItems = new ArrayListObservable<MyItemObservable>(MyItemObservable.class);

        try
        {
            my_listAdapter = new EnabledCollectionAdapter(_context, _myListItems,
                R.layout.my_list_item_template, 0);
        }
        catch (Exception ex)
        {
            Log.e(getClass().getSimpleName(), "Bla bla bla", ex);
            throw new RuntimeException(ex);
        }
    }
}

** my_layout.xml
<ListView
    android:id="@+id/myListView"
    binding:adapter="my_listAdapter"
    style="@style/my_list_view_style" />

Next step would be to create a dedicated Converter (something like ADAPTER), named i.e. ENABLED_ADAPTER, and let the converter instantiate the custom adapter with the observable, item layout, etc. given in XML.

Giuseppe

unread,
Dec 20, 2011, 5:46:38 PM12/20/11
to android...@googlegroups.com, Andy Tsui
Missing bit from previous one:

** MyItemObservable.java
public class MyItemObservable implements IEnabled
{
    [...]
    @Override
    public boolean isEnabled()
    {
        return /* calculate your enabled state */;
    }
}

Andy Tsui

unread,
Dec 20, 2011, 7:53:32 PM12/20/11
to android...@googlegroups.com
Thanks for your effort. I think I omit a truth that the child layout is handled by ListView and no item level control is possible. I may update that part soon, here's my proposed method:

(in markup)
<ListView
    binding:adapter="{dataSource=..., itemTemplate=..., enabled='Enabled'}"

* enabled will look at the item level property for Enabling or not. this is optional, if nothing specified, that means allItemsEnabled.

so, this could prevent the need for IEnabled interface, and could be a bit more generic.

Andy

Giuseppe Piscopo

unread,
Dec 20, 2011, 8:11:22 PM12/20/11
to android...@googlegroups.com, Andy Tsui
Nella citazione in data mercoledì 21 dicembre 2011 01:53:32, Andy Tsui
ha scritto:

> Thanks for your effort. I think I omit a truth that the child layout
> is handled by ListView and no item level control is possible. I may
> update that part soon, here's my proposed method:
>
> (in markup)
> <ListView
> binding:adapter="{dataSource=..., itemTemplate=...,
> enabled='Enabled'}"
>
> * enabled will look at the item level property for Enabling or not.
> this is optional, if nothing specified, that means allItemsEnabled.
>
> so, this could prevent the need for IEnabled interface, and could be a
> bit more generic.
>
> Andy
>
> On Wed, Dec 21, 2011 at 6:46 AM, Giuseppe <piscopo....@gmail.com
> <mailto:piscopo....@gmail.com>> wrote:
>
> Missing bit from previous one:
>
> *** *__*MyItemObservable.java*

> public class MyItemObservable implements IEnabled
> {
> [...]
> @Override
> public boolean isEnabled()
> {
> return /* calculate your enabled state */;
> }
> }
>
>

You're welcome.
Actually, that would be quite a sleek solution!

Andy Tsui

unread,
Dec 20, 2011, 8:51:29 PM12/20/11
to Giuseppe Piscopo, android...@googlegroups.com
I think the only problem, for both of our solutions, is that the enable state cannot reflect dynamically: i.e., if the item is already showing, toggling the enabled state is very hard to reflect on it.. There's method, but way too complicated..

On Wed, Dec 21, 2011 at 9:11 AM, Giuseppe Piscopo <piscopo....@gmail.com> wrote:
Nella citazione in data mercoledì 21 dicembre 2011 01:53:32, Andy Tsui ha scritto:
Thanks for your effort. I think I omit a truth that the child layout is handled by ListView and no item level control is possible. I may update that part soon, here's my proposed method:

(in markup)
<ListView
   binding:adapter="{dataSource=..., itemTemplate=..., enabled='Enabled'}"

* enabled will look at the item level property for Enabling or not. this is optional, if nothing specified, that means allItemsEnabled.

so, this could prevent the need for IEnabled interface, and could be a bit more generic.

Andy

On Wed, Dec 21, 2011 at 6:46 AM, Giuseppe <piscopo....@gmail.com <mailto:piscopo.giuseppe@gmail.com>> wrote:

   Missing bit from previous one:

   *** *__*MyItemObservable.java*

   public class MyItemObservable implements IEnabled
   {
       [...]
       @Override
       public boolean isEnabled()
       {
           return /* calculate your enabled state */;
       }
   }


Giuseppe

unread,
Dec 20, 2011, 9:35:26 PM12/20/11
to android...@googlegroups.com, Giuseppe Piscopo
yes. But I think that this is acceptable, in the sense that:

* Android by itself does not guarantee at all to reflect dynamic changes in listview item's enabled state, in particular in 'areAllItemsEnabled'. See http://developer.android.com/reference/android/widget/BaseAdapter.html#areAllItemsEnabled%28%29

* to "bring to light" this enabled/disabled behaviour for each item is already an advance as compared as before. Even if it does not get updated when the enabled state changes. If the need arises in the future, well ... you can see the actual need and decide how to proceed. Maybe when the developer knows that some item has changed, one can issue some kind of notifySomethingChangedInList and so to force the adapter to call isEnabled again on every item. I mean ... it depends on the need.

Related to your proposed way (ADAPTER plus a further 'enabled' parameter within braces):

I'd like to experiment with that. I can figure out how to extract the 'enabled' parameter from DynamicObject, just copying from original ADAPTER.java. But I cannot figure out how to use that. Here's what I understood:

In principle, this parameter refers to an Observable. But it's not an Observable operating at the collection/listview level. It's one operating at the item level.
a) So I should somehow "get a reference" to this observable (its name?), instead of actually extracting it from the DynamicObject. Right?
b) Then I shoul pass this "reference" down to the custom adapter.
c) Then the custom adapter, for every position required in isEnabled(position), should obtain the current item from collection, and then extract/obtain the actual Observable with that name defined from the current item.

Of course, in the rare circumstances that this is right, I don't know how to go from a) to c).

Andy Tsui

unread,
Dec 20, 2011, 9:52:11 PM12/20/11
to android...@googlegroups.com, Giuseppe Piscopo
Your analysis is right and clear.

Yes, we need to change the Adapter. Since I think the use case is quite common, I think I will change the underneath CollectionAdapter to support this.

> a) So I should somehow "get a reference" to this observable (its name?), instead of actually extracting it from the DynamicObject. Right?
yes, that's why I pass it as a "string" with single quotes => evaluate afterwards

> b) Then I shoul pass this "reference" down to the custom adapter.
true

> c) Then the custom adapter, for every position required in isEnabled(position), should obtain the current item from collection, and then extract/obtain the actual Observable with that name defined from the current item.
I haven't really think in-depth in technical detail, I think it is possible.. but if later it turns out if the interaction is too complicated, I have an easier plan B:

Leave the ListView as it is, but during getView() in the adapter, I will get the "enabled" state of the child item (in other words, the markup is at child level but not listview)

Andy

Giuseppe

unread,
Dec 20, 2011, 10:51:57 PM12/20/11
to android...@googlegroups.com, Giuseppe Piscopo
Ok, I see. So for now I will stick with what I've obtained.
In the meantime, I was able to create the custom Converter returning the custom EnabledCollectionAdapter.

WARNING: The following code is mostly copied from ADAPTER.java implementation. As in my current project I'm using at an old version of AndroidBinding, 0.2, this implementation reflects that version.
If one had to create a more recent custom Converter, it should start from an updated version of ADAPTER.java.

The main differences from the original ADAPTER.java are in green bold. Keep in mind that I had no need for a widely generic implementation, so I just considered the case of a source derived from IObservableCollection, and so I could directly instantiate the custom adapter. Otherwise I should have implemented a similar Utility.getEnabledCollectionAdapter(...).

** ENABLED_ADAPTER.java
public class ENABLED_ADAPTER extends Converter<Adapter>
{
    public ENABLED_ADAPTER(IObservable<?>[] dependents)
    {
        super(Adapter.class, dependents);
    }

    @Override
    public Adapter calculateValue(Object... args) throws Exception
    {
        try
        {
            DynamicObject object = (DynamicObject) args[0];

            if (!object.observableExists("template")) return null;

            if (!object.observableExists("source")) return null;

            int template = (Integer) object.getObservableByName("template").get();

            int spinnerTemplate = -1;

            if (object.observableExists("spinnerTemplate"))
            {
                spinnerTemplate = (Integer) object.getObservableByName("spinnerTemplate").get();
            }

            spinnerTemplate = (spinnerTemplate < 0) ? template : spinnerTemplate;

            IObservable<?> source = object.getObservableByName("source");

            if (!(source instanceof IObservableCollection<?>)) return null;

            @SuppressWarnings("unchecked")
            IObservableCollection<? extends IEnabled> enabledSource = (IObservableCollection<? extends IEnabled>) source;

            return new EnabledCollectionAdapter(Binder.getApplication(), enabledSource, template, spinnerTemplate);
        }
        catch (Exception ex)
        {
            Log.e(getClass().getSimpleName(), "Bla bla bla", ex);
            return null;
        }
    }
}

Andy Tsui

unread,
Dec 21, 2011, 7:33:44 AM12/21/11
to android...@googlegroups.com, Giuseppe Piscopo
I've tried to test the scenario myself, and I found out the "Enable" is working when child item take the control on the click event.

Suppose ListView markup is untouched, the following childitem template works as expected:

<LinearLayout ...
    binding:onClick="..."
    binding:enabled="..." />

if it is not enabled, the click will not response, which is what we are expecting, but if 'onClick' is absent, it is like what Giuseppe found out, which the ListView take control and handle the child click event, so, the enabled is not functioning.

PS, I have started to implement previously mentioned ListView level enable as well, since we do have scenarios required that.

Andy

Giuseppe Piscopo

unread,
Dec 21, 2011, 7:47:18 AM12/21/11
to Andy Tsui, android...@googlegroups.com
Great, well done.

I would say that the common/ suggested way of working with clicks within listviews is by means of onItemClick attribute set at the listview level ... so I'd say that the "listlevel enable" scenario is more appropriate.
Instead, the way of working by means of onClick attribute at the item template level seems somehow a workaround to me.

I might be wrong on those assumptions, of course.

2011/12/21 Andy Tsui <guee...@gmail.com>

Andy Tsui

unread,
Dec 22, 2011, 11:29:43 AM12/22/11
to Giuseppe Piscopo, android...@googlegroups.com
The suggested enhancement is completed. Interested party can download the latest library and markup demo and look for:
Simple List -> List View Individual Item Control
to see how it works.

Andy
Reply all
Reply to author
Forward
0 new messages