Android Spinner选择

当以编程方式更改微调器选择时,以及当用户实际单击微调器控件时,都会调用OnItemSelectedListener事件处理程序。 是否可以确定某个事件是否由用户选择以某种方式触发?

或者是否有另一种方法来处理微调器用户选择?

要解决方法,您需要记住最后选择的位置。 然后在您的微调器侦听器内部将最后选择的位置与新位置进行比较。 如果它们不同,则处理事件并使用新的位置值更新最后选择的位置,否则只是跳过事件处理。

如果在代码中的某个位置,您将以编程方式更改微调器的选定位置,并且您不希望侦听器处理该事件,则只需将最后选择的位置重置为您要设置的位置。

是的,Android中的Spinner很痛苦。 我甚至会说疼痛从它的名字开始 – “Spinner”。 是不是有点误导? :)就我们所说的而言,你也应该知道有一个错误 – Spinner可能无法恢复(并不总是)其状态(在设备旋转时),因此请确保手动处理Spinner的状态。

很难相信一年半之后,这个问题仍然存在,并继续让人们陷入困境……

我想在阅读Arhimed最有用的post之后我想出了解决方法(谢谢,我同意旋转器很痛苦!)。 我一直在做的是避免这些误报是使用一个简单的包装类:

import android.util.Log; import android.view.View; import android.widget.AdapterView; import android.widget.AdapterView.OnItemSelectedListener; public class OnItemSelectedListenerWrapper implements OnItemSelectedListener { private int lastPosition; private OnItemSelectedListener listener; public OnItemSelectedListenerWrapper(OnItemSelectedListener aListener) { lastPosition = 0; listener = aListener; } @Override public void onItemSelected(AdapterView< ?> aParentView, View aView, int aPosition, long anId) { if (lastPosition == aPosition) { Log.d(getClass().getName(), "Ignoring onItemSelected for same position: " + aPosition); } else { Log.d(getClass().getName(), "Passing on onItemSelected for different position: " + aPosition); listener.onItemSelected(aParentView, aView, aPosition, anId); } lastPosition = aPosition; } @Override public void onNothingSelected(AdapterView< ?> aParentView) { listener.onNothingSelected(aParentView); } } 

它所做的就是为已经选择的相同位置捕获项目选择事件(例如,位置0的初始自动触发选择),并将其他事件传递给包装的侦听器。 要使用它,您所要做的就是修改代码中调用侦听器的行以包含包装器(当然还要添加结束括号),所以不要说:

 mySpinner.setOnItemSelectedListener(new OnItemSelectedListener() { ... }); 

你有这个:

 mySpinner.setOnItemSelectedListener(new OnItemSelectedListenerWrapper(new OnItemSelectedListener() { ... })); 

显然,一旦你测试了它,你可以摆脱Log调用,你可以添加重置最后一个位置的能力(当然,你必须保留对实例的引用,而不是声明正如Arhimed所说的那样。

希望这可以帮助某些人被这种奇怪的行为所驱使;-)

在过去,我做过这样的事情来区分

 internal++; // 'internal' is an integer field initialized to 0 textBox.setValue("...."); // listener should not act on this internal setting internal--; 

然后在textBox的监听器中

 if (internal == 0) { // ... Act on user change action } 

我使用++和 – 而不是将布尔值设置为’true’,这样当方法嵌套其他可能也设置内部更改指示符的方法时,不必担心。

我最近在使用微调器时出现这种情况,并且互联网没有find合适的解决方案。

我的应用场景:

X个微调器(动态,每个cpu为2,最小和最大),用于设置和查看CPU频率。 它们在应用程序启动时填充,并且它们还获得cpu集的当前最大/最小频率。 线程在后台运行并每秒检查更改并相应地更新微调器。 如果用户设置了微调器内的新频率,则设置新频率。

问题是线程访问setSelection以更新当前频率,而当前频率又调用我的监听器,而我无法知道是用户还是更改了值的线程。 如果是线程,我不希望调用监听器,因为没有必要改变频率。

我提出了一个完全符合我需求的解决方案,并在你的电话中围绕听众工作:)(我认为这个解决方案可以让你获得最大的控制权)

