33

当我创建我的活动时,我设置了一个 Spinner,为它分配了一个监听器和一个初始值。我知道onItemSelected在应用程序初始化期间会自动调用回调。我觉得奇怪的是,当设备旋转时,这种情况会发生两次,给我带来了一些我必须以某种方式规避的问题。如果微调器初始选择为零,则不会发生这种情况我能够隔离问题,这是触发它的最简单的活动:

public class MainActivity extends Activity implements OnItemSelectedListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Log.i("Test","Activity onCreate");
    setContentView(R.layout.activity_main);
    ((Spinner)findViewById(R.id.spinner1)).setSelection(2);
    ((Spinner)findViewById(R.id.spinner1)).setOnItemSelectedListener(this);
}
@Override
public void onItemSelected(AdapterView<?> spin, View selview, int pos, long selId)
{
    Log.i("Test","spin:"+spin+" sel:"+selview+" pos:"+pos+" selId:"+selId);
}
@Override
public void onNothingSelected(AdapterView<?> arg0) {}
}

这是应用程序启动然后设备旋转时显示的 logcat:

    I/Test( 9881): spin:android.widget.Spinner@4052f508 sel:android.widget.TextView@40530b08 pos:2 selId:2
    I/Test( 9881): Activity onCreate
    I/Test( 9881): spin:android.widget.Spinner@40535d80 sel:android.widget.TextView@40538758 pos:2 selId:2
    I/Test( 9881): spin:android.widget.Spinner@40535d80 sel:android.widget.TextView@40538758 pos:2 selId:2

这是预期的行为吗?我错过了什么吗?

4

10 回答 10

49

设法在另一个stackoverflow问题中找到解决方案:

spinner.post(new Runnable() {
    public void run() {
        spinner.setOnItemSelectedListener(listener);
    }
});
于 2013-04-04T18:27:38.853 回答
25

一般来说,触发 onItemSelected 调用的事件似乎很多,而且很难跟踪所有这些事件。此解决方案允许您仅使用 OnTouchListener 响应用户发起的更改。

为微调器创建侦听器:

public class SpinnerInteractionListener implements AdapterView.OnItemSelectedListener, View.OnTouchListener {

    boolean userSelect = false;

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        userSelect = true;
        return false;
    }

    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
        if (userSelect) { 
            // Your selection handling code here
            userSelect = false;
        }
    }

}

将侦听器作为 OnItemSelectedListener 和 OnTouchListener 添加到微调器:

SpinnerInteractionListener listener = new SpinnerInteractionListener();
mSpinnerView.setOnTouchListener(listener);
mSpinnerView.setOnItemSelectedListener(listener);
于 2015-02-11T23:38:05.807 回答
9

只需在设置侦听器之前使用 setSelection(#, false) :

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    spinner.setSelection(2, false);
    spinner.setOnItemSelectedListener(this);
}

关键是第二个参数。它说不要为过渡设置动画,立即执行操作并防止 onItemSelected 被触发两次,因为系统已经进行了调用。

于 2017-07-07T20:31:51.087 回答
7

第一次onItemSelected运行时,view尚未充气。第二次它已经膨胀了。解决方案是onItemSelectedif (view != null).

@Override
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
    if (view != null) { 
        //do things here

    }
}
于 2017-06-11T15:12:39.223 回答
2

这就是我所做的:

做一个局部变量

Boolean changeSpinner = true;

在 saveInstanceMethod 上保存 spinner 的选中项位置

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putInt("ItemSelect",mySpinner.getSelectedItemPosition());
}

然后在创建的活动上从 savedInstanceState 获取该 int ,如果 int 为 != 0 则将布尔变量设置为 false;

@Override
    public void onActivityCreated(Bundle savedInstanceState) {

    if (savedInstanceState!=null) {
        if (savedInstanceState.getInt("ItemSelect")!=0) {
           changeSpinner = false;
        }
    }

}

最后在微调器的 OnItemSelected 上执行此操作

mySpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
    public void onItemSelected(AdapterView<?> parent,android.view.View v, int position, long id) {
        if (changeSpinner) {
           [...]
        } else {
           changeSpinner= true;
        }
    });

