[英]How can I get an event in Android Spinner when the current selected item is selected again?
我已經為微調器編寫了一個 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();
}
});
我花了好幾個小時試圖找到解決這個問題的方法。 我最終得到了以下結果。 我不確定它是否適用於所有情況,但似乎對我有用。 它只是 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);
}
}
kotlin ,希望它有幫助
import android.content.Context
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatSpinner
class MoreSpinner : 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)
override fun setSelection(position: Int, animate: Boolean) {
val sameSelected = position == selectedItemPosition
super.setSelection(position, animate)
if (sameSelected) {
onItemSelectedListener?.onItemSelected(
this,
selectedView,
position,
selectedItemId
)
}
}
override fun setSelection(position: Int) {
val sameSelected = position == selectedItemPosition
super.setSelection(position)
if (sameSelected) {
onItemSelectedListener?.onItemSelected(
this,
selectedView,
position,
selectedItemId
)
}
}
}
對於較新的平台,請嘗試將此添加到 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 設計已經處於活動狀態的選擇會發生什么事情。
這不是完整的解決方案,但如果您只想在從任何地方返回片段/活動時調用它,它就可以工作。
考慮到mSpinner是你的 Spinner 視圖,我們這樣稱呼它的監聽器:
@Override public void onResume() {
if (mSpinner.getCount() > 0) {
mSpinner.getOnItemSelectedListener()
.onItemSelected( mSpinner, null, mSpinner.getSelectedItemPosition(), 0 );
}
super.onResume();
}
從這個答案繼續https://stackoverflow.com/a/17099104/7392507
在您的 xml 文件中,創建一個微調器。 將微調標簽<Spinner>
替換為<yourdomain.yourprojectname.yourpackagename.spinnerclassname>
例如<com.company.appname.utility.MySpinner>
。
在你的java文件中,定義spinner變量如下: - Spinnerclassname varName = findViewById(R.id.spinnerId);
例如: - MySpinner varSpinner = findViewById(R.id.spinnerId);
@迪米塔爾。 哇,精彩的作品。 感謝那。 我不能贊成您的解決方案(分數不夠),但 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();
}
我的解決方案是基於 benoffi7 的 MySpinner。 通過保存最后選擇的父級和視圖,修復了在同一項目選擇上傳遞空值的問題。
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;
}
}
我們的要求不會出現 Spinner 行為。 我的解決方案不適用於 Spinners,以一種類似的方式實現,在一個 BaseFragment 中使用一個 ListView 來研究我們期望的功能。
好處是:
主要思想是做這樣的事情:
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;
}
}
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 選擇可能會執行可重復的事件(例如開始搜索),因此 Spinner 中無法重新選擇項目實際上是 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”,您可以展示您的吐司。
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();
}
}
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)
}
}
試試這個,如果不制作自定義微調器,這可能不是正確的解決方案,但它是目前可用的最佳工作解決方案。
你需要做的是在每次點擊項目時重置微調適配器,
@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);
}
}
我希望它能幫助你解決你的問題
您好,感謝@Dimitar 對問題的創造性回答。 我已經試過了,它在 2.x 等較舊的 Android 版本上運行良好,但不幸的是它不適用於 3.0 及更高版本(嘗試過 3.2 和 4.0.3)。 出於某種原因,在較新的平台上永遠不會調用 onClick 方法。 有人為此寫了一個錯誤報告: http : //code.google.com/p/android/issues/detail?id=16245
無法在較新的平台上運行意味着我需要不同的解決方案。 在我的應用程序中,在開始時模擬一個帶有隱藏“虛擬”條目的未選擇微調器就足夠了。 如果“隱藏”項目被設置為選擇,那么點擊的每個項目都將導致回調。 對於某些人來說,一個缺點可能是什么都不會出現,但可以使用 Spinner 類覆蓋技巧來修復。
如果您真的想在您的微調器顯示添加一個編輯文本並設置可見性消失屬性時在您的 XML 中執行此任務; 並為微調器和在 view.onclicklisner 上設置的服裝適配器創建服裝適配器,當點擊事件觸發 EditText.setText("0"); 在活動集 edittext textWatcher 事件和事件塊中,您添加 onSpinerItem 事件塊代碼; 你的問題解決了
您必須執行以下步驟: 1. 為微調器創建一個 Adapter Customer 2. 在覆蓋 fun getDropDownView(...) 中,您必須放置一個 view.setOnClickListener {"interface"} 3. 創建一個接口
無需擴展微調器。 我按以下方式使用 SpinnerAdapter:
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//....
//Create an ArrayAdapter as usual
myArrayAdapter = new ArrayAdapter(mParentView.getContext(),R.layout.myarrayadapter_item);
//Create a Spinner as usual
mySpinner = (Spinner)mParentView.findViewById(R.id.myspinner);
//create a spinneradapter
//selecting the same item twice will not throw an onselectionchange event
//therefore add an OnTouchlistener to the DropDownView items
SpinnerAdapter o_SpinnerAdapter = new SpinnerAdapter() {
private ArrayAdapter m_ArrayAdapter = myArrayAdapter;
private View.OnTouchListener m_OnTouchListener = new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
//do something
return false; //return false, don't consume the event
};
@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
TextView o_TextView; //usual the view is a TextView
o_TextView = (TextView)m_ArrayAdapter.getView(position,convertView,parent);
o_TextView.setOnTouchListener(m_OnTouchListener); //Add the ontouchlistener
return o_TextView;
}
@Override
public void registerDataSetObserver(DataSetObserver observer) {
m_ArrayAdapter.registerDataSetObserver(observer);
}
@Override
public void unregisterDataSetObserver(DataSetObserver observer) {
m_ArrayAdapter.unregisterDataSetObserver(observer);
}
@Override
public int getCount() {
return m_ArrayAdapter.getCount();
}
@Override
public Object getItem(int position) {
return m_ArrayAdapter.getItem(position);
}
@Override
public long getItemId(int position) {
return m_ArrayAdapter.getItemId(position);
}
@Override
public boolean hasStableIds() {
return m_ArrayAdapter.hasStableIds();
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return m_ArrayAdapter.getView(position, convertView, parent);
}
@Override
public int getItemViewType(int position) {
return m_ArrayAdapter.getItemViewType(position);
}
@Override
public int getViewTypeCount() {
return m_ArrayAdapter.getViewTypeCount();
}
@Override
public boolean isEmpty() {
return m_ArrayAdapter.isEmpty();
}
};
//Set the SpinnerAdapter instead of myArrayAdapter
m_SpinnerDMXDeviceGroups.setAdapter(o_SpinnerAdapter);
//.......
}
希望能幫助到你
這個威脅對我很有幫助。 有兩句話可能會對某人有所幫助:
第一的:
您應該檢查 OnItemSelecListener 是否已經設置。 在我的例子中,我在設置 OnItemSelecListener 之前調用了 setSelection(),這導致了崩潰。
@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
AdapterView.OnItemSelectedListener selLis = getOnItemSelectedListener();
if (selLis != null) {
selLis.onItemSelected(this, getSelectedView(), position, getSelectedItemId());
}
}
}
第二:
Android Studio 要求我不要派生自 Spinner,而是派生自 AppCompatSpinner。 這看起來像這樣:
import androidx.appcompat.widget.AppCompatSpinner;
public class SpinnerReclickable extends AppCompatSpinner {
...
試試這個。
@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();
}
}
希望它可以工作。
當您再次單擊當前選定的項目時,它不會觸發任何事件。 因此,您無法捕獲 setOnItemSelectedListener 以供微調器響應。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.