Android Spinner:初期化中のonItemSelected呼び出しを回避


156

とを使用してAndroidアプリケーションを作成しましSpinnerTextView。TextViewのSpinnerのドロップダウンリストから選択したアイテムを表示したい。onCreateメソッドにスピナーを実装したので、プログラムを実行しているときに、TextView(ドロップダウンリストから項目を選択する前に)に値が表示されます。

ドロップダウンリストから項目を選択した後でのみ、TextViewに値を表示します。どうすればよいですか?

これが私のコードです:

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.TextView;

public class GPACal01Activity extends Activity implements OnItemSelectedListener {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        Spinner spinner = (Spinner) findViewById(R.id.noOfSubjects);

        // Create an ArrayAdapter using the string array and a default spinner layout
        ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this,R.array.noofsubjects_array, android.R.layout.simple_spinner_item);
        // Specify the layout to use when the list of choices appears
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        // Apply the adapter to the spinner
        spinner.setAdapter(adapter);
        spinner.setOnItemSelectedListener(this);
    }

    public void onItemSelected(AdapterView<?> parent, View arg1, int pos,long id) {
        TextView textView = (TextView) findViewById(R.id.textView1);
        String str = (String) parent.getItemAtPosition(pos);
        textView.setText(str);
    }

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

    }
}

スピナーには常にデフォルトの選択値があります。デフォルトの選択値のないスピナーが必要な場合は、カスタムスピナーを作成するか、空のエントリを持つカスタムスピナーを作成し、getView()メソッドで可視性を変更します。 GONEへの未加工のレイアウト
Houcine

4
なぜこのような迷惑なバグが2018年の終わりにまだ存在するのですか???
user-123

回答:


174
spinner.setOnItemSelectedListener(this); // Will call onItemSelected() Listener.

初めてこれを任意の整数値で処理します

例:最初に取る int check = 0;

public void onItemSelected(AdapterView<?> parent, View arg1, int pos,long id) {
   if(++check > 1) {
      TextView textView = (TextView) findViewById(R.id.textView1);
      String str = (String) parent.getItemAtPosition(pos);
      textView.setText(str);
   }
}

ブール値を使用して、現在の位置と以前の位置を確認することによっても実行できます。こちらをご覧ください


2
素晴らしい男..最初にこの問題に直面したとき、カスタムスピナーを実装しようとしましたが、それでもうまくいきませんでした。
Sash_KP 2014

1
小切手をどこで宣言しますか?getView()の外?viewHolderの内部?どこ?あなたの解決策を試しましたが、私にとってはうまくいきません。
user3718908

9
そのパッチは私が推測する解決策ではありません。
saksham

1
私も同じ問題に直面しています。なんらかのバグですか?また、同じアイテムを繰り返し選択すると、最初からリスナーが機能しません。選択項目が変更された場合のみ機能します。コメント、助けますか?
アミタバ

1
私は同じ問題でこの解決策を試しましたが、これは1つだけ問題がありました。たとえば、ドロップダウンに2つのアイテムがある場合、最初にスピナーアイテムを選択すると問題が発生し、2回目に試行すると正しく機能しません
Waseem

116

OnItemSelectedListenerを設定する前にこの行を置くだけです

spinner.setSelection(0,false)

7
これがどのように役立つのか、そしてなぜそうするのかを書くとよいでしょう。
user3533716

1
それは役立ちますが、どのようにしたいですか?
AEMLoviji

14
これは、最初に選択を設定してからリスナーを追加したために機能しますが、以前にこの選択をすでに選択しているため、リスナーは呼び出されません。新しい選択だけがリスナーを呼び出します。
Android開発者

4
これsetSelection(int, boolean)setSelectionInt()内部で呼び出されるため機能し、これを呼び出す前にではなく、呼び出した後にリスナーを設定する必要があります。setSelection(int)これはsetNextSelectedPositionInt()内部で呼び出されるため機能しないことに注意してください。これが私をここに導いたものです。
Hai Zhang

3
これは、onCreateView()の間または前に宣言されている場合は機能しません。onItemSelectedが呼び出されます。
アラッシュ

62

