Android Spinner : 초기화 중 onItemSelected 호출 방지


157

a Spinner및 a를 사용하여 Android 애플리케이션을 만들었습니다 TextView. TextView의 Spinner 드롭 다운 목록에서 선택한 항목을 표시하고 싶습니다. onCreate프로그램 에서 Spinner를 구현 했으므로 프로그램을 실행할 때 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 () 메서드에서 가시성을 변경해야합니다.
사라지다

5
2018 년 말에도 그러한 성가신 버그가 어떻게 존재합니까?
사용자 -123

답변:


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

따라서 Integer 값으로 처음 처리하십시오.

예 : 처음에 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

1
수표는 어디에서 선언합니까? getView () 외부? 뷰 홀더 안에? 어디? 귀하의 솔루션을 시도했지만 나에게 효과가 없습니다.
user3718908

10
그 해결책은 내가 생각하는 해결책이 아닙니다.
saksham

1
나는 같은 문제에 직면하고있다. 어떤 종류의 버그입니까? 또한 동일한 항목을 반복해서 선택하면 처음에는 리스너가 작동하지 않습니다. 선택 항목이 변경된 경우에만 작동합니다. 의견이나 도움이 필요하십니까?
amitava

1
내 같은 문제로이 솔루션을 시도하지만,이 중국식 팬 하나, 예를 들면 : 나는 드롭 다운에이 개 항목이 때이 제대로 작동하지 않는 2 시간을하려고 할 때 내가 어떤 스피너 항목 1 시간을 선택하면, 그것은 웍
Waseem

117

OnItemSelectedListener 를 설정하기 전에이 줄을 넣으십시오.

spinner.setSelection(0,false)

7
이것이 도움이되는 이유와 이유를 쓰면 더 나은 대답이 될 것입니다.
user3533716

1
도움이되지만 어떻게하고 싶습니까?
AEMLoviji

14
선택을 먼저 설정 한 다음 리스너를 추가하기 때문에 작동하지만 이전에이 선택을 이미 선택했기 때문에 리스너가 호출되지 않습니다. 새로운 선택 만 청취자를 호출합니다.
안드로이드 개발자

4
내부적으로 setSelection(int, boolean)호출 하기 때문에 작동하기 때문에 이것을 호출 setSelectionInt()한 후 (이전에) 대신 리스너를 설정해야합니다. 내부적으로 setSelection(int)전화하기 때문에 작동하지 않는 점에주의 setNextSelectedPositionInt()하십시오.
Hai Zhang

3
onCreateView () 중 또는 이전에 선언 된 경우 작동하지 않습니다. onItemSelected가 호출됩니다.
Arash

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

1
@ErikB nvm, 알아 냈습니다. 리스너 내에서 false로 설정하면 정상적으로 작동합니다.
Sikander

20

이것은 나를 위해 일했다

안드로이드에서 스피너의 초기화는 문제가 될 수 있으며 때로는 위의 문제가이 패턴으로 해결되었습니다.

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

11
나를 위해 spinner.setSelection (position, false)을 같은 방식으로 사용했습니다. setSelection (position) 메소드를 사용하면 초기화 중에 리스너가 호출되었습니다.
Mario Kutlev

2
@HugoGresse spinner.setSelection (0, false); . 그것은 이미 선택 되었기 때문에이 위치의 선택을 무시할 것입니다.
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) {

        }
    });

처음에는 리스너를 만들고 변수 콜백에 기여했습니다. 그런 다음 익명으로 두 번째 리스너를 만들고 이것이 처음 호출되면 리스너가 변경됩니다.]


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

여러 스피너를 사용할 수있는 유사한 간단한 솔루션은 onViewSelected (...)를 처음 실행할 때 AdapterView를 콜렉션에-활동 수퍼 클래스에 배치 한 다음 실행하기 전에 AdapterView가 콜렉션에 있는지 확인하는 것입니다. 이를 통해 수퍼 클래스에서 한 세트의 메소드를 사용할 수 있으며 여러 AdapterView 및 여러 스피너를 지원합니다.

슈퍼 클래스 ...

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;

활동을 만들 때

    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

Abhi의 답변을 바탕 으로이 간단한 청취자를 만들었습니다.

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을 사용하면 값을 설정 한 직후에 항목 선택 항목에서 스피너가 실행되지 않습니다.

서버 호출 후 스피너를 업데이트해야하는 경우 업데이트 라인 바로 앞에 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

Spinner와 동일한 크기와 배경으로 TextView를 Spinner와 함께 배치하여 사용자가 클릭하기 전에 모양을 더 잘 제어 할 수있었습니다. 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.