2

What I want to achieve:

  • A list item is highlighted when the user is pressing it
  • When the user stops pressing it, the list item:
    • remains highlighted, if it was unselected before the pressing
    • loses the highlight, if it was selected before the pressing
  • Long clicks behave the same way as the user stopping the pressing (changing the background of the item depending on its previous state)
  • Scrolling the list, without pressing any specific item, should not highlight any item

Details:

  • For what I read, I think that behaviour could be achieved using list selectors and the state android:state_activated, but this state was introduced in API level 11. The solution I am looking for has to work in API level 10
  • I think that solutions relying on click (onItemClick, onClick...) will not work, because the click is triggered after the user stops the pressing, not when he starts it (like the pressed state does). Changing the highlight of an item using android:state_pressed is not persistent (it will change back after the press is finished) and changing it in android:state_pressed and making it persistent on click will produce a flicker
  • A good example of app that achieves that in API level 10 is Tasks. Just click or long click on items in the list to see the desired behaviour

So, anybody has already solved that? Any idea on how the Tasks app does it?

Edu Zamora
  • 1,492
  • 1
  • 19
  • 30
  • You could always handle the row's selection on your own in the adapter. – user Dec 19 '12 at 06:12
  • @Luksprog: Yes, that is what I am currently doing. But in order to achieve the behavior that I described in the question I would still need somehow to detect when an item is pressed, so I can change the background of the item in the adapter. If I wait until onItemClick is called to select the item and notify that the data in the adapter has changed, I cannot change the background when the user starts pressing the item, but when he stops pressing it. Any idea on how to do that? – Edu Zamora Dec 20 '12 at 04:59
  • The Tasks app doesn't do anything that special, that is a CAB selection. If this is what you want then check this question http://stackoverflow.com/questions/10598348/multiple-selection-in-custom-listview-with-cab . *I think that solutions relying on click (onItemClick, onClick...) will not work* - the Tasks app does exactly this to highlight the row before going to the next screen(and noticing if it's in the CAB selection mode). *Long clicks behave the same way as the user stopping the pressing* - That is implemented in the CAB, see the question above. – user Dec 21 '12 at 17:59
  • @Luksprog: Yes, what I would like is the behavior of the Tasks app when the list is in CAB mode. A while ago, I took a look at the exact question that you link, but I would say that doing what the selected answer says, just changed the background of a list item when the user finished the pressing, not when he started it. I will double-check and report back, though. – Edu Zamora Dec 22 '12 at 09:51
  • 1
    Have a try at the code in that question. If you don't manage to make it, let my know so I can try to provide a sample app. – user Dec 22 '12 at 09:57
  • The code from the CAB answer isn't what you're looking for. Instead your question is most likely related to this one http://stackoverflow.com/questions/13531272/state-activated-on-pre-honeycomb-devices . Haven't tested the actual solution but seems what you're looking for. – user Dec 31 '12 at 12:32
  • That could be what I was looking for. I will try it as soon as I have time and report back! In the other hand, I already have a demo project that works almost exactly as I want but I feel that it should be simpler and cleaner than what I did. If your suggestion does not work as expected, I will post my demo project so everybody can review it. – Edu Zamora Dec 31 '12 at 18:27

3 Answers3

1

You probably want to set a OnTouchListener(in the getView method) on the row View. That way you'll see the MotionEvent for the first touch(the MotionEvent.ACTION_DOWN action) and can set the selection:

    private OnTouchListener mTouchListener = new OnTouchListener() {

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            if (event.getAction() == MotionEvent.ACTION_DOWN) {
                v.setBackgroundColor(Color.RED); // set the selection
            }
            // don't forget to store this new status so the adapter will see
            // it. Otherwise you'll have some problems when the list will be
            // scrolled.
            return false;
        }
    };
user
  • 86,916
  • 18
  • 197
  • 190
  • For the actual highlighting and removing the highlight it works great! The only thing that is not working as intended is that when you scroll the list with your finger, the touched list item changes its background, which should not happen. I think I am going to try to use a GestureDetector on the list, and see how that works. – Edu Zamora Dec 21 '12 at 05:32
0

I finally solved that issue implementing my own list (PressListView) extending ListView. PressListView has a callback to notify the user when an item on the list has been pressed.

Knowing when the items are pressed (which was the most difficult part), the only thing left is handling in a proper way the backgrounds of the item, which you can do in your own Adapter using selectors.

For those interested on seeing this fully working, take a look at my demo project.

You can launch the CAB performing a long click in an item, and once in CAB you can test the highlighting of items, both with touches and D-pad/trackpad.

Although my solution works as I wanted, it is still slower than the list in the Tasks app, when changing the state of the items. If you try to select two items really fast, most of the times it will not select one of the items on my example, but it will in Tasks. If someone knows what it can be, I would be extremely grateful!

Edu Zamora
  • 1,492
  • 1
  • 19
  • 30
0

Implement ActionMode on your ListView (see second link below). In ActionMode, the ListView keeps track of the item checked state automatically when the user clicks on an item. When you are using an adapter for your ListView, you set the background of an item based on the checked state:

@Override
public void bindView(final View view, final Context context, final Cursor cursor)
{
    int pos = cursor.getPosition();

    boolean selected = ((SessionsActivity)context).listView.isItemChecked(pos);
    if(!selected)
        view.setBackgroundResource(R.drawable.list_selector);
    else
        view.setBackgroundResource(R.drawable.list_selector_active);
...

AND, you also need to invalidate the ListView, after each item click:

 private AbsListView.MultiChoiceModeListener multiChoiceModeListener = new AbsListView.MultiChoiceModeListener()
    {
        @Override
        public void onItemCheckedStateChanged(ActionMode mode, int position,
                                              long id, boolean checked)
        {
            // Here you can do something when items are selected/de-selected,
            // such as update the title in the CAB
            listView.invalidateViews();
        }

See https://stackoverflow.com/a/50639298/2477937 and https://medium.com/over-engineering/using-androids-actionmode-e903181f2ee3

Alexander Roehnisch
  • 675
  • 1
  • 7
  • 10
  • Alexander, while this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. [Answers that are little more than a link may be deleted.](//stackoverflow.com/help/deleted-answers) – 4b0 Jun 01 '18 at 08:46
  • @Shree Ok, did it – Alexander Roehnisch Jun 01 '18 at 09:03