APIレベル3以降では、ブール値を持つアクティビティでonUserInteraction()を使用して、ユーザーがデバイスと対話しているかどうかを確認できます。

http://developer.android.com/reference/android/app/Activity.html#onUserInteraction()

@Override
public void onUserInteraction() {
     super.onUserInteraction();
     userIsInteracting = true;
}

私が持っている活動のフィールドとして:

 private boolean userIsInteracting;

最後に、私のスピナー:

      mSpinnerView.setOnItemSelectedListener(new OnItemSelectedListener() {

           @Override
           public void onItemSelected(AdapterView<?> arg0, View view, int position, long arg3) {
                spinnerAdapter.setmPreviousSelectedIndex(position);
                if (userIsInteracting) {
                     updateGUI();
                }
           }

           @Override
           public void onNothingSelected(AdapterView<?> arg0) {

           }
      });

アクティビティを行ったり来たりすると、ブール値はfalseにリセットされます。魅力のように機能します。


3
グッドワンビル..これは、回答として受け入れられたものよりも優れたソリューションだと思います
Ritesh Gune

どこでsetmPreviousSelectedIndexを取得しますか?!?!
TheQ

打ち間違え?:) setPreviousSelectedIndex()
Bill Mote

4
onUserInteractionはActivityメソッドであるため、ネストフラグメントの場合、これは機能しません。他の解決策はありますか?
Chitrang 2017

1
@ErikB nvm、それを考え出した。リスナー内でfalseに設定すると正常に動作します。
シカンデル

20

これは私のために働いた

AndroidでのSpinnerの初期化には問題があります。このパターンによって上記の問題が解決される場合があります。

Spinner.setAdapter();
Spinner.setSelected(false);  // must
Spinner.setSelection(0,true);  //must
Spinner.setonItemSelectedListener(this);

設定アダプターは最初の部分でなければならず、スピナーを初期化するときはonItemSelectedListener(this)が最後になります。上記のパターンにより、スピナーの初期化中にOnItemSelected()が呼び出されない


11

はは...同じ質問があります。initViews()がこのように実行すると、シーケンスがキーになり、リスナーが最後になります。幸運を !

spinner.setAdapter(adapter);
spinner.setSelection(position);
spinner.setOnItemSelectedListener(listener);

グッドワン!以前適用したことは何もうまくいきませんでしたが、これは魅力のように私のために働きました。ありがとう@TreeSouth
Deepika Lalra 2015

11
私にとっては、同じ方法で使用されるspinner.setSelection(position、false)を処理しました。setSelection(position)メソッドを使用すると、リスナーは初期化中に呼び出されました。
Mario Kutlev、2015年

2
@HugoGresse spinner.setSelection(0、false);を呼び出してみてください。。Thingは、すでに選択されているため、この位置の選択を無視します
android developer

8

私の解決策:

protected boolean inhibit_spinner = true;


@Override
        public void onItemSelected(AdapterView<?> arg0, View arg1,
                int pos, long arg3) {

            if (inhibit_spinner) {
                inhibit_spinner = false;
            }else {

            if (getDataTask != null) getDataTask.cancel(true);
            updateData();
            }

        }

7

初期化中にspinner.setOnItemSelectedListener()を呼び出さないようにするには

spinner.setSelection(Adapter.NO_SELECTION, true); //Add this line before setting listener
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {

    }

    @Override
    public void onNothingSelected(AdapterView<?> parent) {

    }
});

6

あなたはこの方法でこれを行うことができます:

AdapterView.OnItemSelectedListener listener = new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
            //set the text of TextView
        }

        @Override
        public void onNothingSelected(AdapterView<?> adapterView) {

        }
    });

yourSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
            yourSpinner.setOnItemSelectedListener(listener);
        }

        @Override
        public void onNothingSelected(AdapterView<?> adapterView) {

        }
    });

最初に、リスナーを作成し、変数コールバックに割り当てます。次に、2番目のリスナーを匿名で作成し、これが初めて呼び出されたときに、リスナーを変更します=]


3

