web-dev-qa-db-ja.com

ゼロ以外の位置が選択されている場合、回転後に2回呼び出されるスピナーのonItemSelectedコールバック

アクティビティを作成するときに、スピナーをセットアップし、リスナーと初期値を割り当てます。 onItemSelectedコールバックは、アプリケーションの初期化中に自動的に呼び出されることを知っています。私が奇妙だと思うのは、これがデバイスを回転させたときに起こることですtwice、私が何らかの方法で回避しなければならないいくつかの問題を引き起こします。これはnotスピナーの初期選択がゼロの場合に発生します。問題を特定することができました。これが、問題を引き起こす最も簡単なアクティビティです。

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

そして、アプリケーションが起動してからデバイスが回転したときに表示されるログキャットは次のとおりです。

    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

これは予想される動作ですか?私は何かが足りないのですか?

29
athos

別のstackoverflowの質問で解決策を見つけることができました:

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

一般に、onItemSelected呼び出しをトリガーするイベントは多数あるようであり、それらすべてを追跡することは困難です。このソリューションでは、OnTouchListenerを使用してユーザーが開始した変更にのみ応答できます。

スピナーのリスナーを作成します。

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;
        }
    }

}

リスナーをOnItemSelectedListenerとOnTouchListenerの両方としてスピナーに追加します。

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

リスナーを設定する前に、setSelection(#、false)を使用するだけです。

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

キーは2番目のパラメーターであり、遷移をアニメーション化せず、アクションをすぐに実行し、onCreateで呼び出されたときにonItemSelectedが2回起動されないようにします。

3
One Code Man

onItemSelectedを初めて実行するとき、viewはまだ膨張していません。 2回目はすでに膨らんでいます。解決策は、onItemSelected内のメソッドをif (view != null)でラップすることです。

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

    }
}

これは私がしたことです:

ローカル変数を実行する

Boolean changeSpinner = true;

SaveInstanceMethodで、スピナーの選択したアイテムの位置を保存します

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

次に、作成されたアクティビティで、savedInstanceStateからそのintを取得し、intが!= 0の場合、ブール変数をfalseに設定します。

@Override
    public void onActivityCreated(Bundle savedInstanceState) {

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

}

そして最後に、スピナーからのOnItemSelectedでこれを行います

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

したがって、最初に呼び出されたときは何も実行されません。ブール変数をtrueにすると、2回目はコードが実行されます。おそらく最善の解決策ではありませんが、それは機能します。

2
DejaVu

Kotlinで@AndresQ。の回答を更新しています。

Spinnerを使用している内部クラスを作成します

_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
    }
_

次に、onCreate()の外部のインスタンス変数をグローバルのように宣言します

_lateinit var spinnerInteractionListener: SpinnerInteractionListener
_

次に、onCreate()内で初期化します。

_spinnerInteractionListener = SpinnerInteractionListener()
_

のように使用します

_spinnerCategory.onItemSelectedListener = spinnerInteractionListener
spinnerCategory.setOnTouchListener(spinnerInteractionListener)
_

ここでspinnerCategoryスピナー

0
Kishan Solanki

これを試して:

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

アイテムのリストと選択する位置がわかったら、setSelectionを呼び出すだけで、onItemSelectedが2回呼び出されるのを防ぐことができます。

私はより良いアプローチだと思うものについての記事を作成しました スピナーで2回呼び出されるonItemSelectedを回避する方法

0
jamesbluecrow