web-dev-qa-db-ja.com

望ましくないonItemSelected呼び出し

36個のスピナーをいくつかの値で初期化しました。それらでonItemSelectedListenerを使用しました。通常どおり、ユーザーはこれらのスピナーを操作して、onItemSeected関数を起動できます。

1つの問題は、呼び出しが初期化中に行われることですが、ここで解決策を見つけ、onItemSelected内のコードを実行する前にグローバル変数「count」を使用してcount> 36かどうかを確認することを避けました。

私の問題はこれです。ユーザーには、「前へ」というボタンをクリックするオプションがあります。このボタンをクリックすると、いくつかのスピナー値をリセットする必要があります。

スピナーをリセットする前にcountの値を0に変更してから、リセット後に37に変更してみましたが、onItemSelectedが呼び出されるのは他のすべての関数の実行が完了した後でのみであるため、AFTER countと呼ばれますスピナーの値は、ユーザーが選択するとすぐに設定されますが、37に戻ります。

OnItemSelected関数を実行せずに、一部のスピナーを繰り返し更新する必要があります。誰かが解決策を見つける手助けをしてくれますか?ありがとう。

19
Vedavyas Bhat

シンプルでエレガントな解決策を見つけました。タグを使用する。最初に「タグ」という新しいXMLファイルを作成し、次のコードを追加しました。

_<resources xmlns:Android="http://schemas.Android.com/apk/res/Android">
  <item name="pos" type="id" />
</resources>
_

私がspin.setSelection(pos)を使用するときはいつでも、spin.setTag(R.id.pos, pos)も使用するため、現在の位置をタグとして設定しています。

次に、onItemSelectedでコードのみif(spin.getTag(R.id.pos) != position)を実行しています。ここで、positionは関数によって提供される位置変数です。このようにして、ユーザーが選択を行っているときにのみ、私のコードが実行されます。ユーザーが選択したのでタグは更新されていないので、処理が終わったらタグをspin.setTag(R.id.pos, position)に更新します。

注:全体を通して同じアダプターを使用することが重要です。そうしないと、「位置」変数が異なる要素を指す可能性があります。

編集:カシウラが指摘したように、複数のタグを使用していない場合は、より単純なバージョン、つまりspin.setTag(pos)spin.getTag()を使用することができます。 XMLファイルの場合。

67
Vedavyas Bhat

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

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

     private Boolean 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) {
        }
    });
16
Ivo Stoyanov

この解決策がここで選択した解決策と同じくらい簡単であるかどうかはわかりませんが、私にとってはうまく機能し、さらに簡単に見えます。

boolean executeOnItemSelected = false;
spinner.setSelection(pos)

そして、OnItemSelectedListener

public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
    if(executeOnItemSelected){
        //Perform desired action
    } else {
        executeOnItemSelected = true;
    }
}
3
user8118328

私がこれを解決した方法は、最初にOnItemSelectedListenerを保存することです。次に、SpinnerのOnItemSelectedListenerをnull値に設定します。コードでSpinnerに項目を設定した後、OnItemSelectedListenerを再度復元します。これでうまくいきました。

以下のコードを参照してください:

        // disable the onItemClickListener before changing the selection by code. Set it back again afterwards
        AdapterView.OnItemSelectedListener onItemSelectedListener = historyPeriodSpinner.getOnItemSelectedListener();
        historyPeriodSpinner.setOnItemSelectedListener(null);
        historyPeriodSpinner.setSelection(0);
        historyPeriodSpinner.setOnItemSelectedListener(onItemSelectedListener);
2
M. Oosterlee

これがこの問題に対する私の解決策です。 AppCompatSpinnerを拡張し、選択コールバックをトリガーせずにプログラムによる選択設定を可能にするメソッドpgmSetSelection(int pos)を追加します。これをRxJavaでコーディングして、選択イベントがObservable経由で配信されるようにしました。

_package com.controlj.view;

import Android.content.Context;
import Android.util.AttributeSet;
import Android.view.View;
import Android.widget.AdapterView;

import io.reactivex.Observable;

/**
 * Created by clyde on 22/11/17.
 */

public class FilteredSpinner extends Android.support.v7.widget.AppCompatSpinner {
    private int lastSelection = INVALID_POSITION;


    public void pgmSetSelection(int i) {
        lastSelection = i;
        setSelection(i);
    }

    /**
     * Observe item selections within this spinner. Events will not be delivered if they were triggered
     * by a call to setSelection(). Selection of nothing will return an event equal to INVALID_POSITION
     *
     * @return an Observable delivering selection events
     */
    public Observable<Integer> observeSelections() {
        return Observable.create(emitter -> {
            setOnItemSelectedListener(new OnItemSelectedListener() {
                @Override
                public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
                    if(i != lastSelection) {
                        lastSelection = i;
                        emitter.onNext(i);
                    }
                }

                @Override
                public void onNothingSelected(AdapterView<?> adapterView) {
                    onItemSelected(adapterView, null, INVALID_POSITION, 0);
                }
            });
        });
    }

    public FilteredSpinner(Context context) {
        super(context);
    }

    public FilteredSpinner(Context context, int mode) {
        super(context, mode);
    }

    public FilteredSpinner(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public FilteredSpinner(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public FilteredSpinner(Context context, AttributeSet attrs, int defStyleAttr, int mode) {
        super(context, attrs, defStyleAttr, mode);
    }
}
_

FragmentonCreateView()で呼び出されるその使用例:

_    mySpinner = view.findViewById(R.id.history);
    mySpinner.observeSelections()
        .subscribe(this::setSelection);
_

ここで、setSelection()は、囲みビュー内のこのようなメソッドであり、ユーザー選択イベントからObservableを介して、およびプログラムによって他の場所から呼び出されるため、選択を処理するためのロジックは一般的です両方の選択方法に。

_private void setSelection(int position) {
    if(adapter.isEmpty())
        position = INVALID_POSITION;
    else if(position >= adapter.getCount())
        position = adapter.getCount() - 1;
    MyData result = null;
    mySpinner.pgmSetSelection(position);
    if(position != INVALID_POSITION) {
        result = adapter.getItem(position);
    }
    display(result);  // show the selected item somewhere
}
_
1
Clyde