次に、ユーザー操作フラグをonTouchメソッドでtrueに設定しonItemSelected()、選択の変更が処理されるとリセットできます。ユーザーインタラクションフラグはスピナー専用に処理され、目的の動作に影響を与える可能性のあるアクティビティの他のビューでは処理されないため、この解決策をお勧めします。

コードで:

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

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) { 
            userSelect = false;
            // Your selection handling code here
        }
    }

}

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

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

2

複数のスピナーを有効にする同様の単純なソリューションは、onItemSelected(...)の最初の実行時に、AdapterViewをコレクション(アクティビティスーパークラス)に配置することです。次に、実行する前に、AdapterViewがコレクションにあるかどうかを確認します。これにより、スーパークラスで1セットのメソッドが有効になり、複数のAdapterViewsとそのための複数のスピナーがサポートされます。

スーパークラス...

private Collection<AdapterView> AdapterViewCollection = new ArrayList<AdapterView>();

   protected boolean firstTimeThrough(AdapterView parent) {
    boolean firstTimeThrough = ! AdapterViewCollection.contains(parent);
    if (firstTimeThrough) {
       AdapterViewCollection.add(parent);
     }
    return firstTimeThrough;
   }

サブクラス...

public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
      if (! firstTimeThrough(parent)) {
        String value = safeString(parent.getItemAtPosition(pos).toString());
        String extraMessage = EXTRA_MESSAGE;
        Intent sharedPreferencesDisplayIntent = new         Intent(SharedPreferencesSelectionActivity.this,SharedPreferencesDisplayActivity.class);
    sharedPreferencesDisplayIntent.putExtra(extraMessage,value);
    startActivity(sharedPreferencesDisplayIntent);
  }
  // don't execute the above code if its the first time through
  // do to onItemSelected being called during view initialization.

}


2

ブールフィールドを作成する

private boolean inispinner;

アクティビティのoncreate内

    spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
            if (!inispinner) {
                inispinner = true;
                return;
            }
            //do your work here
        }

        @Override
        public void onNothingSelected(AdapterView<?> parent) {

        }
    });


1

最初にsetOnTouchListenerを実行し、次にonTouchでsetOnItemSelectedListenerを実行することで実現できます。

@Override
public boolean onTouch(final View view, final MotionEvent event) {
 view.setOnItemSelectedListener(this)
 return false;
}

これ大好き。ユーザーがビューに触れるたびに新しいリスナーを作成しますが。したがって、最初に作成したリスナーをキャッシュして再利用することを好みます。
ヴラド

1

これは私のために働きました:

    spinner.setSelection(0, false);
    new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                spinner.setOnItemSelectedListener(listener);
            }, 500);

1

アビの答えに基づいて、私はこの簡単なリスナーを作りました

class SpinnerListener constructor(private val onItemSelected: (position: Int) -> Unit) : AdapterView.OnItemSelectedListener {

    private var selectionCount = 0

    override fun onNothingSelected(parent: AdapterView<*>?) {
        //no op
    }

    override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
        if (selectionCount++ > 1) {
           onItemSelected(position)
        }
    }
}

0

同じ問題があり、これは私にとってはうまくいきました:

私には2つのスピナーがあり、初期化中、および他のコントロールとの対話中、またはサーバーからデータを取得した後にそれらを更新します。

これが私のテンプレートです:

public class MyClass extends <Activity/Fragment/Whatever> implements Spinner.OnItemSelectedListener {

    private void removeSpinnersListeners() {
        spn1.setOnItemSelectedListener(null);
        spn2.setOnItemSelectedListener(null);
    }

    private void setSpinnersListeners() {
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                spn1.setOnItemSelectedListener(MyClass.this);
                spn2.setOnItemSelectedListener(MyClass.this);
            }
        }, 1);
    }

    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
        // Your code here
    }

    @Override
    public void onNothingSelected(AdapterView<?> parent) {

    }
}

クラスを開始するときは、リスナーを直接設定するのではなく、setSpinnersListeners()を使用します。

Runnableは、値を設定した直後にスピナーがonItemSelectedを起動するのを防ぎます。

