次の問題をどのように処理するのか不思議に思っています。結果は、2つのスピナーの選択されたアイテムに応じて計算されます。 UIの処理、つまりユーザーがスピナーの1つで新しいアイテムを選択するために、アクティビティのonCreate()
メソッドのスピナーにsetOnItemSelectedListener
を使用してリスナーをインストールします。
さて、もちろん、それはうまくいきます。リスナーの仕事は、結果の新しい計算をトリガーすることです。
問題:onPause()
onResume()
をインターセプトして最後の状態を保存/復元するため、プログラムで次のようにプログラムでsetsこれらの2つのスピナーの選択されたアイテムをプログラムで取得しましたここに:
startSpinner.setSelection(pStart);
destSpinner.setSelection(pDest);
これらの2つの呼び出しもリスナーを呼び出します!結果と新しい結果セットの通知の計算方法は、ここで2回呼び出されます。
このための愚かな直接的なアプローチは、ブール変数disablingがリスナーの内部で行うことは何でも、選択された項目を設定する前に設定し、後でそれをリセットすることです。はい。しかし、より良い方法はありますか?
リスナーがコードによって呼び出されることを望まない-アクション、ユーザーアクションによってのみ! :-(
どうやってやるの?ありがとう!
私はより簡単で、良い解決策があると思います。初期化後でもスピナーを更新する必要があったので、これはより一般的な方法です。受け入れられた答えを参照してください:
私の考えでは、プログラムによる変更とユーザーが開始した変更を区別するためのよりクリーンなソリューションは次のとおりです。
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);
このように、初期化または再初期化によるハンドラーメソッドへの予期しない呼び出しはすべて無視されます。
さて、私はそれを今望む方法で機能させました。
ここで理解しておくべきこと(そして、私がその質問を書いているときに私が理解しなかったこと...)は、Android内のすべてが1つのスレッド-UIスレッドで実行されることです。
意味:スピナーの値をあちこちに設定しても、それらは(視覚的に)のみ更新されますandリスナーは呼び出されるだけですafter現在使用しているすべてのメソッド(onCreate
など) 、onResume
など)が終了します。
これにより、次のことが可能になります。
currentPos1
_、_currentPos2
_など)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つで新しい値を選択すると、それに応じて更新が実行されます。
それでおしまい! :-)
Ahh-もう1つ:私の質問への答えは次のとおりです。いいえ。リスナーを(簡単に)無効にすることはできず、値が変更されるたびに呼び出されます。
まず、スピナーリスナーの呼び出しを停止するためのブール値を追加します
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
}
});
サーバーコールを複数回停止するのに便利な機能です。
false
を指定してSpinner.setSelection(int position, boolean animate)
メソッドを呼び出すのは非常に簡単なので、リスナーは変更に反応しません。
Spinner.setSelection(int position、boolean animate)は4.3でリスナーをトリガーします
Spinnerで項目のonClickアクションを呼び出す必要がない、すべてに役立つライブラリを作成しました。次に例を示します。
spinner.setSelection(withAction,position);
ここで、withActionはブールフラグであり、呼び出しまたは非アイテムアクションに使用されます
Github上のリンク: https://github.com/scijoker/spinner2
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を設定することが重要です。
スピナーごとにOnItemSelectedListener
を追加しますafteronResume
に以前の値を設定しました。
私の解決策はとても簡単です。まず、グローバルブール変数を初期化します。
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);
私の場合、スピナーをプログラムでトリガーしているため、以下のように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
}
}
}
This following method will help you to stop invoking automatically the selection listener
yourspinnerobj.post(new Runnable() {
@Override
public void run() {
yourspinnerobj.setOnItemSelectedListener(yourspinnerlistener);
}
});