새로 인스턴스화 된 스피너에서 onItemSelected가 실행되지 않도록하는 방법은 무엇입니까?


419

나는 이것을 해결할 수있는 우아한 방법보다 덜 생각했지만, 뭔가 빠진 것이 틀림 없다는 것을 안다.

나의 onItemSelected 사용자와의 상호 작용없이 즉시 꺼 화재, 이것은 바람직하지 않은 동작입니다. UI가 사용자가 무언가를 선택할 때까지 기다렸다가 아무것도하지 않기를 바랍니다.

나는 심지어 청취자를 설정하려고 시도했다. onResume() 도움이되기를 기대하면서에 했지만 그렇지 않습니다.

사용자가 컨트롤을 터치하기 전에 발사를 중지하려면 어떻게해야합니까?

public class CMSHome extends Activity { 

private Spinner spinner;

@Override
    public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    // Heres my spinner ///////////////////////////////////////////
    spinner = (Spinner) findViewById(R.id.spinner);
    ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
            this, R.array.pm_list, android.R.layout.simple_spinner_item);
    adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    spinner.setAdapter(adapter);
    };

public void onResume() {
    super.onResume();
    spinner.setOnItemSelectedListener(new MyOnItemSelectedListener());
}

    public class MyOnItemSelectedListener implements OnItemSelectedListener {

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

     Intent i = new Intent(CMSHome.this, ListProjects.class);
     i.putExtra("bEmpID", parent.getItemAtPosition(pos).toString());
        startActivity(i);

        Toast.makeText(parent.getContext(), "The pm is " +
          parent.getItemAtPosition(pos).toString(), Toast.LENGTH_LONG).show();
    }

    public void onNothingSelected(AdapterView parent) {
      // Do nothing.
    }
}
}

2
이 솔루션을 보면 쉽고 실용적입니다. stackoverflow.com/a/10102356/621951
Günay Gültekin

1
간단한 해결책은 첫 번째 항목을 Spinner비워 onItemSelected두는 것입니다. 그러면 문자열이 비어 있지 않은지를 감지 할 수 있습니다 startActivity!
Muhammad Babar

이 패턴은 제대로 작동합니다 stackoverflow.com/questions/13397933/…
saksham

답변:


78

솔루션이 작동 할 것으로 예상했을 것입니다. 리스너를 설정하기 전에 어댑터를 설정하면 선택 이벤트가 실행되지 않습니다.

즉, 간단한 부울 플래그를 사용하면 악성 첫 번째 선택 이벤트를 감지하고 무시할 수 있습니다.


15
그래, 그래 그게 내가 우아한 해결책으로 의미 한 것입니다. 더 좋은 방법이 있어야합니다. 그래도 감사합니다.
FauxReal

5
Dev ml의이 글은 이것에 대한 더 많은 통찰력을 가지고 있습니다 : groups.google.com/group/android-developers/browse_thread/thread/…- 불행히도 해결책은 없습니다 ...
BoD

25
컴포넌트 배치 프로세스는 선택 리스너를 시작합니다. 따라서 레이아웃이 완료된 리스너를 추가해야합니다 . 레이아웃 후 어느 시점에서 일어날 것으로 보인다 나는이 작업을 수행하려면 적절한 간단 장소를 찾을 수 없었습니다 onResume()onPostResume()일반 후크의 모든 레이아웃이 일어나는 시간에 완료 있도록.
Dan Dyer

28
이 부울 플래그를 피하십시오-앞으로 행동이 바뀌면 버그가 발생할 수 있습니다. 더 확실한 해결책은 선택된 첫 번째 항목으로 초기화 된 "현재 선택된 색인"으로 변수를 유지하는 것입니다. 그런 다음 선택 이벤트-새 위치와 같은지 확인하십시오-돌아가서 아무것도하지 마십시오. 물론 선택시 변수를 업데이트하십시오.
daniel.gindi

2
작동하지 않습니다. @casanova works의 답변입니다. 그 대답이 받아 들여 져야합니다.
Siddharth

379

Runnables 사용이 완전히 잘못되었습니다.

setSelection(position, false);초기 선택에서 사용setOnItemSelectedListener(listener)

이 방법으로 선택한 항목 리스너가 호출되게하는 애니메이션없이 선택을 설정합니다. 그러나 리스너가 null이므로 아무것도 실행되지 않습니다. 그런 다음 청취자가 할당됩니다.

따라서이 정확한 순서를 따르십시오.

Spinner s = (Spinner)Util.findViewById(view, R.id.sound, R.id.spinner);
s.setAdapter(adapter);
s.setSelection(position, false);
s.setOnItemSelectedListener(listener);

48
숨겨진 보석 +1! "animate"매개 변수로 false를 전달하면 리스너 콜백이 호출되지 않습니다. 대박!
pkk

3
+1 이상하지만 우아한 해결책 :) 다행히도, 나는 이미 setSelection을 호출해야했다 ...
Martin T.

