wake-up-neo.com

The onItemSelected-Rückruf von Spinner wird nach einer Drehung zweimal aufgerufen, wenn eine Position ungleich Null ausgewählt ist

Wenn ich meine Aktivität erstelle, richte ich einen Spinner ein und weise ihm einen Listener und einen Anfangswert zu. Ich weiß, dass der Rückruf für onItemSelected während der Anwendungsinitialisierung automatisch aufgerufen wird. Was ich merkwürdig finde, ist, dass dies passiert zweimal wenn das Gerät gedreht wird, was zu Problemen führt, die ich irgendwie umgehen muss. Dies geschieht nicht , wenn die anfängliche Auswahl des Drehfelds Null ist. Ich konnte das Problem eingrenzen. Hier ist die einfachste Aktivität, die es ausgelöst hat:

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) {}
}

Und hier ist der Logcat, der angezeigt wird, wenn die Anwendung gestartet und dann das Gerät gedreht wird:

    I/Test( 9881): spin:[email protected] sel:[email protected] pos:2 selId:2
    I/Test( 9881): Activity onCreate
    I/Test( 9881): spin:[email protected] sel:[email protected] pos:2 selId:2
    I/Test( 9881): spin:[email protected] sel:[email protected] pos:2 selId:2

Ist das das erwartete Verhalten? Vermisse ich etwas?

26
athos

Es ist gelungen, eine Lösung in einer anderen Stackoverflow-Frage zu finden:

spinner.post(new Runnable() {
    public void run() {
        spinner.setOnItemSelectedListener(listener);
    }
});
46

Im Allgemeinen scheint es viele Ereignisse zu geben, die den Aufruf onItemSelected auslösen, und es ist schwierig, alle zu verfolgen. Mit dieser Lösung können Sie nur mit einem OnTouchListener auf vom Benutzer eingeleitete Änderungen reagieren.

Erstellen Sie Ihren Listener für den 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;
        }
    }

}

Fügen Sie den Listener sowohl als OnItemSelectedListener als auch als OnTouchListener zum Drehfeld hinzu:

SpinnerInteractionListener listener = new SpinnerInteractionListener();
mSpinnerView.setOnTouchListener(listener);
mSpinnerView.setOnItemSelectedListener(listener);
17
Andres Q.

Wenn die onItemSelected zum ersten Mal ausgeführt wird, ist die view noch nicht aufgeblasen. Das zweite Mal ist es schon aufgeblasen. Die Lösung besteht darin, Methoden in onItemSelected mit if (view != null) zu verpacken.

@Override
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
    if (view != null) { 
        //do things here

    }
}

Das habe ich gemacht:

Mach eine lokale Variable

Boolean changeSpinner = true;

Speichern Sie in der saveInstanceMethod die ausgewählte Elementposition des Drehfelds

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putInt("ItemSelect",mySpinner.getSelectedItemPosition());
}

Holen Sie sich dann für die erstellte Aktivität das int von savedInstanceState und setzen Sie die boolesche Variable auf false, wenn das int! = 0 ist.

@Override
    public void onActivityCreated(Bundle savedInstanceState) {

    if (savedInstanceState!=null) {
        if (savedInstanceState.getInt("ItemSelect")!=0) {
           changeSpinner = false;
        }
    }

}

Und zum Schluss auf dem OnItemSelected vom Spinner dies tun

mySpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
    public void onItemSelected(AdapterView<?> parent,Android.view.View v, int position, long id) {
        if (changeSpinner) {
           [...]
        } else {
           changeSpinner= true;
        }
    });

Wenn also das erste Mal aufgerufen wird, wird nichts getan, nur die boolesche Variable auf true gesetzt, und das zweite Mal wird der Code ausgeführt. Vielleicht nicht die beste Lösung, aber es funktioniert.

2
DejaVu

Verwenden Sie einfach setSelection (#, false), bevor Sie den Listener setzen:

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

Der Schlüssel ist der zweite Parameter, der angibt, den Übergang nicht zu animieren, die Aktion sofort auszuführen und dann zu verhindern, dass onItemSelected zweimal ausgelöst wird, wenn onCreate aufgerufen wird.

2
One Code Man

Ich aktualisiere die Antwort von @Andres Q. in Kotlin.

Erstellen Sie eine innere Klasse, in der Sie Spinner verwenden

inner class SpinnerInteractionListener : AdapterView.OnItemSelectedListener, View.OnTouchListener {
        override fun onNothingSelected(parent: AdapterView<*>?) {

        }

        override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
            if (userSelect) {
                //Your selection handling code here
                userSelect = false
            }
        }

        @SuppressLint("ClickableViewAccessibility")
        override fun onTouch(v: View?, event: MotionEvent?): Boolean {
            userSelect = true
            return false
        }

        internal var userSelect = false
    }

Dann deklarieren Sie die Instanzvariable außerhalb von onCreate() als global wie

lateinit var spinnerInteractionListener: SpinnerInteractionListener

dann initialisiere es in onCreate() von

spinnerInteractionListener = SpinnerInteractionListener()

und benutze es gerne

spinnerCategory.onItemSelectedListener = spinnerInteractionListener
spinnerCategory.setOnTouchListener(spinnerInteractionListener)

hier ist spinnerCategorySpinner

0

Versuche dies:

boolean mConfigChange = false;

@Override
protected void onCreate(Bundle savedInstanceState) {
    // TODO Auto-generated method stub
    mConfigChange = false;
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_mainf);

    Log.i("SpinnerTest", "Activity onCreate");
    ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this, R.array.colors,
            Android.R.layout.simple_spinner_item);
    adapter.setDropDownViewResource(Android.R.layout.simple_spinner_dropdown_item);
    ((Spinner) findViewById(R.id.spin)).setAdapter(adapter);

     ((Spinner) findViewById(R.id.spin)).setSelection(2);
    ((Spinner) findViewById(R.id.spin)).setOnItemSelectedListener(this);

}

@Override
protected void onResume() {
    mConfigChange = true;
    super.onResume();
}

@Override
public void onItemSelected(AdapterView<?> spin, View selview, int pos, long selId) {
    if (!mConfigChange)
        Log.i("Test", "spin:" + spin + " sel:" + selview + " pos:" + pos + " selId:" + selId);
    else
        mConfigChange = false;
}
0

Sie können einfach setSelection anrufen, sobald Sie wissen, dass die Liste der Elemente und die Position ausgewählt werden müssen. Auf diese Weise vermeiden Sie, dass onItemSelected zweimal aufgerufen wird.

Ich habe einen Artikel darüber verfasst, was meiner Meinung nach ein besserer Ansatz ist. Wie vermeide ich, dass onItemSelected in Spinners zweimal aufgerufen wird.

0
jamesbluecrow