web-dev-qa-db-ja.com

コードで選択された項目を設定するときに呼び出されるonItemSelectedListenerを無効にする方法

次の問題をどのように処理するのか不思議に思っています。結果は、2つのスピナーの選択されたアイテムに応じて計算されます。 UIの処理、つまりユーザーがスピナーの1つで新しいアイテムを選択するために、アクティビティのonCreate()メソッドのスピナーにsetOnItemSelectedListenerを使用してリスナーをインストールします。

さて、もちろん、それはうまくいきます。リスナーの仕事は、結果の新しい計算をトリガーすることです。

問題:onPause()onResume()をインターセプトして最後の状態を保存/復元するため、プログラムで次のようにプログラムでsetsこれらの2つのスピナーの選択されたアイテムをプログラムで取得しましたここに:

startSpinner.setSelection(pStart);
destSpinner.setSelection(pDest);

これらの2つの呼び出しもリスナーを呼び出します!結果と新しい結果セットの通知の計算方法は、ここで2回呼び出されます。

このための愚かな直接的なアプローチは、ブール変数disablingがリスナーの内部で行うことは何でも、選択された項目を設定する前に設定し、後でそれをリセットすることです。はい。しかし、より良い方法はありますか?

リスナーがコードによって呼び出されることを望まない-アクション、ユーザーアクションによってのみ! :-(

どうやってやるの?ありがとう!

33
Zordid

私はより簡単で、良い解決策があると思います。初期化後でもスピナーを更新する必要があったので、これはより一般的な方法です。受け入れられた答えを参照してください:

不要なonItemSelected呼び出し

6
Vedavyas Bhat

私の考えでは、プログラムによる変更とユーザーが開始した変更を区別するためのよりクリーンなソリューションは次のとおりです。

OnTouchListenerとOnItemSelectedListenerの両方として、スピナーのリスナーを作成します。

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

}

両方のイベントタイプを登録するスピナーにリスナーを追加します

SpinnerInteractionListener listener = new SpinnerInteractionListener();
mSpinnerView.setOnTouchListener(listener);
mSpinnerView.setOnItemSelectedListener(listener);

このように、初期化または再初期化によるハンドラーメソッドへの予期しない呼び出しはすべて無視されます。

45
Andres Q.

さて、私はそれを今望む方法で機能させました。

ここで理解しておくべきこと(そして、私がその質問を書いているときに私が理解しなかったこと...)は、Android内のすべてが1つのスレッド-UIスレッドで実行されることです。

意味:スピナーの値をあちこちに設定しても、それらは(視覚的に)のみ更新されますandリスナーは呼び出されるだけですafter現在使用しているすべてのメソッド(onCreateなど) 、onResumeなど)が終了します。

これにより、次のことが可能になります。

  • 選択した位置をフィールド変数に保持します。 (_currentPos1_、_currentPos2_など)
  • リスナーonItemSelectedListener()refreshMyResult()などのメソッドを呼び出します。
  • プログラムで位置を設定する場合は、スピナーを設定しますand直後に独自の更新メソッドを手動で呼び出します。

refreshMyResult()メソッドは次のようになります。

_int newPos1 = mySpinner1.getSelectedItemPosition();
int newPos2 = mySpinner2.getSelectedItemPosition();
// only do something if update is not done yet
if (newPos1 != currentPos1 || newPos2 != currentPos2) {
    currentPos1 = newPos1;
    currentPos2 = newPos2;

    // do whatever has to be done to update things!

}
_

リスナーは後で呼び出されます-そのときまでに、currentPosの記憶された位置はすでに更新されています-何も起こらず、他の不要な更新は行われません。ユーザーがスピナーの1つで新しい値を選択すると、それに応じて更新が実行されます。

それでおしまい! :-)

Ahh-もう1つ:私の質問への答えは次のとおりです。いいえ。リスナーを(簡単に)無効にすることはできず、値が変更されるたびに呼び出されます。