35
Spinner UI 요소가 어셈블 될 때 리스너는 계속 실행되므로 OP에 의해 설명 된 원치 않는 동작을 막지 않더라도 리스너가 실행됩니다. 이것은 onCreateView () 중 또는 이전에 선언되지 않은 경우 훌륭하게 작동하지만 요청한 것은 아닙니다.
Rudi Kershaw

6
유용하지만 제시된 OP와 다른 문제를 해결합니다. OP는 프로그래머가 setSelection을 설정하지 않아도 뷰가 처음 나타날 때 자동으로 발생하는 선택 이벤트를 나타 냅니다.
ToolmakerSteve

2
setSelection (..) 메소드의 "false"값 매개 변수가 저에게 해결책이었습니다. 타이!
Dani

195

댄 다이어의 대답을 참조하면, 등록하려고 OnSelectListenerA의 post(Runnable)방법 :

spinner.post(new Runnable() {
    public void run() {
        spinner.setOnItemSelectedListener(listener);
    }
});

그렇게함으로써 원하는 행동이 마침내 일어났습니다.

이 경우에도 리스너가 변경된 항목에서만 실행됨을 의미합니다.


1
오류가 발생했습니다 : AdapterView <SpinnerAdapter> 유형의 setOnItemSelectedListener (AdapterView.OnItemSelectedListener) 메소드가 인수 (new Runnable () {})에 적용되지 않는 이유는 무엇입니까?
Jakob

이것은 본질적으로 Runnable과 UI Thread 간의 경쟁 조건을 설정하지 않습니까?
kenny_k

6
@theFunkyEngineer -이 코드는 해야 메인 스레드 방법 예 중 하나에서 실행 onCreate(), onResume()경쟁 조건없이 위험으로,이 경우 등의 환상적인 트릭. 나는 보통 onCreate()레이아웃 코드 직후 에이 트릭을 사용 합니다.
Richard Le Mesurier

1
이것은 훌륭한 솔루션이며 분명히 해킹은 아닙니다! 이러한 종류의 기능은 프레임 워크에서 작업이 수행되는 방식입니다. 스피너가 이것을 내부적으로하지 않는 것은 부끄러운 일입니다. 그러나 이것은 활동 생성 후에 일부 코드가 실행되도록 보장하는 가장 깨끗한 방법입니다. 액티비티가 알리려고 할 때 리스너가 스피너에 아직 설정되어 있지 않기 때문에 작동합니다.
jophde

1
이것은 수용 가능한 솔루션 입니다. 블라인드 샷이 아닙니다. 다른 해결책은 미래에 행동 변화 문제에 더 취약하다.
Kuldeep Singh Dhaka

50

Spinner사용자에게 알리지 않고 선택 을 변경하는 작은 유틸리티 방법을 만들었습니다 .

private void setSpinnerSelectionWithoutCallingListener(final Spinner spinner, final int selection) {
    final OnItemSelectedListener l = spinner.getOnItemSelectedListener();
    spinner.setOnItemSelectedListener(null);
    spinner.post(new Runnable() {
        @Override
        public void run() {
            spinner.setSelection(selection);
            spinner.post(new Runnable() {
                @Override
                public void run() {
                    spinner.setOnItemSelectedListener(l);
                }
            });
        }
    });
}

리스너를 비활성화하고 선택을 변경 한 후 리스너를 다시 활성화합니다.

트릭은 호출이 UI 스레드와 비동기 적이므로 연속 처리기 포스트에서 호출해야합니다.


대박. 나는 여러 스피너를 가지고 값을 설정하기 전에 모든 리스너를 null로 설정하려고 시도했지만 모든 것을 원래대로 설정했지만 작동하지 않는 이유가 있습니다. 대신이 기능을 시도해 보았습니다. 왜 내 것이 작동하지 않았는지 모르겠지만, 작동하지 않습니다. D
JStephen

4
참고 사항 : 전화하면 setSpinnerSelectionWithoutCallingListener 첫 번째 호출자가 이미 리스너를로 설정 한 상태에서 두 번째 호출이 이루어 지도록 두 번 빠르게null 스피너가 null리스너에 영원히 고정 됩니다. 다음 수정을 제안합니다 : add if (listener == null) return;after spinner.setSelection(selection).
Violet Giraffe

34

불행히도이 문제에 대해 가장 일반적으로 제안되는 두 가지 솔루션, 즉 콜백 발생 수를 계산하고 나중에 콜백을 설정하기 위해 Runnable을 게시하면 접근성 옵션이 활성화 된 경우 모두 실패 할 수 있습니다. 이러한 문제를 해결하는 도우미 클래스가 있습니다. 추가 설명은 주석 블록에 있습니다.

import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.Spinner;
import android.widget.SpinnerAdapter;

