437

我想了一些不太优雅的方法来解决这个问题,但我知道我一定遗漏了一些东西。

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.
    }
}
}
4

33 回答 33

395

Runnables 的使用是完全不正确的。

setSelection(position, false);在初始选择之前使用setOnItemSelectedListener(listener)

通过这种方式,您可以在没有动画的情况下设置您的选择,这会导致调用 on item selected 侦听器。但是侦听器为空,因此没有运行任何内容。然后分配您的听众。

所以遵循这个确切的顺序:

Spinner s = (Spinner)Util.findViewById(view, R.id.sound, R.id.spinner);
s.setAdapter(adapter);
s.setSelection(position, false);
s.setOnItemSelectedListener(listener);
于 2013-06-27T07:19:03.797 回答
205

参考 Dan Dyer 的回答,尝试OnSelectListener在一个post(Runnable)方法中注册:

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

通过为我这样做,最终发生了希望的行为。

在这种情况下,这也意味着侦听器仅在更改的项目上触发。

于 2012-02-21T10:09:32.300 回答
81

我本来希望您的解决方案能够正常工作-尽管如果您在设置侦听器之前设置适配器,则不会触发选择事件。

话虽如此,一个简单的布尔标志将允许您检测流氓优先选择事件并忽略它。

于 2010-04-01T17:53:21.343 回答
55

我创建了一个小的实用程序方法来更改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 线程是异步的,因此您必须在连续的处理程序帖子中执行此操作。

于 2012-11-23T11:51:09.537 回答
35

不幸的是,对于这个问题,两个最常见的建议解决方案,即计算回调发生次数和发布一个 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(); }
}
于 2013-06-15T15:40:11.583 回答
32

当我不想这样做时,我遇到了很多关于旋转器射击的问题,而且这里的所有答案都是不可靠的。他们工作 - 但只是有时。您最终会遇到它们将失败并在您的代码中引入错误的情况。

对我有用的是将最后选择的索引存储在变量中并在侦听器中对其进行评估。如果它与新选择的索引相同,则不执行任何操作并返回,否则继续侦听器。做这个:

//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

}

当我这么说的时候相信我,这是迄今为止最可靠的解决方案。一个黑客,但它的工作原理!

于 2014-08-05T15:39:09.317 回答
27

只是为了充实使用​​ onTouchListener 来区分对 setOnItemSelectedListener 的自动调用(这是 Activity 初始化等的一部分)与由实际用户交互触发的调用之间的提示,我在这里尝试了一些其他建议后做了以下操作和发现它使用最少的代码行运行良好。

只需为您的活动/片段设置一个布尔字段,例如:

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;
于 2016-02-06T14:53:20.533 回答
26

我处于类似情况,我有一个简单的解决方案适合我。

它看起来像方法setSelection(int position)并且setSelected(int position, boolean animate)具有不同的内部实现。

当您使用setSelected(int position, boolean animate)带有 false animate 标志的第二种方法时,您无需触发onItemSelected侦听器即可获得选择。

于 2012-12-07T12:05:26.590 回答
13
spinner.setSelection(Adapter.NO_SELECTION, false);
于 2014-06-21T21:05:11.167 回答
10

如果您在代码中进行选择,则会发生这种情况;

   mSpinner.setSelection(0);

而不是上面的语句使用

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

编辑:此方法不适用于 Mi Android 版 Mi UI。

于 2017-04-20T07:28:46.790 回答
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);

这样,不会触发任何事件,也不会中断用户交互。这大大降低了我的代码复杂性。这应该包含在库存 Android 中,因为它确实是 PITA。

于 2015-05-31T08:42:44.620 回答
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) {
                }

            });

        }
    });
于 2015-07-29T15:22:57.727 回答
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
}
于 2016-08-17T07:17:28.557 回答
3

我找到了更优雅的解决方案。它涉及计算调用了多少次 ArrayAdapter(在您的情况下为“适配器”)。假设您有 1 个微调器并且您调用:

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
}
于 2013-02-08T08:22:49.667 回答
3

由于没有什么对我有用,而且我认为我有超过 1 个微调器(恕我直言,持有 bool 地图是一种矫枉过正),我使用标签来计算点击次数:

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) {
        }
    });
于 2015-08-24T17:39:20.587 回答
2

我的小贡献是对上述一些内容的变体,它适合我几次。

将整数变量声明为默认值(或保存在首选项中的最后使用值)。使用 spinner.setSelection(myDefault) 在注册侦听器之前设置该值。在 onItemSelected 检查新的微调器值是否等于您在运行任何其他代码之前分配的值。

