Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spinner's onItemSelected callback called twice after a rotation if non-zero position is selected

When I create my activity, I setup a Spinner, assigning it a listener and an initial value. I know that the onItemSelected callback is called automatically during application initialization. What I find strange is that this happens twice when the device is rotated, causing me some problems that I will have to circumvent someway. This does not happen if the spinner initial selection is zero. I was able to isolate the issue, here's the simplest activity triggering it:

public class MainActivity extends Activity implements OnItemSelectedListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Log.i("Test","Activity onCreate");
    setContentView(R.layout.activity_main);
    ((Spinner)findViewById(R.id.spinner1)).setSelection(2);
    ((Spinner)findViewById(R.id.spinner1)).setOnItemSelectedListener(this);
}
@Override
public void onItemSelected(AdapterView<?> spin, View selview, int pos, long selId)
{
    Log.i("Test","spin:"+spin+" sel:"+selview+" pos:"+pos+" selId:"+selId);
}
@Override
public void onNothingSelected(AdapterView<?> arg0) {}
}

And here's the logcat shown when the application is started and then the device rotated:

    I/Test( 9881): spin:android.widget.Spinner@4052f508 sel:android.widget.TextView@40530b08 pos:2 selId:2
    I/Test( 9881): Activity onCreate
    I/Test( 9881): spin:android.widget.Spinner@40535d80 sel:android.widget.TextView@40538758 pos:2 selId:2
    I/Test( 9881): spin:android.widget.Spinner@40535d80 sel:android.widget.TextView@40538758 pos:2 selId:2

Is this the expected behaviour? Am I missing something?

like image 383
athos Avatar asked Jan 28 '13 11:01

athos


3 Answers

Managed to find a solution in another stackoverflow question:

spinner.post(new Runnable() {
    public void run() {
        spinner.setOnItemSelectedListener(listener);
    }
});
like image 128
Raphael Oliveira Avatar answered Oct 05 '22 10:10

Raphael Oliveira


In general, there seem to be many events that trigger the onItemSelected call, and it is difficult to keep track of all of them. This solution allows you to only respond to user-initiated changes using an OnTouchListener.

Create your listener for the spinner:

public class SpinnerInteractionListener implements AdapterView.OnItemSelectedListener, View.OnTouchListener {

    boolean userSelect = false;

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        userSelect = true;
        return false;
    }

    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
        if (userSelect) { 
            // Your selection handling code here
            userSelect = false;
        }
    }

}

Add the listener to the spinner as both an OnItemSelectedListener and an OnTouchListener:

SpinnerInteractionListener listener = new SpinnerInteractionListener();
mSpinnerView.setOnTouchListener(listener);
mSpinnerView.setOnItemSelectedListener(listener);
like image 26
Andres Q. Avatar answered Oct 05 '22 10:10

Andres Q.


Just use setSelection(#, false) before setting the listener:

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    spinner.setSelection(2, false);
    spinner.setOnItemSelectedListener(this);
}

The key is the second parameter. It says to not animate the transition, executing the action immediately and preventing onItemSelected from being fired twice, because a call is already made by the system.

like image 29
One Code Man Avatar answered Oct 05 '22 09:10

One Code Man