/**
 * Spinner Helper class that works around some common issues 
 * with the stock Android Spinner
 * 
 * A Spinner will normally call it's OnItemSelectedListener
 * when you use setSelection(...) in your initialization code. 
 * This is usually unwanted behavior, and a common work-around 
 * is to use spinner.post(...) with a Runnable to assign the 
 * OnItemSelectedListener after layout.
 * 
 * If you do not call setSelection(...) manually, the callback
 * may be called with the first item in the adapter you have 
 * set. The common work-around for that is to count callbacks.
 * 
 * While these workarounds usually *seem* to work, the callback
 * may still be called repeatedly for other reasons while the 
 * selection hasn't actually changed. This will happen for 
 * example, if the user has accessibility options enabled - 
 * which is more common than you might think as several apps 
 * use this for different purposes, like detecting which 
 * notifications are active.
 * 
 * Ideally, your OnItemSelectedListener callback should be
 * coded defensively so that no problem would occur even
 * if the callback was called repeatedly with the same values
 * without any user interaction, so no workarounds are needed.
 * 
 * This class does that for you. It keeps track of the values
 * you have set with the setSelection(...) methods, and 
 * proxies the OnItemSelectedListener callback so your callback
 * only gets called if the selected item's position differs 
 * from the one you have set by code, or the first item if you
 * did not set it.
 * 
 * This also means that if the user actually clicks the item
 * that was previously selected by code (or the first item
 * if you didn't set a selection by code), the callback will 
 * not fire.
 * 
 * To implement, replace current occurrences of:
 * 
 *     Spinner spinner = 
 *         (Spinner)findViewById(R.id.xxx);
 *     
 * with:
 * 
 *     SpinnerHelper spinner = 
 *         new SpinnerHelper(findViewById(R.id.xxx))
 *         
 * SpinnerHelper proxies the (my) most used calls to Spinner
 * but not all of them. Should a method not be available, use: 
 * 
 *      spinner.getSpinner().someMethod(...)
 *
 * Or just add the proxy method yourself :)
 * 
 * (Quickly) Tested on devices from 2.3.6 through 4.2.2
 * 
 * @author Jorrit "Chainfire" Jongma
 * @license WTFPL (do whatever you want with this, nobody cares)
 */
public class SpinnerHelper implements OnItemSelectedListener {
    private final Spinner spinner;

    private int lastPosition = -1;
    private OnItemSelectedListener proxiedItemSelectedListener = null;  

    public SpinnerHelper(Object spinner) {
         this.spinner = (spinner != null) ? (Spinner)spinner : null;        
    }

    public Spinner getSpinner() {
        return spinner;
    }

    public void setSelection(int position) { 
        lastPosition = Math.max(-1, position);
        spinner.setSelection(position);     
    }

    public void setSelection(int position, boolean animate) {
        lastPosition = Math.max(-1, position);
        spinner.setSelection(position, animate);        
    }

    public void setOnItemSelectedListener(OnItemSelectedListener listener) {
        proxiedItemSelectedListener = listener;
        spinner.setOnItemSelectedListener(listener == null ? null : this);
    }   

    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
        if (position != lastPosition) {
            lastPosition = position;
            if (proxiedItemSelectedListener != null) {
                proxiedItemSelectedListener.onItemSelected(
                        parent, view, position, id
                );
            }
        }
    }

    public void onNothingSelected(AdapterView<?> parent) {
        if (-1 != lastPosition) {
            lastPosition = -1;
            if (proxiedItemSelectedListener != null) {
                proxiedItemSelectedListener.onNothingSelected(
                        parent
                );
            }
        }
    }

    public void setAdapter(SpinnerAdapter adapter) {
        if (adapter.getCount() > 0) {
            lastPosition = 0;
        }
        spinner.setAdapter(adapter);
    }

    public SpinnerAdapter getAdapter() { return spinner.getAdapter(); } 
    public int getCount() { return spinner.getCount(); }    
    public Object getItemAtPosition(int position) { return spinner.getItemAtPosition(position); }   
    public long getItemIdAtPosition(int position) { return spinner.getItemIdAtPosition(position); }
    public Object getSelectedItem() { return spinner.getSelectedItem(); }
    public long getSelectedItemId() { return spinner.getSelectedItemId(); }
    public int getSelectedItemPosition() { return spinner.getSelectedItemPosition(); }
    public void setEnabled(boolean enabled) { spinner.setEnabled(enabled); }
    public boolean isEnabled() { return spinner.isEnabled(); }
}

3
이것은 가장 높은 투표 응답이어야합니다. 간단하지만 훌륭합니다. 초기화하는 한 줄을 제외하고 모든 현재 구현을 동일하게 유지할 수 있습니다. 오래된 프로젝트를 아주 쉽게 개조 할 수있었습니다. 또한 스피너가 열릴 때 키보드를 닫기 위해 OnTouchLisener 인터페이스를 구현하여 하나의 돌로 두 마리의 새를 죽였습니다. 이제 모든 스피너는 내가 원하는 방식으로 정확하게 동작합니다.
user3829751 2016 년

아름다운 대답. 어댑터에 All ()을 추가 할 때 여전히 0 번째 요소로 트리거되지만 내 0 번째 요소는 중립 (아무것도하지 않음) 동작의 줄임표입니다.
jwehrle

31