我扩展了Spinner:

 import android.content.Context; import android.widget.Spinner; public class MySpinner extends Spinner { private boolean call_listener = true; public MySpinner(Context context) { super(context); } public boolean getCallListener() { return call_listener; } public void setCallListener(boolean b) { call_listener = b; } @Override public void setSelection(int position, boolean lswitch) { super.setSelection(position); call_listener = lswitch; } } 

并创建了我自己的OnItemSelectedListener:

 import android.util.Log; import android.view.View; import android.widget.AdapterView; import android.widget.AdapterView.OnItemSelectedListener; public class SpinnerOnItemSelectedListener implements OnItemSelectedListener { public void onItemSelected(AdapterView< ?> parent, View view, int pos,long id) { MySpinner spin = (MySpinner) parent.findViewById(parent.getId()); if (!spin.getCallListener()) { Log.w("yourapptaghere", "Machine call!"); spin.setCallListener(true); } else { Log.w("yourapptaghere", "UserCall!"); } } @Override public void onNothingSelected(AdapterView< ?> arg0) { // TODO Auto-generated method stub } } 

如果您现在创建MySpinner,可以使用它来设置选择:

setSelection(position, callListener);

其中callListener为true或false。 True会调用监听器并且是默认的,这就是为什么用户交互被识别的原因,false也会调用监听器但是使用你想要的代码用于这种特殊情况,在我的情况下是例证:没什么。

我希望其他人觉得这很有用,并且如果这样的事情已经存在,我们可以长途跋涉

我也在互联网上寻找一个很好的解决方案,但没有find任何满足我需求的解决方案。 所以我在Spinner类上编写了这个扩展,这样你就可以设置一个简单的OnItemClickListener,它具有与ListView相同的行为。

只有当项目被“选中”时,才会调用onItemClickListener。

玩得开心!

  public class MySpinner extends Spinner { private OnItemClickListener onItemClickListener; public MySpinner(Context context) { super(context); } public MySpinner(Context context, AttributeSet attrs) { super(context, attrs); } public MySpinner(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override public void setOnItemClickListener(android.widget.AdapterView.OnItemClickListener inOnItemClickListener) { this.onItemClickListener = inOnItemClickListener; } @Override public void onClick(DialogInterface dialog, int which) { super.onClick(dialog, which); if (this.onItemClickListener != null) { this.onItemClickListener.onItemClick(this, this.getSelectedView(), which, this.getSelectedItemId()); } } } 

只是为了扩展aaamos上面的post,因为我没有50个回复点来评论,我在这里创建一个新的答案。

基本上,他的代码适用于初始Spinner选择为0的情况。但是为了概括它,我通过以下方式修改了他的代码:

 @Override public void setOnItemSelectedListener(final OnItemSelectedListener listener) { if (listener != null) super.setOnItemSelectedListener(new OnItemSelectedListener() { private static final int NO_POSITION = -1; private int lastPosition = NO_POSITION; @Override public void onItemSelected(AdapterView< ?> parent, View view, int position, long id) { if ((lastPosition != NO_POSITION) && (lastPosition != position)) listener.onItemSelected(parent, view, position, id); lastPosition = position; } @Override public void onNothingSelected(AdapterView< ?> parent) { listener.onNothingSelected(parent); } }); else super.setOnItemSelectedListener(null); } 

基本上,这段代码将忽略onItemSelected()的第一次触发,然后忽略所有后续的“相同位置”调用。

当然,这里的要求是以编程方式设置选择,但无论如何,如果默认位置不是0,则应该是这种情况。

我做了一些日志记录,发现它只在初始化时被调用,这很烦人。 无法看到所有这些代码的需要,我只是创建了一个初始化为保护值的实例variables,然后在第一次调用该方法后设置它。

我在调用onItemSelected方法时记录,否则只调用一次。

我有一个问题,它创建了两个东西,并意识到这是因为我在我的自定义适配器上调用add(),它已经引用了我引用的列表并已添加到适配器外部。 在我意识到这一点并删除了添加方法后,问题就消失了。

你们确定需要所有这些代码吗?

我知道这已经很晚了,但我想出了一个非常简单的解决方案。 它基于Arhimed的答案,它完全一样。 它也很容易实现。 参考接受的答案:

不受欢迎的onItemSelected调用