如果用户再次选择相同的值,这具有不运行代码的额外优势。

于 2013-06-21T11:09:57.340 回答
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

此包装器还将处理在布局创建期间调用侦听器的问题。

玩得开心,詹斯。

于 2014-10-27T20:42:57.773 回答
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()例如Fragment

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

wheresetSelection()是封闭视图中的一个方法,看起来像这样,它既可以通过用户选择事件调用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
}
于 2017-11-22T22:43:05.787 回答
0

我会试着打电话

spinner.setOnItemSelectedListener(new MyOnItemSelectedListener());

在你调用 setAdapter() 之后。也尝试在适配器之前调用。

您始终可以使用子类化解决方案,您可以在其中将布尔标志包装到覆盖的 setAdapter 方法以跳过事件。

于 2010-04-01T18:26:30.250 回答
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;
}
于 2012-07-14T16:15:18.627 回答
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;
}
于 2015-03-17T21:55:37.440 回答
0

如果您需要即时重新创建活动,例如:更改主题,简单的标志/计数器将不起作用

使用 onUserInteraction() 函数检测用户活动,

参考:https ://stackoverflow.com/a/25070696/4772917

于 2015-04-10T09:18:44.567 回答
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) {

        }
    });

完毕

于 2015-04-13T09:45:12.240 回答
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) {

     }
});
于 2015-09-13T14:39:12.967 回答
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 回调。

于 2016-12-20T12:12:08.687 回答
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
            }
        });
于 2017-03-09T08:49:12.080 回答
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) {
      }
  });
于 2017-08-29T08:06:20.943 回答
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;
    }
}
于 2017-09-22T10:05:28.530 回答
0

我可能在帖子中回答得太晚了,但是我设法使用 Android 数据绑定库 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>在标签中包含您的布局文件

于 2017-10-18T06:06:41.117 回答
-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) {
            }
        });
于 2014-12-26T20:25:00.823 回答
-1

这不是一个特别优雅的解决方案,但我发现它工作可靠。只需从 onResume 内部将侦听器设置在延迟线程上。然后,您可以自由地进行任何您喜欢的初始化,然后 UI 将进行任何布局更改,然后设置侦听器。

Thread t = new Thread(){
            public void run() {
                try{                
                    Thread.sleep(1000);
                    getActivity().runOnUiThread(new Runnable() {                        
                        @Override
                        public void run() {
                            setSpinnerListeners();

                        }
                    });
                }
                catch(Exception e){
                    e.printStackTrace();
                }
            };
        };
        t.start();
于 2015-07-08T17:56:55.873 回答
-1

设计一个通用的微调器,只需输入数据并让用户选择,优点:

1. 保持应用程序的微调器样式相同。
2. 在任何地方启动微调器。
3.易于处理的链接微调器(使用不同的数据再次启动ReuseSpinner)。

我的演示示例:ReuseSpinner
将数据传递给 ReuseSpinner :

Intent intent = new Intent(MainActivity.this, SpinnerActivity.class);
intent.putExtra(SpinnerActivity.Extra_Resource, arrayList);
startActivityForResult(intent, mRequestCode_select_country_prompt);

获取用户的选择:

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        // TODO Auto-generated method stub
        super.onActivityResult(requestCode, resultCode, data);

        if(requestCode == mRequestCode_select_country && resultCode == RESULT_OK){
            if(data != null){
                Map.Entry<String,String> entry = (Map.Entry<String,String>) data.getSerializableExtra(SpinnerActivity.Result_Data);
                if(entry != null){
                    Log.i(TAG, String.format("get result -> key:%s , value:%s", entry.getKey(), entry.getValue()));

                }
            }
        }
    }
于 2016-08-04T09:50:40.207 回答
-1

不是一个完美的解决方案,但如果您对将起始字符串作为占位符感到满意,您可以添加占位符弹簧值(“Day_of_Work_Out”)

  @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
            String name = (String) parent.getItemAtPosition(position);
            if (name.equals("Day_of_Work_Out")) {

            }else {

                workOutD = name;
                Intent intent = new Intent();
                workOutNam = workOutName.getText().toString();

                if (workOutNam == null) {

                    startActivity(intent);
                    Log.i("NewWorkOutActivity","Name is null");

                }else {
                    Log.i("NewWorkOutActivity","Name Not null");
                    Toast.makeText(NewWorkOutActivity.this, "Please Select a Day", Toast.LENGTH_LONG).show();
                }


            }
        }
于 2017-04-26T22:41:35.750 回答