원치 않을 때 스피너 발사와 관련된 많은 문제가 있었으며 여기에있는 모든 대답은 신뢰할 수 없습니다. 그들은 때때로 작동합니다. 결국 시나리오가 실패하고 코드에 버그가 생길 수 있습니다.

나를 위해 일한 것은 마지막으로 선택한 인덱스를 변수에 저장하고 리스너에서 평가하는 것이 었습니다. 새로 선택한 색인과 동일한 경우 아무 것도 수행하지 않고 리스너를 계속하십시오. 이 작업을 수행:

//Declare a int member variable and initialize to 0 (at the top of your class)
private int mLastSpinnerPosition = 0;

//then evaluate it in your listener
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {

  if(mLastSpinnerPosition == i){
        return; //do nothing
  }

  mLastSpinnerPosition = i;
  //do the rest of your code now

}

내가 이것을 말할 때 나를 믿어 라. 이것은 지금까지 가장 신뢰할만한 해결책이다. 해킹이지만 작동합니다!


값을 변경하려는 경우에도 작동합니까? 필자의 경우 변경 리스너를 트리거하지 않고 실제로 0 일 때 값을 3과 같이 설정하려고합니다. int i 사용자가 값을 선택하면 다른 값만 반환한다고 말하고 있습니까?
JStephen

안녕하세요 JStephen, 당신이 무슨 뜻인지 100 % 확신하지 못합니다. 그러나 int는 onItemSelected가 트리거 될 때마다 스피너의 위치가됩니다. 문제는 실제 사용자 상호 작용없이 스피너가 처음로드 될 때마다 onItemSelected가 트리거되어이 경우 원치 않는 동작이 발생한다는 것입니다. int i는 스피너가 처음로드 될 때의 기본 시작 인덱스이므로이 초기 시점에서 0과 같습니다. 그래서 내 솔루션은 현재 선택된 항목을 다시 선택하는 대신 실제로 다른 항목을 선택했는지 확인합니다 ... 질문에 대답합니까?
Chris

크리스 안녕하세요, 사용자가 편집 할 수 있도록 데이터베이스에서 정보를 가져 오는 페이지가 있습니다. 페이지가 열리면 스피너를 채우고 위치를 데이터베이스에있는 값으로 설정하십시오. 따라서 예를 들어 위치를 3으로 설정하면 onItemSelected가 i와 3으로 설정되어 초기와 다릅니다. 사용자가 실제로 변경 한 경우에만 설정되었다고 말하고 있다고 생각했습니다.
JStephen

4
사용자가 위치 0을 선택하면 어떻게됩니까? 그들은 무시됩니다.
Yetti99

나는 마지막 입장이 좋은 생각이라고 생각하지 않습니다. SharedPreferences에서 위치를로드하고 setSelection을 사용하여 스피너를 초기화합니다. SharedPrefs의 값은 스피너가 생성 될 때 기본값과 동일하지 않기 때문에 onItemSelected가 시작될 때 트리거됩니다.
Arthez

26

나는 비슷한 상황에 있었고, 나를 위해 일하는 간단한 해결책이 있습니다.

이 방법처럼 보인다 setSelection(int position)setSelected(int position, boolean animate)다른 내부 구현이있다.

setSelected(int position, boolean animate)false 애니메이션 플래그와 함께 두 번째 방법을 사용하면 onItemSelected리스너를 시작 하지 않고 선택을 얻을 수 있습니다.


더 좋은 방법은 onItemSelected에 대한 추가 호출에 대해 걱정하지 않고 올바른 선택을 표시하도록하는 것입니다. 따라서 리스너를 추가하기 전에 spinner.setSelection (selectedIndex)을 호출하면 일관되게 작동합니다.
andude

1
스피너에 대한 setSelected (int position, boolean animate) 방법이 없습니다
shift66

4
필요한 실제 전화는 다음과 같습니다setSelection(int position, boolean animate);
Brad

당신을 위해 +1. 이 코드는 번 이상 스피너의 콘텐츠 및 선택 유지가 사용자 상호 작용 만 onItemSelected 수정보다 일반적인 문제 해결
alrama

4
슬프게도 거짓 애니메이션 플래그는 여전히 통화 onItemSelectedAPI23에
MCY

23

onTouchListener를 사용하여 setOnItemSelectedListener에 대한 자동 호출 (활동 초기화 등의 일부 임)과 실제 사용자 상호 작용에 의해 트리거 된 호출을 구별하는 힌트를 살리기 위해 여기에 다른 제안을 시도한 후 다음을 수행했습니다. 가장 적은 코드 줄에서도 잘 작동한다는 것을 알았습니다.

다음과 같이 액티비티 / 프레임에 부울 필드를 설정하십시오.

private Boolean spinnerTouched = false;

그런 다음 스피너의 setOnItemSelectedListener를 설정하기 직전에 onTouchListener를 설정하십시오.

    spinner.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            System.out.println("Real touch felt.");
            spinnerTouched = true;
            return false;
        }
    });

    spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
    ...
         if (spinnerTouched){
         //Do the stuff you only want triggered by real user interaction.
        }
        spinnerTouched = false;