11
Zordid

まず、スピナーリスナーの呼び出しを停止するためのブール値を追加します

  Boolean check = false;

次に、Touchリスナーを追加し、Itemをクリックして、以下のコードのようにリスナーをクリックします

 holder.filters.setOnTouchListener(new View.OnTouchListener() {
               @Override
               public boolean onTouch(View v, MotionEvent event) {

                   check = true;
                   return false;
               }
           });

           holder.filters.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener()
           {

               @Override
               public void onItemSelected(AdapterView<?> parent, View arg1, int position, long id)
               {
                   flag = filterids.get(position);

                   if(check)
                   {
                       check = false;
                       new Applyfilters().execute(flag,"3");
                   }else{

                   }

               }

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

サーバーコールを複数回停止するのに便利な機能です。

3
Venkatesh

falseを指定してSpinner.setSelection(int position, boolean animate)メソッドを呼び出すのは非常に簡単なので、リスナーは変更に反応しません。

2
joe

Spinner.setSelection(int position、boolean animate)は4.3でリスナーをトリガーします

2
P01550n

Spinnerで項目のonClickアクションを呼び出す必要がない、すべてに役立つライブラリを作成しました。次に例を示します。

spinner.setSelection(withAction,position);

ここで、withActionはブールフラグであり、呼び出しまたは非アイテムアクションに使用されます

Github上のリンク: https://github.com/scijoker/spinner2

2
a.black13

Spinner.setSelection(position)を使用すると、常にsetOnItemSelectedListener()がアクティブになります

コードの2回の起動を回避するために、私はこのソリューションを使用します。

private mIsSpinnerFirstCall=true;

...
Spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
        //If a new value is selected (avoid activating on setSelection())
        if(!mIsSpinnerFirstCall) {
            // Your code goes gere
        }
        mIsSpinnerFirstCall = false;
    }

    public void onNothingSelected(AdapterView<?> arg0) {
    }
});

このソリューションは、Spinner.setSelection(position)が使用されていることが確実な場合に有効です。また、Spinner.setSelection(position)を使用する前に、毎回mIsSpinnerFirstCall = trueを設定することが重要です。

1
Ivo Stoyanov

スピナーごとにOnItemSelectedListenerを追加しますafteronResumeに以前の値を設定しました。

1
Graeme Duncan

私の解決策はとても簡単です。まず、グローバルブール変数を初期化します。

boolean shouldWork = true;

次に、onCreate()メソッドで以下のコードを使用します。

Spinner spinner = findViewById(R.id.spinner);
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
    @Override
    public void onItemSelected(AdapterView adapter, View v, int i, long lng) {
        if (shouldWork) {
               // Do your actions here
        }
        else
            shouldWork = true;
    }
    public void onNothingSelected(AdapterView<?> parentView)  {

    }
});

これで、以下のコードでonItemSelected()メソッドを呼び出すことなく、どこでもsetSelectionメソッドを使用できます。

shouldWork = false;
spinner.setSelection(0);
1
Burak

私の場合、スピナーをプログラムでトリガーしているため、以下のようにspinner.performClick()の後にspinnerSelectedフラグを追加する必要があります。

private var spinnerSelected = false

someView.setOnClickListener {
    spinner.performClick()
    spinnerSelected = true
}

spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
    override fun onNothingSelected(parent: AdapterView<*>?) {
        // do nothing
    }

    override fun onItemSelected(
        parent: AdapterView<*>?,
        view: View?,
        position: Int,
        id: Long
    ) {
        if (spinnerSelected) {
            //... do something

            spinnerSelected = false
        }
      }
    }
0
elbert rivas
    This following method will help you to stop invoking automatically the selection listener


    yourspinnerobj.post(new Runnable() {
                @Override
                public void run() {
                    yourspinnerobj.setOnItemSelectedListener(yourspinnerlistener);
                }
            });
0
Jaya