あなたはスピナー(サーバ等の呼び出し後)の使用更新する必要がある場合removeSpinnersListeners()あなたの更新行の前、右、およびsetSpinnersListenersを()右の更新行の後。これにより、更新後にonItemSelectedが起動されなくなります。


0

コード

spinner.setOnTouchListener(new View.OnTouchListener() { 
@Override public boolean onTouch(View view, MotionEvent motionEvent) { isSpinnerTouch=true; return false; }});

holder.spinner_from.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> adapterView, View view, int slot_position, long l) {
                if(isSpinnerTouch)
                {
                    Log.d("spinner_from", "spinner_from");
                    spinnerItemClickListener.onSpinnerItemClickListener(position, slot_position, AppConstant.FROM_SLOT_ONCLICK_CODE);
                }
                else {

                }
            }

            @Override
            public void onNothingSelected(AdapterView<?> adapterView) {

            }
        });

0

私にとって、Abhiのソリューションは、Apiレベル27までうまく機能します。

ただし、Apiレベル28以降では、リスナーの設定時にonItemSelected()が呼び出されないようです。つまり、onItemSelected()が呼び出されることはありません。

したがって、Apiレベルをチェックするための短いifステートメントを追加しました。

public void onItemSelected(AdapterView<?> parent, View arg1, int pos,long id) {

            if(Build.VERSION.SDK_INT >= 28){ //onItemSelected() doesn't seem to be called when listener is set on Api 28+
                check = 1;
            }

            if(++check > 1) {
                //Do your action here
            }
        }

それはかなり奇妙で、他の人にもこの問題があるかどうかはわかりませんが、私の場合はうまくいきました。


0

テキストビューをスピナーの上に配置し、サイズと背景をスピナーと同じにしたので、ユーザーがクリックする前の外観をより細かく制御できます。そこでTextViewを使用すると、TextViewを使用して、ユーザーが対話を開始したときにフラグを付けることもできます。

私のKotlinコードは次のようになります。

private var mySpinnerHasBeenTapped = false

private fun initializeMySpinner() {

    my_hint_text_view.setOnClickListener {
        mySpinnerHasBeenTapped = true //turn flag to true
        my_spinner.performClick() //call spinner click
    }

    //Basic spinner setup stuff
    val myList = listOf("Leonardo", "Michelangelo", "Rafael", "Donatello")
    val dataAdapter: ArrayAdapter<String> = ArrayAdapter<String>(this, android.R.layout.simple_spinner_dropdown_item, myList)
    my_spinner.adapter = dataAdapter

    my_spinner.onItemSelectedListener = object : OnItemSelectedListener {

        override fun onItemSelected(parent: AdapterView<*>?, view: View, position: Int, id: Long) {

            if (mySpinnerHasBeenTapped) { //code below will only run after the user has clicked
                my_hint_text_view.visibility = View.GONE //once an item has been selected, hide the textView
                //Perform action here
            }
        }

        override fun onNothingSelected(parent: AdapterView<*>?) {
            //Do nothing
        }
    }
}

レイアウトファイルは次のようになります。重要な部分は、SpinnerとTextViewが同じ幅、高さ、マージンを共有していることです。

        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <Spinner
                android:id="@+id/my_spinner"
                android:layout_width="match_parent"
                android:layout_height="35dp"
                android:layout_marginStart="10dp"
                android:layout_marginEnd="10dp"
                android:background="@drawable/bg_for_spinners"

                android:paddingStart="8dp"
                android:paddingEnd="30dp"
                android:singleLine="true" />

            <TextView
                android:id="@+id/my_hint_text_view"
                android:layout_width="match_parent"
                android:layout_height="35dp"                
                android:layout_marginStart="10dp"
                android:layout_marginEnd="10dp"
                android:background="@drawable/bg_for_spinners"

                android:paddingStart="8dp"
                android:paddingEnd="30dp"
                android:singleLine="true"
                android:gravity="center_vertical"
                android:text="*Select A Turtle"
                android:textColor="@color/green_ooze"
                android:textSize="16sp" />

        </FrameLayout>

最初のonItemSelected呼び出しを無視して他のソリューションが機能すると確信していますが、それが常に呼び出されると想定するという考えは本当に好きではありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.