1
그것은 훌륭하게 작동하며 Android 6 이상부터 작동하는 유일한 방법입니다. 그러나 setOnKeyListener ()와 동일한 작업을 수행해야합니다. 그렇지 않으면 사용자가 키보드로 탐색 할 때 작동하지 않습니다.
Stéphane

잘 작동합니다. 다른 모든 솔루션에는 다른 전화에 문제가 있습니다.
Ziwei Zeng

이것은 간단하고 절대적으로 완벽합니다! 추가적인 넌센스가 필요하지 않으며 로직 만 명심하십시오. 내가 여기까지 끝까지 스크롤을해서 기뻐요!
user3833732

setOnKeyListener () 대신, 스피너를 서브 클래스하고 두 경우 모두 (touch / key) 호출되는 재정의 된 preformClick () 메소드에서 spinnerTouched = true 플래그를 설정할 수 있습니다. 나머지는 동일합니다.
전능하신

그냥 내가 최근에 여기에 게시 DropDownPreferences와 같은 버그를 해결하려면이 나타납니다 언급하고 싶었 : stackoverflow.com/questions/61867118/... 내가 * 요리하는 f를 TBH 믿을 수 없어 : D
다니엘 윌슨

13
spinner.setSelection(Adapter.NO_SELECTION, false);

3
코드 자체는 말할 수 있지만 약간의 설명은 먼 길을 간다 :)
nhaarman

8

오랫동안 머리카락을 뽑은 후 나는 내 자신의 Spinner 클래스를 만들었습니다. 리스너를 적절히 연결 해제하고 연결하는 메소드를 추가했습니다.

public class SaneSpinner extends Spinner {
    public SaneSpinner(Context context) {
        super(context);
    }

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

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

    // set the ceaseFireOnItemClickEvent argument to true to avoid firing an event
    public void setSelection(int position, boolean animate, boolean ceaseFireOnItemClickEvent) {
        OnItemSelectedListener l = getOnItemSelectedListener();
        if (ceaseFireOnItemClickEvent) {
            setOnItemSelectedListener(null);
        }

        super.setSelection(position, animate);

        if (ceaseFireOnItemClickEvent) {
            setOnItemSelectedListener(l);
        }
    }
}

다음과 같이 XML에서 사용하십시오.

<my.package.name.SaneSpinner
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/mySaneSpinner"
    android:entries="@array/supportedCurrenciesFullName"
    android:layout_weight="2" />

인플레이션 및 호출 세트 선택 후 SaneSpinner 인스턴스를 검색하기 만하면됩니다.

mMySaneSpinner.setSelection(1, true, true);

이를 통해 이벤트가 발생하지 않으며 사용자 상호 작용이 중단되지 않습니다. 이것은 내 코드 복잡성을 크게 줄였습니다. 실제 PITA이므로 Android에 재고가 포함되어야합니다.


1
이것은 나를 위해 작동하지 않으며 여전히 onItemSelected를 트리거합니다.
Arthez

Arthez는 세 번째 인수에 실제로 해당되는지 다시 확인하십시오. 그렇다면 다른 것이 잘못되었습니다. 가능하면 코드를 게시하십시오.
fusion44

8

레이아웃이 끝날 때까지 리스너 추가를 연기하면 레이아웃 단계에서 원하지 않는 이벤트가 발생하지 않습니다.

spinner.getViewTreeObserver().addOnGlobalLayoutListener(
    new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            // Ensure you call it only once works for JELLY_BEAN and later
            spinner.getViewTreeObserver().removeOnGlobalLayoutListener(this);

            // add the listener
            spinner.setOnItemSelectedListener(new OnItemSelectedListener() {

                @Override
                public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
                    // check if pos has changed
                    // then do your work
                }

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

            });

        }
    });

이것은 효과가 있으며 IMO는 OP의 특정 문제에 대한 가장 깨끗한 솔루션입니다. 더 이상 사용되지 않으며이 답변이 사용하는 방법과 비슷한 이름을 사용하여 ViewTreeObserver.OnGlobalLayoutListenerJ 아래의 버전을 제거 할 수 있습니다 ViewTreeObserver.removeGlobalOnLayoutListener.
Jack Meister

7

코드에서 다음과 같이 선택하면 발생합니다.

   mSpinner.setSelection(0);

위의 진술 대신에

   mSpinner.setSelection(0,false);//just simply do not animate it.

편집 :이 방법은 Mi Android 버전 Mi UI에서 작동하지 않습니다.


2
이것은 분명히 문제를 해결했습니다. Spinner 위젯에 대한 문서를 읽었습니다. 차이점을 이해하는 것은 절대적으로 까다 롭습니다. setSelection (int position, boolean animate)-> 어댑터 데이터의 특정 항목으로 직접 이동하십시오. setSelection (int position)-> 현재 선택된 항목을 설정합니다.
Matt

5

나는 매우 간단한 대답을 얻었습니다 .100 % 확실하게 작동합니다.

boolean Touched=false; // this a a global variable

