22

I have a ListView that opens another activity when an item row is clicked via a onItemClick listener.

I would like that row to stay in its pressed state from the time it is clicked to the time the screen switches to a new activity. I think this would be a clearer experience for the user and you see this kind of thing with most buttons that open/close dialogs or switch activities.

I tried setting view.setPressed(true) in the onItemClick() listener but it seems to get called just a moment after the press state changed back to normal because it flickers slightly.

For example:

mListView.setOnItemClickListener(new OnItemClickListener() {

   @Override
   public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
       view.setPressed(true);
       //start an activity
   }
});

That code almost works except for the flicker (User presses the list item and it turns to its pressed state, then user lets go (completing the click) and it turns back to its normal state for a split second before turning back to the pressed state from the setPressed(true) call)

Any ideas?

Thanks

Edit: I should mention that I am using an xml drawable selector to define the normal, pressed, selected, etc states for the background of the list.

Rudolf Meijering
  • 1,559
  • 1
  • 14
  • 20
cottonBallPaws
  • 21,220
  • 37
  • 123
  • 171

7 Answers7

27

I had the exact same problem to much frustration!

It is, however, easily solved by substituting state_pressed with state_selected. The pressed state will still change rapidly before onItemClick() is called, but since your theme does not depend on state_pressed the flicker won't be visible. Once in onItemClick() you set state_selected and the item will stay selected without any flicker occurring.

Here's my selector:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Selected Item -->
    <item android:state_selected="true"
        android:drawable="@color/list_pressed" />

    <!-- Default Item -->
    <item android:state_selected="false"
        android:drawable="@android:color/list_default" />
</selector>

And my onListItemClick(...):

@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
    super.onListItemClick(l, v, position, id);
    v.setSelected(true);
    //TODO: add further actions here
}
Rudolf Meijering
  • 1,559
  • 1
  • 14
  • 20
  • I finally got around to testing this and it does in fact work! Thanks for sharing your answer months later. – cottonBallPaws Nov 07 '11 at 23:22
  • No problem, hopefully this will save someone else the struggle we had to go through :) – Rudolf Meijering Nov 09 '11 at 20:25
  • @Rudolf: what you have set at list_pressed and android:color/list_default? As i got error at that line. – Shreyash Mahajan Jan 21 '12 at 07:42
  • Please help me for this:http://stackoverflow.com/questions/8894780/how-to-remain-the-listview-index-selected-and-highlighted/8909570#8909570 – Shreyash Mahajan Jan 21 '12 at 07:49
  • @iDroidExplorer: That is just the name of a colour I defined for my particular theme. You can define your own colour by looking here: http://developer.android.com/guide/topics/resources/more-resources.html#Color – Rudolf Meijering Feb 10 '12 at 07:51
  • hi! I have the same problem above. My question is I can't call super.onItemClicked(...); – jayellos Feb 22 '12 at 12:14
  • @marvz don't call super.onItemClicked change it to **super.onListItemClick** – Rudolf Meijering May 04 '12 at 11:28
  • 1
    @RudolfMeijering I got a problem with this. It works fine for one but when we select the second item from the list view, it shows me two items selected. – Rohit Dec 17 '12 at 12:42
  • @Rohit Unfortunately I cannot remember enough of the implementation to help you off the bat. Perhaps create a new question and post your current code. Put a link in the comments and I'll have a look. – Rudolf Meijering Dec 20 '12 at 06:01
  • Scrolling a large list will cause each view to be re-used for different rows. The view that you call setSelected on won't stay in sync with the selected item in the list when you scroll it off the screen. – Jared Kells Oct 18 '13 at 01:28
  • You don't need `state_selected="false"` - it's enough that the previous item is set to `true` with the same key. – AlikElzin-kilaka Feb 24 '14 at 12:22
  • 1
    Do we even HAVE onListItemClick callback?! – Behnam Sep 29 '14 at 14:04
6

