developer tip

현재 선택한 항목을 다시 선택할 때 Android Spinner에서 이벤트를 받으려면 어떻게해야합니까?

optionbox 2021. 1. 5. 07:59
반응형

현재 선택한 항목을 다시 선택할 때 Android Spinner에서 이벤트를 받으려면 어떻게해야합니까?


스피너 항목이 변경 될 때 응답 할 스피너에 대한 setOnItemSelectedListener를 작성했습니다. 내 요구 사항은 현재 선택한 항목을 다시 클릭하면 토스트가 표시되어야한다는 것입니다. 이 이벤트를받는 방법? 현재 선택한 항목을 다시 클릭하면 스피너가 응답하지 않습니다. `

    StorageSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener(){

        @Override
        public void onItemSelected(AdapterView adapter, View v, int i, long lng) {              
            Toast.makeText(getApplicationContext(), (CharSequence) StorageSpinner.getSelectedItem(), Toast.LENGTH_SHORT).show();

        }

        @Override
        public void onNothingSelected(AdapterView arg0) {
            Toast.makeText(getApplicationContext(), "Nothing selected", Toast.LENGTH_SHORT).show();

        }
    });  

현재 선택된 항목을 다시 클릭하면 이벤트가 발생하지 않습니다. 따라서 스피너가 응답하도록 setOnItemSelectedListener를 잡을 수 없습니다.


이 문제를 해결하기 위해 몇 시간을 보냈습니다. 나는 다음과 같이 끝났다. 모든 경우에 작동하는지 확실하지 않지만 나를 위해 작동하는 것 같습니다. 선택을 확인하고 선택이 동일한 값으로 설정된 경우 리스너를 호출하는 Spinner 클래스의 확장 일뿐입니다.

import android.content.Context;
import android.util.AttributeSet;
import android.widget.Spinner;


/** Spinner extension that calls onItemSelected even when the selection is the same as its previous value */
public class NDSpinner extends Spinner {

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

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

    public NDSpinner(Context context, AttributeSet attrs, int defStyle)
    { super(context, attrs, defStyle); }

    @Override 
    public void setSelection(int position, boolean animate) {
        boolean sameSelected = position == getSelectedItemPosition();
        super.setSelection(position, animate);
        if (sameSelected) {
            // Spinner does not call the OnItemSelectedListener if the same item is selected, so do it manually now
            getOnItemSelectedListener().onItemSelected(this, getSelectedView(), position, getSelectedItemId());
        }
    } 

    @Override
    public void setSelection(int position) {
        boolean sameSelected = position == getSelectedItemPosition();
        super.setSelection(position);
        if (sameSelected) {
            // Spinner does not call the OnItemSelectedListener if the same item is selected, so do it manually now
            getOnItemSelectedListener().onItemSelected(this, getSelectedView(), position, getSelectedItemId());
        }
    }

}

이 시도

public class MySpinner extends Spinner{

OnItemSelectedListener listener;

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

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

        if (position == getSelectedItemPosition())
        {
            listener.onItemSelected(null, null, position, 0);
        }       
    }

    public void setOnItemSelectedListener(OnItemSelectedListener listener)
    {
        this.listener = listener;
    }
}

이 스피너는 항상 선택이 변경되었음을 알려줍니다.

package com.mitosoft.ui.widgets;

import java.lang.reflect.Method;
import android.content.Context;
import android.content.DialogInterface;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.AdapterView;
import android.widget.Spinner;

//com.mitosoft.ui.widgets.NoDefaultSpinner
public class NoDefaultSpinner extends Spinner {

    private int lastSelected = 0;
    private static Method s_pSelectionChangedMethod = null;


    static {        
        try {
            Class noparams[] = {};
            Class targetClass = AdapterView.class;

            s_pSelectionChangedMethod = targetClass.getDeclaredMethod("selectionChanged", noparams);            
            if (s_pSelectionChangedMethod != null) {
                s_pSelectionChangedMethod.setAccessible(true);              
            }

        } catch( Exception e ) {
            Log.e("Custom spinner, reflection bug:", e.getMessage());
            throw new RuntimeException(e);
        }
    }

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

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

    public NoDefaultSpinner(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public void testReflectionForSelectionChanged() {
        try {
            Class noparams[] = {};          
            s_pSelectionChangedMethod.invoke(this, noparams);
        } catch (Exception e) {
            Log.e("Custom spinner, reflection bug: ", e.getMessage());
            e.printStackTrace();                
        }
    } 




    @Override
    public void onClick(DialogInterface dialog, int which) {    
        super.onClick(dialog, which);
            if(lastSelected == which)
                testReflectionForSelectionChanged();

            lastSelected = which;
    }
}

최신 Android 버전에서 작업하는 사람들을 위해 업데이트 된 답변을 남길 것이라고 생각했습니다.

적어도 4.1.2 및 4.3 (내가 테스트 한 장치)에서 작동하는 위 답변의 함수를 함께 컴파일했습니다. 이 함수는 리플렉션을 사용하지 않고 대신 마지막으로 선택한 인덱스 자체를 추적하므로 SDK가 클래스가 서로 확장되는 방식을 변경하더라도 안전하게 사용할 수 있습니다.

import android.content.Context;
import android.util.AttributeSet;
import android.widget.Spinner;

public class SelectAgainSpinner extends Spinner {

    private int lastSelected = 0;

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

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

    public SelectAgainSpinner(Context context, AttributeSet attrs, int defStyle)
    { super(context, attrs, defStyle); }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if(this.lastSelected == this.getSelectedItemPosition() && getOnItemSelectedListener() != null)
            getOnItemSelectedListener().onItemSelected(this, getSelectedView(), this.getSelectedItemPosition(), getSelectedItemId());
        if(!changed)
            lastSelected = this.getSelectedItemPosition();

        super.onLayout(changed, l, t, r, b);
    } 
}

최신 플랫폼의 경우 이것을 Dimitar의 솔루션에 추가하십시오. 나는 그것이 작동한다고 생각한다 :)

(onLayout을 재정의하고 onClick 메서드를 제거해야합니다)

    @Override
public void onClick(DialogInterface dialog, int which) {    
    super.onClick(dialog, which);
}

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    if(this.lastSelected == this.getSelectedItemPosition())
        testReflectionForSelectionChanged();
    if(!changed)
        lastSelected = this.getSelectedItemPosition();

    super.onLayout(changed, l, t, r, b);
} 

내가 찾은 것은 스피너에서 동일한 항목을 다시 선택하면 OnItemSelectedListener가 호출되지 않는다는 것입니다. 스피너를 클릭하고 동일한 값을 다시 선택하면 OnItemSelectedListener 메서드가 호출되지 않습니다. 사람들은 UI 디자인에 따라 이미 활성화 된 선택 항목을 클릭하면 어떤 일이 일어날 것이라고 기대하지 않습니다.


@Dimitar. 와우, 훌륭한 작품입니다. 감사합니다. 귀하의 솔루션 (포인트가 충분하지 않음)을 찬성 할 수는 없지만 NoDefaultSpinner 클래스가 작동합니다. 이 한 가지만 문제였습니다. "OnClick"내에서 super.onClick을 호출 한 다음 testReflectionForSelectionChanged ()를 호출하기 때문에 선택 항목이 실제로 변경되면 스피너에 대한 onItemSelected 핸들러가 두 번 호출됩니다 (동일한 경우 기능은 올바름). 항목이 다시 선택됨). 나는 그것을 해킹하여 이것을 해결했습니다. 터치 된 항목을 기록한 onTouchEvent 재정의를 추가 한 다음 "onClick"에서 변경되었는지 확인했습니다.

private Object ob=null; //class level variable
 @Override
public boolean onTouchEvent(MotionEvent m)
{
    if (m.getAction()==MotionEvent.ACTION_DOWN)
    {
        ob=this.getSelectedItem();
    }
    return super.onTouchEvent(m);
}
@Override
public void onClick(DialogInterface dialog, int which) {    
    super.onClick(dialog, which);
    if (this.getSelectedItem().equals(ob))
        testReflectionForSelectionChanged();
}

이것은 완전한 솔루션은 아니지만 어디서나 조각 / 활동으로 돌아갈 때 이것을 호출하려는 경우에만 작동합니다.

고려 mSpinner 것은 , 우리가 이런 식의 리스너를 호출하여 회 전자이다 :

@Override public void onResume()
    {
        if ( mSpinner.getCount() > 0 )
        {
            mSpinner.getOnItemSelectedListener()
                    .onItemSelected( mSpinner, null, mSpinner.getSelectedItemPosition(), 0 );
        }
        super.onResume();
    }

안녕하세요, 문제에 대한 창의적인 답변에 대해 @Dimitar에게 감사드립니다. 나는 그것을 시도했고 2.x와 같은 이전 Android 버전에서 잘 작동하지만 불행히도 버전 3.0 이상에서는 작동하지 않습니다 (3.2 및 4.0.3 시도). 어떤 이유로 onClick 메서드는 최신 플랫폼에서 호출되지 않습니다. 누군가 여기에 버그 보고서를 작성했습니다. http://code.google.com/p/android/issues/detail?id=16245

최신 플랫폼에서 작동하지 않는다는 것은 다른 솔루션이 필요하다는 것을 의미합니다. 내 응용 프로그램에서는 처음에 숨겨진 "더미"항목이있는 선택되지 않은 스피너를 시뮬레이션하는 것으로 충분했습니다. 그런 다음 "숨겨진"항목이 선택 항목으로 설정된 경우 클릭 된 모든 항목은 콜백을 발생시킵니다. 일부 단점은 아무것도 선택되지 않을 수 있지만 Spinner 클래스 재정의 트릭을 사용하여 수정할 수 있습니다.

Android Spinner에서 한 항목을 숨기는 방법을 참조하세요.


Spinner 동작은 요청에 대해 예상되지 않습니다. 내 솔루션은 Spinner와 함께 작동하지 않습니다. 하나의 BaseFragment 내부에 하나의 ListView를 사용하여 우리가 예상하는 기능을 연구하는 유사한 방식으로 만듭니다.

혜택은 다음과 같습니다.

  1. Spinner 기본값을 확장하는 데 더 이상 골칫거리가 아닙니다.
  2. 간편한 구현 및 사용자 정의.
  3. 모든 Android API와 완벽하게 호환됩니다.
  4. 첫 번째 OnItemSelectedListener.onItemSelected 호출에 대한 얼굴이 없습니다.

주요 아이디어는 다음과 같이하는 것입니다.

BaseFragment 레이아웃은 다음과 유사합니다.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:background="@null"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical"
              android:gravity="center">

    <ListView
            android:id="@+id/fragment_spinnerList"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
</LinearLayout>

코드는 다음과 같습니다.

public class SpinnerListFragment extends android.support.v4.app.DialogFragment {

    static SpinnerListFragment newInstance(List<String> items) {

        SpinnerListFragment spinnerListFragment = new SpinnerListFragment();
        Bundle args = new Bundle();

        args.putCharSequenceArrayList("items", (ArrayList) items);
        spinnerListFragment.setArguments(args);

        return spinnerListFragment;
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {

        Dialog dialog = new Dialog(getActivity(), R.style.dialog);
        final View view = getActivity().getLayoutInflater().inflate(R.layout.dialog_spinner_list, null);

        dialog.getWindow().setContentView(view);
        dialog.setCanceledOnTouchOutside(true);

        // CUSTOMIZATION...

        final List items = (ArrayList) getArguments().getCharSequenceArrayList("items");

        final ListView spinnerList = (ListView) view.findViewById(R.id.fragment_spinnerList);

        ArrayAdapter<String> arrayAdapter =
                new ArrayAdapter<String>(
                        getActivity(),
                        R.layout.search_spinner_list_item,
                        items);

        spinnerList.setAdapter(arrayAdapter);

        spinnerList.setOnItemClickListener(new AdapterView.OnItemClickListener() {

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

                // DO SOMETHING...

                SpinnerListFragment.this.dismiss();
            }
        });

        return dialog;
    }

}

내 솔루션은 benoffi7의 MySpinner를 기반으로합니다. 마지막으로 선택한 상위 및보기를 저장하여 동일한 항목 선택에서 null 전달을 수정합니다.

public class MySpinner extends Spinner {

    private OnItemSelectedListener listener;
    private AdapterView<?> lastParent;
    private View lastView;
    private long lastId;

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

    public MySpinner(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initInternalListener();
    }

    private void initInternalListener() {
        super.setOnItemSelectedListener(new OnItemSelectedListener() {

            @Override
            public void onItemSelected(AdapterView<?> parent, View view,
                int position, long id) {
                lastParent = parent;
                lastView = view;
                lastId = id;
                if (listener != null) {
                    listener.onItemSelected(parent, view, position, id);
                }
            }

            @Override
            public void onNothingSelected(AdapterView<?> parent) {
                //lastParent = parent; // do we need it?
                if (listener != null) {
                    listener.onNothingSelected(parent);
                }
            }
        });
    }

    @Override
    public void setSelection(int position) {
        if (position == getSelectedItemPosition() && listener != null) {
            listener.onItemSelected(lastParent, lastView, position, lastId);
        } else {
            super.setSelection(position);
        }
    }

    @Override
    public void setOnItemSelectedListener(OnItemSelectedListener listener) {
        this.listener = listener;
    }
}

class MySpinner extends Spinner {


    public MySpinner(Context context) {
        super(context);
        // TODO Auto-generated constructor stub
    }
    public MySpinner(Context context, AttributeSet attrs)
    {
        super(context, attrs);
    }
    @Override
    public void setSelection(int position, boolean animate) {
        ignoreOldSelectionByReflection();
        super.setSelection(position, animate);
    }

    private void ignoreOldSelectionByReflection() {
        try {
            Class<?> c = this.getClass().getSuperclass().getSuperclass().getSuperclass();
            Field reqField = c.getDeclaredField("mOldSelectedPosition");
            reqField.setAccessible(true);
            reqField.setInt(this, -1);
        } catch (Exception e) {
            Log.d("Exception Private", "ex", e);
            // TODO: handle exception
        }
    }

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

}

다음과 같이 "onTouch"를 기반으로 프로그래밍 방식으로 "선택된 항목"을 설정할 수 있기 때문에 쉬운 해결책이 있습니다.

spinnerobject.setOnTouchListener(new AdapterView.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {

            final int MAKE_A_SELECTION = 1; //whatever index that is the normal starting point of the spinner.
            spinnerObject.setSelection(MAKE_A_SELECTION);
            return false;
        }
    });

(1) 스피너 행이 표시되기 직전에 스피너 텍스트가 기본값으로 다시 변경되고 (2)이 기본 항목이 목록의 일부가된다는 점에서 작은 패널티가 있습니다. 누군가가 스피너의 특정 항목을 비활성화하는 방법을 추가 할 수 있습니까?

일반적으로 스피너 선택은 검색 시작과 같은 반복 가능한 이벤트를 실행할 수 있기 때문에 스피너에서 항목을 다시 선택할 가능성이 없다는 것은 실제로 Spinner 클래스에서 누락 된 기능 / 버그이며 Android 개발자가 최대한 빨리 수정해야하는 버그입니다. .


안녕하세요, 이것은 나를 위해 일했습니다.

ArrayAdapter<String> adaptador1 = new ArrayAdapter<String>( Ed_Central.this, 
                                                            android.R.layout.simple_spinner_item,
                                                            datos1
                                                            );
        lista1.setAdapter( adaptador1 );

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

                lista1.setSelection( 0 );

                switch ( position ) {
                    case 1:
                        Toast.makeText( getApplicationContext(), "msg1", Toast.LENGTH_SHORT ).show();
                        break;
                    case 2:
                        Toast.makeText( getApplicationContext(), "msg2", Toast.LENGTH_SHORT ).show();
                        break;
                    case 3:
                        Toast.makeText( getApplicationContext(), "msg3", Toast.LENGTH_SHORT ).show();
                        break;

                    default:
                        break;
                }
            }

항상 어댑터는 위치 "0"에있을 것이고 토스트를 보여줄 수 있습니다.


스피너 디스플레이가 하나의 편집 텍스트를 추가하고 가시성 사라진 속성을 설정할 때 XML에서이 작업을 정말로 수행하려는 경우; spinner 및 view.onclicklisner에 설정된 의상 어댑터에 대한 의상 어댑터를 만들고 clickevent가 발생했을 때 EditText.setText ( "0"); 활동 세트 edittext textWatcher Event 및 이벤트 블록에서 onSppinerItem 이벤트 블록 코드를 추가합니다. 문제 해결


package customclasses;

/**
 * Created by Deepak on 7/1/2015.
 */

import android.content.Context;
import android.util.AttributeSet;
import android.widget.Spinner;

/**
 * Spinner extension that calls onItemSelected even when the selection is the same as its previous value
 */
public class NDSpinner extends Spinner {
    public boolean isDropDownMenuShown=false;

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

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

    public NDSpinner(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }


    @Override
    public void
    setSelection(int position, boolean animate) {
        boolean sameSelected = position == getSelectedItemPosition();
        super.setSelection(position, animate);
        if (sameSelected) {
            // Spinner does not call the OnItemSelectedListener if the same item is selected, so do it manually now
            getOnItemSelectedListener().onItemSelected(this, getSelectedView(), position, getSelectedItemId());
        }
    }

    @Override
    public boolean performClick() {
        this.isDropDownMenuShown = true; //Flag to indicate the spinner menu is shown
        return super.performClick();
    }
    public boolean isDropDownMenuShown(){
        return isDropDownMenuShown;
    }
    public void setDropDownMenuShown(boolean isDropDownMenuShown){
        this.isDropDownMenuShown=isDropDownMenuShown;
    }
    @Override
    public void
    setSelection(int position) {
        boolean sameSelected = position == getSelectedItemPosition();
        super.setSelection(position);
        if (sameSelected) {
            // Spinner does not call the OnItemSelectedListener if the same item is selected, so do it manually now
            getOnItemSelectedListener().onItemSelected(this, getSelectedView(), position, getSelectedItemId());
        }
    }
    @Override
    public void onDetachedFromWindow() {
        super.onDetachedFromWindow();
    }
}

Custom spinner with same item selection callback in Kotlin:

class StorageSpinner : AppCompatSpinner {

    constructor(context: Context) : super(context)
    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)

    interface StorageSpinnerSelectionCallback {
        fun onItemSelected(position: Int)
    }

    private var selectionCallback: StorageSpinnerSelectionCallback? = null

    fun setSelectionCallback(selectionCallback: StorageSpinnerSelectionCallback) {
        this.selectionCallback = selectionCallback
    }

    fun removeSelectionCallback() {
        selectionCallback = null
    }

    override fun setSelection(position: Int) {
        super.setSelection(position)

        if (position == selectedItemPosition) selectionCallback?.onItemSelected(position)
    }
}

You must perform the following steps: 1. Create an Adapter Customer for the spinner 2. In the override fun getDropDownView (...), you must put a view.setOnClickListener {"interface"} 3. Create an interface


Try this, this might be not the proper solution but is the best working solution available right now, if not making a custom spinner.

what you have to do is to reset spinner adapter on every item click,

 @Override
    public void onItemSelected(AdapterView adapter, View v, int position, long lng) {              

    if (position == getSelectedItemPosition())
    {
        //do your thing
        //then at end of statement reset adapter like

       spinner.setAdapter(adapter);
    } 

    }

I hope it helped you solve your problem


Continue from this answer https://stackoverflow.com/a/17099104/7392507

In your xml file, create a spinner. Replace the spinner tag <Spinner> with <yourdomain.yourprojectname.yourpackagename.spinnerclassname> for example <com.company.appname.utility.MySpinner>.

In your java file, define the spinner variable as below: -Spinnerclassname varName = findViewById(R.id.spinnerId);

For example: -MySpinner varSpinner = findViewById(R.id.spinnerId);


Just try out with this.

@Override
public void onItemSelected(AdapterView adapter, View v, int i, long lng) {  

    if(v.hasFocus() {            
        Toast.makeText(getApplicationContext(), (CharSequence) StorageSpinner.getSelectedItem(), Toast.LENGTH_SHORT).show();
    }    
}

Hope it may work.

ReferenceURL : https://stackoverflow.com/questions/5335306/how-can-i-get-an-event-in-android-spinner-when-the-current-selected-item-is-sele

반응형