public void changetouchvalue()
{
   Touched=true;
}

// this code is written just before onItemSelectedListener

 spinner.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            System.out.println("Real touch felt.");
            changetouchvalue();
            return false;
        }
    });

//inside your spinner.SetonItemSelectedListener , you have a function named OnItemSelected iside that function write the following code

if(Touched)
{
 // the code u want to do in touch event
}

3

나는 이것에 대해 훨씬 더 우아한 해결책을 찾았습니다. ArrayAdapter (여러분의 경우 "어댑터")가 몇 번이나 호출되었는지 계산합니다. 하나의 스피너가 있고 전화한다고 가정 해 봅시다.

int iCountAdapterCalls = 0;

ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
            this, R.array.pm_list, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    spinner.setAdapter(adapter);

onCreate 후 int 카운터를 선언 한 다음 onItemSelected () 메소드 내부에 "if"조건을 지정하여 어댑터가 호출 된 횟수를 확인하십시오. 귀하의 경우에는 한 번만 호출하면됩니다.

if(iCountAdapterCalls < 1)
{
  iCountAdapterCalls++;
  //This section executes in onCreate, during the initialization
}
else
{
  //This section corresponds to user clicks, after the initialization
}

2

내 작은 기여는 위의 일부 중 몇 가지에 적합한 변형입니다.

정수 변수를 기본값 (또는 환경 설정에 마지막으로 저장된 값)으로 선언하십시오. 리스너가 등록되기 전에 spinner.setSelection (myDefault)을 사용하여 해당 값을 설정하십시오. onItemSelected에서 새 스피너 값이 추가 코드를 실행하기 전에 지정한 값과 같은지 확인하십시오.

사용자가 동일한 값을 다시 선택하면 코드를 실행하지 않는 이점이 있습니다.


1

같은 문제가 있었지만 태그를 사용 하여이 솔루션에 왔습니다. 기본 개념은 간단합니다. 스피너가 프로그래밍 방식으로 변경 될 때마다 태그가 선택한 위치를 반영하는지 확인하십시오. 리스너에서 선택한 위치가 태그와 같은지 확인합니다. 그렇다면 스피너 선택이 프로그래밍 방식으로 변경되었습니다.

아래는 새로운 "스피너 프록시"클래스입니다.

package com.samplepackage;

import com.samplepackage.R;
import android.widget.Spinner;

public class SpinnerFixed {

    private Spinner mSpinner;

    public SpinnerFixed(View spinner) {
         mSpinner = (Spinner)spinner;
         mSpinner.setTag(R.id.spinner_pos, -2);
    }

    public boolean isUiTriggered() {
         int tag = ((Integer)mSpinner.getTag(R.id.spinner_pos)).intValue();
         int pos = mSpinner.getSelectedItemPosition();
         mSpinner.setTag(R.id.spinner_pos, pos);
         return (tag != -2 && tag != pos);
    }

    public void setSelection(int position) {
        mSpinner.setTag(R.id.spinner_pos, position);
        mSpinner.setSelection(position);
    }

    public void setSelection(int position, boolean animate) {
        mSpinner.setTag(R.id.spinner_pos, position);
        mSpinner.setSelection(position, animate);
    }

    // If you need to proxy more methods, use "Generate Delegate Methods"
    // from the context menu in Eclipse.
}

또한 Values디렉토리에 태그 설정이있는 XML 파일이 필요합니다 . 나는 내 파일 이름을 지정 spinner_tag.xml했지만 그것은 당신에게 달려 있습니다. 다음과 같이 보입니다 :

<resources xmlns:android="http://schemas.android.com/apk/res/android">
  <item name="spinner_pos" type="id" />
</resources>

이제 교체

Spinner myspinner;
...
myspinner = (Spinner)findViewById(R.id.myspinner);

와 코드에서

SpinnerFixed myspinner;
...
myspinner = new SpinnerFixed(findViewById(R.id.myspinner));

그리고 핸들러를 다음과 같이 만드십시오.

myspinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {

    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
        if (myspinner.isUiTriggered()) {
            // Code you want to execute only on UI selects of the spinner
        }
    }

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

이 함수 isUiTriggered()는 사용자가 스피너를 변경 한 경우에만 true를 반환합니다. 이 함수는 부작용이 있습니다. 태그를 설정하므로 동일한 리스너 호출에서 두 번째 호출은 항상을 반환 false합니다.

이 래퍼는 레이아웃 생성 중에 리스너가 호출되는 문제도 처리합니다.

젠스


1

나를 위해 아무것도 효과가 없었으며 내 견해로는 1 회 이상의 스피너가 있으며 (BOHO지도를 보유하는 IMHO는 과잉입니다) 태그를 사용하여 클릭 수를 계산합니다.

spinner.setTag(0);
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
            Integer selections = (Integer) parent.getTag();
            if (selections > 0) {
                // real selection
            }
            parent.setTag(++selections); // (or even just '1')
        }

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

1

이미 많은 답변이 있습니다.

선택 콜백을 트리거하지 않고 프로그래밍 방식의 선택 설정을 허용 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);
    }
}

