微调器:当所选项目保持不变时,不调用 onItemSelected
Posted
技术标签:
【中文标题】微调器:当所选项目保持不变时,不调用 onItemSelected【英文标题】:Spinner : onItemSelected not called when selected item remains the same 【发布时间】:2012-06-06 22:24:46 【问题描述】:我的Spinner
有一个OnItemSelectedListener
,但是当所选项目与前一个相同时,它不会被调用。显然OnClickListener
不是Spinner
的选项。
每次用户单击某个项目时,我都需要捕捉。有什么想法吗?
也许Spinner
位于ActionBar
内部这一事实会干扰正常行为?
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)
inflater.inflate(R.menu.tracklist_menu, menu);
Spinner spinner = (Spinner) menu.findItem(R.id.option_ordering_spinner)
.getActionView();
spinner.setAdapter(mSpinnerAdapter);
spinner.setSelection(PrefsHelper.getOrderingSpinnerPos(prefs));
spinner.setOnItemSelectedListener(new OnItemSelectedListener()
@Override
public void onItemSelected(AdapterView<?> parent, View view,
int position, long id)
String str = "selected";
System.out.println(str);
if (optionMenuInitialized)
switch (position)
case 0:
// rdm
getActivity()
.sendBroadcast(
new Intent(
MyIntentAction.DO_RESHUFFLE_PLAYLIST));
smp.setCurrentTracklistCursorPos(-1);
trackAdapter.notifyDataSetChanged();
break;
case 1:
// artist
getActivity()
.sendBroadcast(
new Intent(
MyIntentAction.DO_ORDER_PLAYLIST_BY_ARTIST));
smp.setCurrentTracklistCursorPos(-1);
trackAdapter.notifyDataSetChanged();
break;
case 2:
// folder
getActivity()
.sendBroadcast(
new Intent(
MyIntentAction.DO_ORDER_PLAYLIST_BY_FOLDER));
smp.setCurrentTracklistCursorPos(-1);
trackAdapter.notifyDataSetChanged();
break;
PrefsHelper.setOrderingSpinnerPos(prefEditor, position);
prefEditor.commit();
optionMenuInitialized = true;
@Override
public void onNothingSelected(AdapterView<?> parent)
);
【问题讨论】:
你看这篇文章了吗***.com/questions/3928071/… 是的,这并没有帮助......无论如何,我刚刚找到了解决方案,并准备在这里写下来=) 这不相关,但将 Spinner 可见性设置为 VIEW.GONE 也会导致此问题。 【参考方案1】:从@Vishal Yadav 的回答进一步,如果您想通过调用spinner.setSelection(pos, false);
来设置初始位置而不触发 OnItemSelectedListener,那么自定义微调器应该是:
public class CSpinner extends AppCompatSpinner
private int lastPosition = 0;
public CSpinner(Context context, AttributeSet attrs)
super(context, attrs);
@Override
public void setSelection(int position, boolean animate)
OnItemSelectedListener listener = getOnItemSelectedListener();
setOnItemSelectedListener(null);
super.setSelection(position, animate);
lastPosition = position;
setOnItemSelectedListener(listener);
@Override
public void setSelection(int position)
super.setSelection(position);
boolean sameSelected = lastPosition == getSelectedItemPosition();
OnItemSelectedListener onItemSelectedListener = getOnItemSelectedListener();
if (sameSelected && onItemSelectedListener != null)
onItemSelectedListener.onItemSelected(this, getSelectedView(), position, getSelectedItemId());
lastPosition = position;
【讨论】:
【参考方案2】:如果它仍然是实际的,正确的回调调用应该是
@Override
public void setSelection(int position)
super.setSelection(position);
if(listener != null)
listener.onItemSelected(this, getChildAt(position), position, 0);
【讨论】:
这不是正确的回调。首先,您应该使用getItemIdAtPosition(position)
作为最后一个参数,第二个getChildAt(position)
在这种情况下不应该使用。 Spinner
s 使用View
回收就像ListView
s 一样,所以getChildAt
不能用于从适配器中检索位置的第项。
最后一个参数是正确的,以获得正确的 ID。但是要获得视图,可以使用 getChildAt() ,因为如果它存在,我们将获得正确的视图,如果不存在,我们将获得 null。在大多数情况下,当单击项目并因此在微调器中可见时会调用代码,因此 getChildAt() 返回正确的视图。至少我从来没有遇到过返回错误视图的情况。
不,你不正确。例如,微调器有 100 个项目,最后一个被单击。但是只有10个可见。在这种情况下,getChildAt()
将始终返回 99 位置的 null,因为它没有 100 个子 Views
。你不能依赖getChildAt()
和任何AdapterView
s。您可以致电onItemSelected(this, getSelectedView(), position, getItemIdAtPosition(position));
。
也许我弄错了,但您是否想说 getChildAt() 仅返回索引 0-9 的视图(对于 10 个可见项目)?我没试过,但听起来很奇怪。我希望它应该从底层适配器返回适配器中所有项目的视图 - 更具体地说,如果它是可见的,它应该返回视图,否则它应该返回 null。当我有一些我现在没有的空闲时间时,我会尝试。当我单击第 100 个项目(位置 99)时,具有 100 个项目(索引 0-99)的微调器将返回 null?我不明白。当我单击最后一项时,它必须是可见的并且 getChildAt(99) 必须返回它。还是不行?
你知道view recycling吗?每个项目都没有View
s,只添加了一些必要的View
s。这就是getChild(99)
将返回 null 的原因。 10 和 99 只是示例,显然这取决于当前情况。【参考方案3】:
这是一个更好的实现 -
自定义微调器类 -
import android.content.Context;
import android.util.AttributeSet;
import androidx.appcompat.widget.AppCompatSpinner;
public class CSpinner extends AppCompatSpinner
private int lastPosition = 0;
public CSpinner(Context context, AttributeSet attrs)
super(context, attrs);
@Override
public void setSelection(int position)
super.setSelection(position);
boolean sameSelected = lastPosition == getSelectedItemPosition();
OnItemSelectedListener onItemSelectedListener = getOnItemSelectedListener();
if (sameSelected && onItemSelectedListener != null)
onItemSelectedListener.onItemSelected(this, getSelectedView(), position, getSelectedItemId());
lastPosition = position;
设置监听器 -
spn.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener()
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id)
Log.d("onItemSelected", String.valueOf(position));
@Override
public void onNothingSelected(AdapterView<?> parent)
);
【讨论】:
点赞!!!你拯救了我的一天。【参考方案4】:我找到了一个简单的解决方案
只需再次调用 setAdapter 来代替第二个微调器的 notifyDataSetChanged
【讨论】:
【参考方案5】:重写了通用解决方案,但使用:
-
牢记androidx
扩展自
AppCompatSpinner
使用内置的OnItemSelectedListener
侦听器而不是创建自己的侦听器
添加了初始侦听器调用技巧
这里:
import android.content.Context;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatSpinner;
public class FixedSpinner extends AppCompatSpinner
// add other constructors that you need
public FixedSpinner(Context context, int mode)
super(context, mode);
private void processSelection(int position)
boolean sameSelected = position == getSelectedItemPosition();
final OnItemSelectedListener listener = getOnItemSelectedListener();
if (sameSelected && listener != null)
// Spinner does not call the OnItemSelectedListener if the same item is selected, so do it manually now
listener.onItemSelected(this, getSelectedView(), position, getSelectedItemId());
@Override
public void setSelection(int position)
processSelection(position);
super.setSelection(position);
@Override
public void setSelection(int position, boolean animate)
processSelection(position);
super.setSelection(position, animate);
@Override
public void setOnItemSelectedListener(@Nullable OnItemSelectedListener listener)
// This hack fixes bug, when immediately after initialization, listener is called
// To make this fix work, first add data, only then set listener
// Having done this, you may refresh adapter data as many times as you want
setSelection(0, false);
super.setOnItemSelectedListener(listener);
【讨论】:
太棒了,谢谢。这解决了我在使用微调器时遇到的问题。 布尔sameSelected = position == getSelectedItemPosition();始终为 TRUE,getSelectedItemPosition() 方法返回与位置相同的值。【参考方案6】:我遇到了同样的问题,我通过在每次适配器更改项目时设置 onItemSelectedListener 来解决它。
【讨论】:
【参考方案7】:要让你的微调器改变,尽管最后一个索引的值是选择的,只需使用:
spinner.setSelection(0);
在调用其他选择之前
spinner.setSelection(number);
这样,微调器将触发两次 OnItemSelected 事件。只需确保第二次执行您需要的任何操作即可。
【讨论】:
同一个item位置为0怎么办? spinner.setSelection(0); if(number!=0) spinner.setSelection(number);【参考方案8】:这里有一个更好的实现:
public class SpinnerPlus extends Spinner
AdapterView.OnItemSelectedListener listener;
public SpinnerPlus(Context context, AttributeSet attrs)
super(context, attrs);
@Override
public void setSelection(int position)
super.setSelection(position);
if (listener != null)
listener.onItemSelected(this, getSelectedView(), position, 0);
public void setOnItemSelectedEvenIfUnchangedListener(
AdapterView.OnItemSelectedListener listener)
this.listener = listener;
【讨论】:
【参考方案9】:最简单的解决方案:
spinner.performItemClick(view,position,id)
【讨论】:
【参考方案10】:我发现了这项工作而不是提供的工作
/** 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());
【讨论】:
如果没有设置OnItemSelectedListener
并且选择了相同的项目,你将得到一个NPE。
这个也为我工作,接受的答案没有
感谢原作者:***.com/a/11323043/3726133【参考方案11】:
好的,我终于找到了解决方案,通过创建我自己的扩展 Spinner 的类:
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 (listener != null)
listener.onItemSelected(null, null, position, 0);
public void setOnItemSelectedEvenIfUnchangedListener(
OnItemSelectedListener listener)
this.listener = listener;
【讨论】:
你能分享一些关于如何为ActionBar导航列表实现相同功能的想法吗? @benoffi7 你的答案在哪里? @dhams 抱歉,我的回答有误,我将其删除。 @elgui 这一行 listener.onItemSelected(null, null, position, 0);给我一个 NPE,你能告诉我如何让 AdapterView & View 在上面的行中通过。谢谢 如何在 listener.onItemSelected(null, null, position, 0); 中获取父级和视图?以上是关于微调器:当所选项目保持不变时,不调用 onItemSelected的主要内容,如果未能解决你的问题,请参考以下文章