35

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

さて、もちろん、それは問題なく機能します。リスナーの作業は、結果の新しい計算をトリガーすることです。

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

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

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

これに対する愚かな直接的なアプローチは、ブール変数を使用して、リスナーが内部で行うことをすべて無効にし、選択した項目を設定する前に設定し、後でリセットすることです。わかった。しかし、もっと良い方法はありますか?

リスナーがコード(アクション)によって呼び出されるのは望ましくありません。ユーザーアクションによってのみ呼び出されます。:-(

どうしますか?ありがとう!

4

12 に答える 12

51

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

スピナーのリスナーを 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);

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

于 2015-02-11T23:52:34.963 に答える
11

わかりました、私は今、私が望むようにそれを機能させました。

ここで理解しておくべきこと (そして、私がその質問を書いていたときはわかりませんでした...) は、Android のすべてが 1 つのスレッド (UI スレッド) で実行されるということです。

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

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

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

メソッドは次の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 つで新しい値を選択すると、それに応じて更新が実行されます。

それでおしまい!:-)

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

于 2010-02-24T14:00:51.693 に答える
2

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

于 2013-07-12T09:08:50.280 に答える
2

Spinner.setSelection(int position, boolean animate) does trigger the listener on 4.3

于 2013-09-05T16:27:52.367 に答える
2

I created a library that help for all, that no need to call item onClick action in Spinner For example:

spinner.setSelection(withAction,position);

where withAction is a boolean flag, that used for call or not item action

Link on Github: https://github.com/scijoker/spinner2

于 2015-10-11T19:55:23.593 に答える
1

で以前の値を設定した後OnItemSelectedListener、 for each スピナーを追加します。onResume

于 2010-02-19T16:39:59.183 に答える
1

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 を設定することが重要です。

于 2014-10-22T11:03:12.437 に答える