Android Spinner OnItemSelected仅限用户交互时触发

6
我知道这个问题已经被多次回答,但我没有发现它们中的任何一个令人满意,特别是没有优雅的解决方法。
问题在于OnItemSelected不仅在用户选择项目时触发,而且在程序设置选项时也会触发。
有些答案建议在程序员设置Spinner值时设置标志。然而,有时其他Android代码会在你的代码之外设置该值。
Android设置该值的常见位置是在Spinner实例化时进行。有些答案解决了这个特定的问题。然而,Android会在许多地方出错并重新实例化Spinner。找到所有这些地方不太优雅。
因此,问题是:如何将他们的OnItemSelectedListener代码仅附加到用户与UI交互所做的选择?
  • Spinner.setOnItemClickListener似乎完美地回答了这个问题,但Google已经禁用了它
  • AdapterView.setOnClickListener也似乎是一个自然的候选人,但它也会生成运行时错误
接下来的方法是扩展Spinner并开始覆盖方法,但这当然意味着要处理APIs(我宁愿不这样做,或者至少希望有其他用户与我一起开发)。
2个回答

4

因此,我发布了一个答案,但是任何批评,改进或其他更优雅的答案都是受欢迎的。

关键是覆盖onClick以设置一个标志,将onItemSelectedListener与用户交互联系起来,并触发onItemClickedListener。

如果您不感到舒适,可以使用API setOnItemClickedListener(也许是为了未来的兼容性),当然也可以替换自己的方法。我只是觉得应该一直实现onItemClickedListener以达到这个效果,这是我的微小抗议。

另外,如果有人能想出spinnerTouched标志被短路(保持真实的时间比它应该长)的原因,请告诉我们,以便我们可以解决。到目前为止,它似乎运行得相当不错。

public class OnItemClickSpinner extends Spinner implements AdapterView.OnItemSelectedListener {

    boolean spinnerTouched = false;
    OnItemClickListener onItemClickListener = null;
    OnItemSelectedListener onItemSelectedListener = null;

    public OnItemClickSpinner(Context context) {
        super(context);
        super.setOnItemSelectedListener(this);
    }

    public OnItemClickSpinner(Context context, AttributeSet attrs) {
        super(context, attrs);
        super.setOnItemSelectedListener(this);
    }

    public OnItemClickSpinner(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        super.setOnItemSelectedListener(this);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        spinnerTouched = true;
        return super.onTouchEvent(event);
    }

    @Override
    public void setOnItemClickListener(OnItemClickListener listener) {
        onItemClickListener = listener;
    }

    @Override
    public void setOnItemSelectedListener(OnItemSelectedListener onItemSelectedListener) {
        this.onItemSelectedListener = onItemSelectedListener;
        super.setOnItemSelectedListener(this);
    }

    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
        if(spinnerTouched && this.onItemClickListener!=null) this.onItemClickListener.onItemClick(parent,view,position,id);
        if(this.onItemSelectedListener!=null) this.onItemSelectedListener.onItemSelected(parent,view,position,id);
        spinnerTouched=false;
    }

    @Override
    public void onNothingSelected(AdapterView<?> parent) {
        if(this.onItemSelectedListener!=null) this.onItemSelectedListener.onNothingSelected(parent);
        spinnerTouched=false;
    }
}

3

我在 iOS 的一些 UI 组件上遇到了类似的问题。我决定使用类似的 hack。

所以,在拥有 spinner 的对象中的某个地方,比如一个 activity 中,声明一个成员变量:private var isSelectionFromTouch: Boolean = false

其余相关代码:

init {
    spinner.onItemSelectedListener = this
    spinner.setOnTouchListener { _, _ ->
        this.isSelectionFromTouch = true
        false
    }
}

// On Item Selected

override
fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
    if (!isSelectionFromTouch) { return }
    Log.d(TAG, "item selected. do something.")
    isSelectionFromTouch = false
}

override
fun onNothingSelected(parent: AdapterView<*>?) {
    isSelectionFromTouch = false
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接