The best way you can do this before Android 3.0 is to use a custom state in a custom view or to change your view's background color from the adapter. Starting with Android 3.0 you can use the new state_activated to keep a list item selected.

a.ch.
  • 8,285
  • 5
  • 40
  • 53
Romain Guy
  • 97,993
  • 18
  • 219
  • 200
  • 1
    The target for this includes devices below 3.0, so I'll have to go with one of the former routes. In either of those approaches, is there a built-in method I could hitch a ride on, that would be fired when it needed to change the background color ? (After it is determined that it was an actual click, and before it returns the background to an unpressed state.) Or do I have to get my hands dirty with onTouch methods, which sounds a bit scary in regards to keeping the ListView functioning properly. Thanks – cottonBallPaws Feb 09 '11 at 00:10
3

From inside an Adapter you can reference the view you want to change the state on. Say you have a ListView with a title and a star (ie. favorite star) and you want that to stay "selected/pressed" when touched. Override your getView and do something like:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    final View view = super.getView(position, convertView, parent);
    final ImageButton favorite = (ImageButton) view.findViewById(R.id.favorite);
    favorite.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            Log.d(TAG, "Star pressed...");
            v.setSelected(!v.isSelected());
        }
    });
    return view;
}

And your ImageButton layout should reference a drawable like this:

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_selected="true" android:drawable="@android:drawable/star_big_on" />
    <item android:state_pressed="true" android:drawable="@android:drawable/star_big_on" />
    <item android:drawable="@android:drawable/star_big_off" />
</selector>
slott
  • 3,266
  • 1
  • 35
  • 30
2

You are using xml for selector. check whether you have correctly given the state.
While you set view.setPressed(true) it will use the drawable that is set for the state android:state_pressed="true" like

<item android:state_pressed="true"   
    android:drawable="@color/gray" />
Labeeb Panampullan
  • 34,521
  • 28
  • 94
  • 112
  • The state works fine, it is a matter of finding a way to get it to stay in the pressed state (or at least look like the pressed state) after it has been successfully "pressed" – cottonBallPaws Feb 07 '11 at 16:59
  • I'm sorry, on mistake, now corrected, check my edit, make sure that you have made this state_pressed="true" only event in your xml – Labeeb Panampullan Feb 08 '11 at 04:28
0

Have you already tried to use unscheduleDrawable in combination with drawableStateChanged

So you could maybe listen if the state changed and then immediately unschedule and set it back to pressed.

Maybe there isn't flickering anymore.

  • That seems like it could be helpful, but I guess I'm not sure I would know when to call it, since a press doesn't always mean it has been "clicked" because it might turn into a long press or a canceled press. – cottonBallPaws Feb 06 '11 at 01:30
  • Hmm yes thats true. And you only now that it is a long press when the long press is already done. Then you would have to listen to longpress to and set background back again. –  Feb 06 '11 at 01:34
  • Just another option.. What if you use something like an animation or something like that so that it is at least not flickering but "smoothly" blending? –  Feb 06 '11 at 01:34
  • @littleFluffyKitty just to notice you ;) –  Feb 06 '11 at 01:34
0

In this example the background is changed after the button is pressed in a list item. http://androidforbeginners.blogspot.com/2010/03/clicking-buttons-in-listview-row.html

You can do a similar thing in onItemSelected() by manipulating View which has been passed to this call. Usually the view passed is a LinearLayout which contains children views such as textView.

Lumis
  • 21,517
  • 8
  • 63
  • 67
  • That changes the background of the list row on the onClick of the button. However, I am trying to accomplish this with the onClick of the list row itself. That still has the some issue where it uses onClick, which fires after the state of the press has already returned back to normal. – cottonBallPaws Feb 07 '11 at 02:23
0

Do not use the selector XML. Instead change the list item images manually. This way you will be able to control the duration of a particular image

Kshitij Aggarwal
  • 5,287
  • 5
  • 34
  • 41