Android Spinner selection

2019-01-13 16:30发布

The OnItemSelectedListener event handler gets called both when a spinner selection is changed programmatically, and when a user physically clicks the spinner control. Is is possible to determine if an event was triggered by a user selection somehow?

Or is there another way to handle spinner user selections?

8条回答
一纸荒年 Trace。
2楼-- · 2019-01-13 16:33

In the past I've done things like this to distinguish

internal++; // 'internal' is an integer field initialized to 0
textBox.setValue("...."); // listener should not act on this internal setting
internal--;

Then in textBox's listener

if (internal == 0) {
  // ... Act on user change action
}

I use ++ and -- rather than setting a boolean value to 'true' so that there is no worry when methods nest other methods that might also set the internal change indicator.

查看更多
在下西门庆
3楼-- · 2019-01-13 16:37

I know this is pretty late, but I came up with a very simple solution to this. It is based on Arhimed's answer, it's exactly the same. It's very easy to implement too. Refer the accepted answer:

Undesired onItemSelected calls

查看更多
\"骚年 ilove
4楼-- · 2019-01-13 16:41

I did some logging and discovered that it only ever gets called on initialize, which is annoying. Can't see the need for all this code, I just created an instance variable that was initialised to a guard value, and then set it after the method had been called the first time.

I logged when the onItemSelected method was being called it was otherwise only being called once.

I had a problem where it was creating two of something and realised it was because I was calling add() on my custom adapter, which already had a reference to the list I was referencing and had added to outside the adapter. After I realised this and removed the add method the problem went away.

Are you guys sure you need all this code?

查看更多
男人必须洒脱
5楼-- · 2019-01-13 16:42

To workaround you need to remember the last selected position. Then inside of your spinner listener compare the last selected position with the new one. If they are different, then process the event and also update the last selected position with new position value, else just skip the event processing.

If somewhere within the code you are going to programatically change spinner selected position and you don't want the listener to process the event, then just reset the last selected position to the one you're going to set.

Yes, Spinner in Android is painful. I'd even say pain starts from its name - "Spinner". Isn't it a bit misleading? :) As far as we're talking about it you should also be aware there's a bug - Spinner may not restore (not always) its state (on device rotation), so make sure you handle Spinner's state manually.

查看更多
你好瞎i
6楼-- · 2019-01-13 16:47

Just to expand on aaamos's post above, since I don't have the 50 rep points to comment, I am creating a new answer here.

Basically, his code works for the case when the initial Spinner selection is 0. But to generalize it, I amended his code the following way:

@Override
public void setOnItemSelectedListener(final OnItemSelectedListener listener)
{
    if (listener != null)
        super.setOnItemSelectedListener(new OnItemSelectedListener()
        {
            private static final int NO_POSITION  = -1;

            private int lastPosition = NO_POSITION;


            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id)
            {
                if ((lastPosition != NO_POSITION) && (lastPosition != position))
                    listener.onItemSelected(parent, view, position, id);

                lastPosition = position;
            }


            @Override
            public void onNothingSelected(AdapterView<?> parent)
            {
                listener.onNothingSelected(parent);
            }
        });
    else
        super.setOnItemSelectedListener(null);
}

Basically, this code will ignore the very first firing of onItemSelected(), and then all subsequent "same position" calls.

Of course, the requirement here is that the selection is set programatically, but that should be the case anyhow if the default position is not 0.

查看更多
家丑人穷心不美
7楼-- · 2019-01-13 16:53

I had this situation lately when using spinners and the internet didn't came up with a suitable solution.

My application scenario:

X spinners (dynamically, 2 for each cpu, min & max) for setting & viewing the CPU-Frequency. They are filled when the application starts and they also get the current max/min freq of the cpu set. A thread runs in the background and checks for changes every second and updates the spinners accordingly. If a new frequency inside the spinner is set by the user the new frequency is set.

The issue was that the thread accessed setSelection to update the current frequency which in turn called my listener and I had no way of knowing if it was the user or the thread that changed the value. If it was the thread I didn't want the listener to be called since there would have been no need to change the frequency.

I came up with a solution that suits my needs perfectly and works around the listener on your call :) (and I think this solution gives you maximal control)

I extended Spinner:

import android.content.Context;
import android.widget.Spinner;

public class MySpinner extends Spinner {
    private boolean call_listener = true;

    public MySpinner(Context context) {
        super(context);
    }

    public boolean getCallListener() {
        return call_listener;
    }

    public void setCallListener(boolean b) {
        call_listener = b;
    }

    @Override
    public void setSelection(int position, boolean lswitch) {
        super.setSelection(position);
        call_listener = lswitch;
    }
}

and created my own OnItemSelectedListener:

import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;

public class SpinnerOnItemSelectedListener implements OnItemSelectedListener {
      public void onItemSelected(AdapterView<?> parent, View view, int pos,long id) {
          MySpinner spin = (MySpinner) parent.findViewById(parent.getId());
          if (!spin.getCallListener()) {
              Log.w("yourapptaghere", "Machine call!");
              spin.setCallListener(true);
          } else {
              Log.w("yourapptaghere", "UserCall!");
          }
      }

      @Override
      public void onNothingSelected(AdapterView<?> arg0) {
        // TODO Auto-generated method stub
      }
}

If you now create a MySpinner you can use this to set the selection:

setSelection(position, callListener);

Where callListener is either true or false. True will call the listener and is default, which is why user interactions are getting identified, false will also call the listener but uses code you want for this special case, exempli gratia in my case: Nothing.

I hope that someone else finds this useful and is spared a long journey to look if something like this already exists :)

查看更多
登录 后发表回答