Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/226.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android 微调器';如果选择了非零位置,则在旋转后调用两次MSelected回调_Android_Android Widget_Android Spinner - Fatal编程技术网

Android 微调器';如果选择了非零位置,则在旋转后调用两次MSelected回调

Android 微调器';如果选择了非零位置,则在旋转后调用两次MSelected回调,android,android-widget,android-spinner,Android,Android Widget,Android Spinner,当我创建我的活动时,我设置了一个微调器,给它分配一个侦听器和一个初始值。我知道在应用程序初始化过程中会自动调用onItemSelected回调。我觉得奇怪的是,当设备旋转时,会发生两次,,这给我带来了一些问题,我必须以某种方式避免这些问题。如果微调器初始选择为零,则不会发生这种情况。我能够隔离问题,下面是触发它的最简单的活动: public class MainActivity extends Activity implements OnItemSelectedListener { @Overr

当我创建我的活动时,我设置了一个微调器,给它分配一个侦听器和一个初始值。我知道在应用程序初始化过程中会自动调用
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) {}
}
这是预期的行为吗?我遗漏了什么吗?

试试这个:

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;
}
boolean mConfigChange=false;
@凌驾
创建时受保护的void(Bundle savedInstanceState){
//TODO自动生成的方法存根
mConfigChange=false;
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i(“喷丝头测试”、“创建活动”);
ArrayAdapter=ArrayAdapter.createFromResource(此,R.array.colors,
android.R.layout.simple\u微调器(项目);
setDropDownViewResource(android.R.layout.simple\u微调器\u下拉菜单\u项);
((微调器)findViewById(R.id.spin)).setAdapter(适配器);
((旋转器)findViewById(R.id.spin)).setSelection(2);
((微调器)findViewById(R.id.spin)).setonimselectedlistener(this);
}
@凌驾
受保护的void onResume(){
mConfigChange=true;
super.onResume();
}
@凌驾
已选择公共位置(适配器视图旋转、视图选择视图、内部位置、长选择ID){
如果(!mConfigChange)
Log.i(“测试”、“旋转:+spin+”sel:+selview+”pos:+pos+”selId:+selId”);
其他的
mConfigChange=false;
}

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

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

一般来说,似乎有许多事件会触发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;
        }
    }

}
这就是我所做的:

做一个局部变量

Boolean changeSpinner = true;
在saveInstanceMethod上,保存微调器的选定项目位置

@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;
        }
    }

}
最后,在从微调器中选择的屏幕上,执行以下操作

mySpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
    public void onItemSelected(AdapterView<?> parent,android.view.View v, int position, long id) {
        if (changeSpinner) {
           [...]
        } else {
           changeSpinner= true;
        }
    });
mySpinner.setOnItemSelectedListener(新的AdapterView.OnItemSelectedListener(){
已选择公共视图(AdapterView父级、android.view.view v、int位置、长id){
if(更改微调器){
[...]
}否则{
changeSpinner=true;
}
});
因此,第一次调用时不会做任何事情,只需将布尔变量设为true,第二次将执行代码。
也许不是最好的解决方案,但它很有效。

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


我写了一篇关于我认为更好的方法的文章

第一次运行
onItemSelected
时,
视图还没有膨胀。第二次它已经膨胀了。解决方法是用
if(view!=null)
将方法包装在
onItemSelected

@覆盖
已选择公共位置(AdapterView父项、视图、整数位置、长id){
如果(视图!=null){
//在这里做事
}
}
在设置侦听器之前,只需使用setSelection(#,false):

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

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

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

创建一个使用微调器的内部类

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()中初始化它

spinnerInteractionListener = SpinnerInteractionListener()
像这样使用它

spinnerCategory.onItemSelectedListener = spinnerInteractionListener
spinnerCategory.setOnTouchListener(spinnerInteractionListener)

这里
spinnerCategory
是我编写了一个扩展函数,它跳过除用户启动的选择事件以外的所有选择事件。如果您使用的不是第一个默认spinner位置,请不要忘记覆盖取消位置

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
fun Spinner.setFakeSelectSkipWatcher(执行:(位置:Int)->单位,解除位置:Int=0){
val listener=对象:AdapterView.OnItemSelectedListener{
var previousIsNull=-1
var notSkip=false
已选择覆盖选项(p0:AdapterView?,view:view?,position:Int,p3:Long){
如果(不跳过)执行(位置)
否则{
if((视图!=null&&position==defPosition)||
(视图==null&&position==defPosition)||
(视图!=null&&previousIsNull==1&&position!=defPosition)
)notSkip=true
}
previousIsNull=如果(视图==null)1其他0
}
覆盖未选择的乐趣(p0:AdapterView?{}
}
onItemSelectedListener=监听器

}

谢谢,但这似乎并不能解决我的问题,它只会使第一次回调“无效”(但第二次在轮换后仍会触发).我已经在做类似的事情了,问题是第一次活动创建和设备轮换之间的呼叫数量明显不一致。你找到解决方案了吗?我仍然坚持这个问题…你能给这个问题添加一个链接吗?当然@AlvaroSantisteban,如果我没有弄错的话,它来自这个问题:但这不仅仅是一个问题一年后,我不再记得细节:(.3年后,非常感谢你不要这样做!这是一个丑陋的解决方法。正确和最简单的方法是使用setSelection(#,false)在设置侦听器之前。请参阅,在设置侦听器时,这解决了最初的双重调用。不幸的是,没有在每次更改选择时解决双重调用。这是一个非常优雅的解决方案
lateinit var spinnerInteractionListener: SpinnerInteractionListener
spinnerInteractionListener = SpinnerInteractionListener()
spinnerCategory.onItemSelectedListener = spinnerInteractionListener
spinnerCategory.setOnTouchListener(spinnerInteractionListener)
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