호출의 사용의 예 onCreateView()A의 Fragment예를 들면 :

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

where 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
}

0

전화하려고

spinner.setOnItemSelectedListener(new MyOnItemSelectedListener());

setAdapter ()를 호출 한 후 또한 어댑터 전에 전화를 걸어보십시오.

이벤트를 건너 뛰기 위해 부울 플래그를 재정의 된 setAdapter 메소드로 랩핑 할 수있는 서브 클래 싱을 사용하는 솔루션이 항상 있습니다.


0

부울 플래그 또는 카운터가있는 솔루션은 방향 변경 중에 onItemSelected () 호출이 플래그 또는 카운터를 "오버 플라잉"했기 때문에 도움이되지 않았습니다.

나는 하위 클래스 android.widget.Spinner를 만들고 작은 추가를했습니다. 관련 부품은 다음과 같습니다. 이 솔루션은 저에게 효과적이었습니다.

private void setHandleOnItemSelected()
{
  final StackTraceElement [] elements = Thread.currentThread().getStackTrace();

  for (int index = 1; index < elements.length; index++)
  {
     handleOnItemSelected = elements[index].toString().indexOf("PerformClick") != -1; //$NON-NLS-1$

     if (handleOnItemSelected)
     {
        break;
     }
  }
}

@Override
public void setSelection(int position, boolean animate)
{
  super.setSelection(position, animate);

  setHandleOnItemSelected();
}

@Override
public void setSelection(int position)
{
  super.setSelection(position);

  setHandleOnItemSelected();
}

public boolean shouldHandleOnItemSelected()
{
  return handleOnItemSelected;
}

0

이것은 우아한 해결책이 아닙니다. 사실 그것은 오히려 Rube-Goldberg이지만 작동하는 것 같습니다. 배열 어댑터를 확장하고 getDropDownView를 재정 의하여 스피너가 적어도 한 번 사용되었는지 확인합니다. 새로운 getDropDownView 메소드에는 드롭 다운 메뉴가 적어도 한 번 사용되었음을 표시하도록 설정된 부울 플래그가 있습니다. 플래그가 설정 될 때까지 리스너에 대한 호출을 무시합니다.

MainActivity.onCreate () :

ActionBar ab = getActionBar();
ab.setDisplayShowTitleEnabled(false);
ab.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
ab.setListNavigationCallbacks(null, null);

ArrayList<String> abList = new ArrayList<String>();
abList.add("line 1");
...

ArAd  abAdapt = new ArAd (this
   , android.R.layout.simple_list_item_1
   , android.R.id.text1, abList);
ab.setListNavigationCallbacks(abAdapt, MainActivity.this);

재정의 된 배열 어댑터 :

private static boolean viewed = false;
private class ArAd extends ArrayAdapter<String> {
    private ArAd(Activity a
            , int layoutId, int resId, ArrayList<String> list) {
        super(a, layoutId, resId, list);
        viewed = false;
    }
    @Override
    public View getDropDownView(int position, View convertView,
            ViewGroup parent) {
        viewed = true;
        return super.getDropDownView(position, convertView, parent);
    }
}

수정 된 리스너 :

@Override
public boolean onNavigationItemSelected(
   int itemPosition, long itemId) {
   if (viewed) {
     ...
   }
   return false;
}


0

내가 한 간단한 방법으로 :

private AdapterView.OnItemSelectedListener listener;
private Spinner spinner;

onCreate ();

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

listener = new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> adapterView, View view, int position, long l) {

            Log.i("H - Spinner selected position", position);
        }

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

        }
    };

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

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

        }
    });

끝난


흥미로운 해결책입니다. 더 많은 설명을 사용할 수 있습니다. 기본적으로 첫 번째 onItemSelected 이벤트를 의도적으로 무시합니다. 접근성 옵션이 활성화 된 경우 (예 : Jorrit의 설명 참조) 와 같은 경우에는 제대로 작동 할 수 있습니다 .
jk7

0
if () {        
       spinner.setSelection(0);// No reaction to create spinner !!!
     } else {
        spinner.setSelection(intPosition);
     }


spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {

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

         if (position > 0) {
           // real selection
         }

      }

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

     }
});

0

그것이 나의 최종적이고 사용하기 쉬운 솔루션입니다.

public class ManualSelectedSpinner extends Spinner {
    //get a reference for the internal listener
    private OnItemSelectedListener mListener;

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

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

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

    @Override
    public void setOnItemSelectedListener(@Nullable OnItemSelectedListener listener) {
        mListener = listener;
        super.setOnItemSelectedListener(listener);
    }

    public void setSelectionWithoutInformListener(int position){
        super.setOnItemSelectedListener(null);
        super.setSelection(position);
        super.setOnItemSelectedListener(mListener);
    }

    public void setSelectionWithoutInformListener(int position, boolean animate){
        super.setOnItemSelectedListener(null);
        super.setSelection(position, animate);
        super.setOnItemSelectedListener(mListener);
    }
}