所以,第一次 when is called 什么都不做,只是将布尔变量设为 true,第二次将执行代码。也许不是最好的解决方案,但它有效。

于 2015-02-14T23:56:46.370 回答
1

我正在更新@Andres Q. 在 Kotlin 中的答案。

创建一个使用 Spinner 的内部类

inner class SpinnerInteractionListener : AdapterView.OnItemSelectedListener, View.OnTouchListener {
        override fun onNothingSelected(parent: AdapterView<*>?) {

        }

        override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
            if (userSelect) {
                //Your selection handling code here
                userSelect = false
            }
        }

        @SuppressLint("ClickableViewAccessibility")
        override fun onTouch(v: View?, event: MotionEvent?): Boolean {
            userSelect = true
            return false
        }

        internal var userSelect = false
    }

然后将外部实例变量声明onCreate()为全局变量

lateinit var spinnerInteractionListener: SpinnerInteractionListener

onCreate() 然后在里面初始化它

spinnerInteractionListener = SpinnerInteractionListener()

并像使用它一样

spinnerCategory.onItemSelectedListener = spinnerInteractionListener
spinnerCategory.setOnTouchListener(spinnerInteractionListener)

spinnerCategory微调器

于 2018-10-03T10:40:47.480 回答
0

试试这个:

boolean mConfigChange = false;

@Override
protected void onCreate(Bundle savedInstanceState) {
    // TODO Auto-generated method stub
    mConfigChange = false;
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_mainf);

    Log.i("SpinnerTest", "Activity onCreate");
    ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this, R.array.colors,
            android.R.layout.simple_spinner_item);
    adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    ((Spinner) findViewById(R.id.spin)).setAdapter(adapter);

     ((Spinner) findViewById(R.id.spin)).setSelection(2);
    ((Spinner) findViewById(R.id.spin)).setOnItemSelectedListener(this);

}

@Override
protected void onResume() {
    mConfigChange = true;
    super.onResume();
}

@Override
public void onItemSelected(AdapterView<?> spin, View selview, int pos, long selId) {
    if (!mConfigChange)
        Log.i("Test", "spin:" + spin + " sel:" + selview + " pos:" + pos + " selId:" + selId);
    else
        mConfigChange = false;
}
于 2013-01-28T13:32:57.047 回答
0

一旦你知道有项目列表和要选择的位置,你就可以调用setSelection,这样你onItemSelected就可以避免被调用两次。

我创建了一篇关于我认为更好的方法的文章如何避免 onItemSelected 在 Spinners 中被调用两次

于 2016-04-02T15:41:46.863 回答
0

我编写了一个扩展函数,它跳过所有选择事件,除了用户发起的那些。如果您不使用第一个默认微调器位置,请不要忘记覆盖 defPosition

fun Spinner.setFakeSelectSkipWatcher(execute: (position: Int) -> Unit, defPosition: Int = 0) {
val listener = object : AdapterView.OnItemSelectedListener {
    var previousIsNull = -1
    var notSkip = false
    override fun onItemSelected(p0: AdapterView<*>?, view: View?, position: Int, p3: Long) {
        if (notSkip) execute(position)
        else {
            if ((view != null && position == defPosition) ||
                (view == null && position == defPosition) ||
                (view != null && previousIsNull == 1 && position != defPosition)
            ) notSkip = true
        }
        previousIsNull = if (view == null) 1 else 0
    }
    override fun onNothingSelected(p0: AdapterView<*>?) {}
}
onItemSelectedListener = listener

}

于 2021-04-18T07:42:02.250 回答
0

在 kotlin stateflow 中让它变得简单。在片段中,它也可以保存旋转数据。在我的代码中,我解决了

在视图模型中:

private val _selectedPosition = MutableStateFlow(0)
val selectedPosition = _selectedPosition.asStateFlow()

fun setPosition(position: Int) {
    _selectedPosition.value = position
}

在片段中

val selectedPosition= viewModel.selectedPosition.value
spinner.setSelection(selectedPosition)
于 2022-02-22T23:12:37.223 回答