Spinner:如何知道项目选择是通过编程方式更改还是通过用户操作通过UI更改

时间:2010-05-14 09:36:31

标签: android

我的代码运行spinner的OnItemSelectedListener事件。所以当我在方法中时:

public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id) {
    // I want to do something here if it's a user who changed the the selected item
}

...我如何知道项目选择是以编程方式完成还是通过用户操作通过UI完成?

6 个答案:

答案 0 :(得分:10)

我不知道这可以从方法中区分出来。实际上,这是一个很多人面临的问题,当启动微调器时onItemSelected被触发。目前似乎唯一的解决方法是使用外部变量。

private Boolean isUserAction = false;

...

public void onItemSelected( ... ) {

    if( isUserAction ) {
       // code for user initiated selection
    } else {
       // code for programmatic selection
       // also triggers on init (hence the default false)
    }

    // reset variable, so that it will always be true unless tampered with
    isUserAction = true;
}

public void myButtonClick( ... ) {
    isUserAction = false;
    mySpinner.setSelectedItem ( ... );
}

答案 1 :(得分:5)

我制作了一个新的Spinner类来封装上面提到的原则。但即便如此,你必须确保调用正确的方法而不是setSelection

gist

中的相同内容
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.AdapterView;

/**
 * Used this to differentiate between user selected and prorammatically selected
 * Call {@link Spinner#programmaticallySetPosition} to use this feature.
 * Created by vedant on 6/1/15.
 */
public class Spinner extends android.widget.Spinner implements AdapterView.OnItemSelectedListener {

    OnItemSelectedListener mListener;

    /**
     * used to ascertain whether the user selected an item on spinner (and not programmatically)
     */
    private boolean mUserActionOnSpinner = true;

    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {

        if (mListener != null) {

            mListener.onItemSelected(parent, view, position, id, mUserActionOnSpinner);
        }
        // reset variable, so that it will always be true unless tampered with
        mUserActionOnSpinner = true;
    }

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

    public interface OnItemSelectedListener {
        /**
         * <p>Callback method to be invoked when an item in this view has been
         * selected. This callback is invoked only when the newly selected
         * position is different from the previously selected position or if
         * there was no selected item.</p>
         *
         * Impelmenters can call getItemAtPosition(position) if they need to access the
         * data associated with the selected item.
         *
         * @param parent The AdapterView where the selection happened
         * @param view The view within the AdapterView that was clicked
         * @param position The position of the view in the adapter
         * @param id The row id of the item that is selected
         */
        void onItemSelected(AdapterView<?> parent, View view, int position, long id, boolean userSelected);

        /**
         * Callback method to be invoked when the selection disappears from this
         * view. The selection can disappear for instance when touch is activated
         * or when the adapter becomes empty.
         *
         * @param parent The AdapterView that now contains no selected item.
         */
        void onNothingSelected(AdapterView<?> parent);
    }

    public void programmaticallySetPosition(int pos, boolean animate) {
        mUserActionOnSpinner = false;
        setSelection(pos, animate);
    }

    public void setOnItemSelectedListener (OnItemSelectedListener listener) {
        mListener = listener;
    }

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

    public Spinner(Context context, int mode) {
        super(context, mode);
        super.setOnItemSelectedListener(this);
    }

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

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

    public Spinner(Context context, AttributeSet attrs, int defStyle, int mode) {
        super(context, attrs, defStyle, mode);
        super.setOnItemSelectedListener(this);
    }
}

答案 2 :(得分:5)

使用 Spinner setOnTouchListener()方法,您可以非常简单地达到预期效果:

// Instance variables
boolean spinnerTouched = false;
Spinner spinner;

// onCreate() / onCreateView() / etc. method..
spinner = ...;
spinner.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {

        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            spinnerTouched = true; // User DID touched the spinner!
        }

        return false;
    }
});
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
    @Override
    public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id) {

        if (spinnerTouched) {
            // Do something
        }
        else {
            // Do something else
        }

    }

    @Override
    public void onNothingSelected(AdapterView<?> parentView) {
    }
});

// Your method that you use the change the spinner selection programmatically...
private void changeSpinnerSelectionProgrammatically(int pos) {
    stateSpinnerTouched = false; // User DIDN'T touch the spinner
    boolean useAnimation = false;
    spinner.setSelection(pos, useAnimation); // Calls onItemSelected()
}

答案 3 :(得分:1)

我想出了一个简单而通用的解决方法。请参阅此问题的接受答案:

Undesired onItemSelected calls

因此,如果position不等于spin.getTag(R.id.pos),则您知道回调是由于用户进行了更改,因为无论何时您自己进行更改,您都将标记设置为spin.setTag(R.id.pos, pos)其中pos是您设置的值。如果您使用此方法,请务必在完成工作后在onItemSelected中设置标记!

答案 4 :(得分:1)

SeekBar不同,Spinner没有内置支持来检测更改是程序化还是用户,所以我建议,不要使用微调器进行任何类型的递归编程任务。当我尝试使用与MediaPlayerSeekBar的递归连接实现Spinner时,我的体验非常糟糕。结果充满了失望。所以你只有在喜欢不快乐和失望的情况下才能尝试。

注意:的 我通过向我的微调器选择中添加了一个Button来解决了我的问题。 不要浪费时间去解决不必要的事情。我的意思是,做一个解决方案并不是一个好习惯,而是重新实现Spinner以获得我们自己预期的行为。

如果上述陈述错误,我真的很抱歉。我之所以这样,是因为我喜欢编码和编码。

答案 5 :(得分:0)

我知道这已经很晚了,但我刚刚开始使用android并遇到了这个问题,我找到了合适的解决方法。

我使用了基于可触控模式的可聚焦方案的解决方法。

  1. 在调整模式下将微调器视图设置为可聚焦。

  2. 设置微调器的焦点更改侦听器,以便在聚焦时调用spinner.performClick()。

  3. 在微调器的onItemSelected侦听器中,将焦点返回到布局的父视图(或者您认为合适的视图)

  4. 可以通过检查微调器是否具有焦点来识别用户输入,因为程序化更改不会请求聚焦。

  5. PS:当您在onCreate中为微调器设置focusableintouchmode时,请确保在没有任何其他可聚焦视图的情况下立即将焦点返回到父视图。