setSelection(...)기본 동작에 기본값 을 사용 하거나 setSelectionWithoutInformListener(...)OnItemSelectedListener 콜백을 트리거하지 않고 스피너에서 항목을 선택하는 데 사용 하십시오.


0

내가 사용에 필요한 mSpinner플래그가 있으므로, ViewHolder에 mOldPosition익명 내부 클래스에 설정됩니다.

mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            int mOldPosition = mSpinner.getSelectedItemPosition();

            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long l) {
                if (mOldPosition != position) {
                    mOldPosition = position;
                    //Do something
                }
            }

            @Override
            public void onNothingSelected(AdapterView<?> adapterView) {
                //Do something
            }
        });

0

onClickListener 객체를 만드는 동안 초기 색인을 저장합니다.

   int thisInitialIndex = 0;//change as needed

   myspinner.setSelection(thisInitialIndex);

   myspinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {

      int initIndex = thisInitialIndex;

      @Override
      public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
         if (id != initIndex) { //if selectedIndex is the same as initial value
            // your real onselecteditemchange event
         }
      }

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

0

내 솔루션은 사용 onTouchListener하지만 사용을 제한하지는 않습니다. onTouchListener필요한 경우 setup에 대한 랩퍼를 작성합니다 onItemSelectedListener.

public class Spinner extends android.widget.Spinner {
    /* ...constructors... */

    private OnTouchListener onTouchListener;
    private OnItemSelectedListener onItemSelectedListener;

    @Override
    public void setOnItemSelectedListener(OnItemSelectedListener listener) {
        onItemSelectedListener = listener;
        super.setOnTouchListener(wrapTouchListener(onTouchListener, onItemSelectedListener));
    }

    @Override
    public void setOnTouchListener(OnTouchListener listener) {
        onTouchListener = listener;
        super.setOnTouchListener(wrapTouchListener(onTouchListener, onItemSelectedListener));
    }

    private OnTouchListener wrapTouchListener(final OnTouchListener onTouchListener, final OnItemSelectedListener onItemSelectedListener) {
        return onItemSelectedListener != null ? new OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                Spinner.super.setOnItemSelectedListener(onItemSelectedListener);
                return onTouchListener != null && onTouchListener.onTouch(view, motionEvent);
            }
        } : onTouchListener;
    }
}

0

게시물에 너무 늦게 응답 할 수는 있지만 Android Data binding 라이브러리 Android Databinding을 사용하여이를 달성 했습니다. . 선택한 항목이 변경 될 때까지 리스너가 호출되지 않도록 사용자 정의 바인딩을 만들었으므로 사용자가 동일한 위치를 반복해서 선택하더라도 이벤트가 시작되지 않습니다.

레이아웃 xml 파일

    <layout>
  <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/activity_vertical_margin"
xmlns:app="http://schemas.android.com/apk/res-auto">


<Spinner
    android:id="@+id/spinner"
    android:layout_width="150dp"
    android:layout_height="wrap_content"
    android:spinnerMode="dropdown"
    android:layout_below="@id/member_img"
    android:layout_marginTop="@dimen/activity_vertical_margin"
    android:background="@drawable/member_btn"
    android:padding="@dimen/activity_horizontal_margin"
    android:layout_marginStart="@dimen/activity_horizontal_margin"
    android:textColor="@color/colorAccent"
    app:position="@{0}"
    />
 </RelativeLayout>
 </layout>

app:position 선택하려는 위치를 전달하는 곳입니다.

커스텀 바인딩

  @BindingAdapter(value={ "position"}, requireAll=false)
  public static void setSpinnerAdapter(Spinner spinner, int selected) 
  {

    final int [] selectedposition= new int[1];
    selectedposition[0]=selected;


    // custom adapter or you can set default adapter
        CustomSpinnerAdapter customSpinnerAdapter = new CustomSpinnerAdapter(spinner.getContext(), <arraylist you want to add to spinner>);
        spinner.setAdapter(customSpinnerAdapter);
            spinner.setSelection(selected,false);


    spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {

            String item = parent.getItemAtPosition(position).toString();
        if( position!=selectedposition[0]) {
                        selectedposition[0]=position;
            // do your stuff here
                    }
                }


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

        }
    });
}

사용자 정의 데이터 바인딩에 대한 자세한 내용은 Android Custom Setter를 참조하십시오.

노트

  1. Gradle 파일에서 데이터 바인딩을 활성화하는 것을 잊지 마십시오

       android {
     ....
     dataBinding {
     enabled = true
    }
    }
  2. 레이아웃 파일을 <layout>태그에 포함


-1
mYear.setOnItemSelectedListener(new OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parent, View arg1, int item, long arg3) {
                if (mYearSpinnerAdapter.isEnabled(item)) {

                }
            }

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

2
1) 코드를 올바르게 포맷하십시오. 2) 코드가 무엇을하고 있는지에 대한 설명도 감사하겠습니다. 코드를 읽을 때 모든 코드 스 니펫이 즉시 이해되는 것은 아닙니다.
Mike Koch
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.