是否可以知道Spinner
是开放还是已关闭?如果有一些onOpenListener for Spinners,那就更好了。
我尝试使用像这样的OnItemSelectedListener:
spinnerType.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
executeSomething();
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
Log.d("nothing" , "selected");
}
});
我知道如果选择了某些东西(在executeSomething()中),窗口将会关闭。但是如果我在对话框外单击,我也不会得到通知,这也会关闭微调器
答案 0 :(得分:72)
观察这些事件的另一个选择是扩展Spinner
类并使用其中一种方法(performClick()
将触发其对话框/弹出窗口),然后监视持有此窗口的窗口的焦点自定义Spinner
。这应该为您提供所有可能的完成可能性(对于对话框或下拉模式)的所需闭合事件。
自定义Spinner
类:
public class CustomSpinner extends Spinner {
/**
* An interface which a client of this Spinner could use to receive
* open/closed events for this Spinner.
*/
public interface OnSpinnerEventsListener {
/**
* Callback triggered when the spinner was opened.
*/
void onSpinnerOpened(Spinner spinner);
/**
* Callback triggered when the spinner was closed.
*/
void onSpinnerClosed(Spinner spinner);
}
private OnSpinnerEventsListener mListener;
private boolean mOpenInitiated = false;
// implement the Spinner constructors that you need
@Override
public boolean performClick() {
// register that the Spinner was opened so we have a status
// indicator for when the container holding this Spinner may lose focus
mOpenInitiated = true;
if (mListener != null) {
mListener.onSpinnerOpened(this);
}
return super.performClick();
}
@Override
public void onWindowFocusChanged (boolean hasFocus) {
if (hasBeenOpened() && hasFocus) {
performClosedEvent();
}
}
/**
* Register the listener which will listen for events.
*/
public void setSpinnerEventsListener(
OnSpinnerEventsListener onSpinnerEventsListener) {
mListener = onSpinnerEventsListener;
}
/**
* Propagate the closed Spinner event to the listener from outside if needed.
*/
public void performClosedEvent() {
mOpenInitiated = false;
if (mListener != null) {
mListener.onSpinnerClosed(this);
}
}
/**
* A boolean flag indicating that the Spinner triggered an open event.
*
* @return true for opened Spinner
*/
public boolean hasBeenOpened() {
return mOpenInitiated;
}
}
答案 1 :(得分:28)
基于@Luksprog精彩的解决方案,我只想添加一个小的更改,如果有人在片段中使用CustomSpinner,这将非常有用。
我们不使用Activity.onWindowFocusChanged
函数,而是覆盖View.onWindowFocusChanged
函数。因此整个CustomSpinner类变为
import android.content.Context;
import android.util.AttributeSet;
import android.widget.Spinner;
public class CustomSpinner extends Spinner {
private static final String TAG = "CustomSpinner";
private OnSpinnerEventsListener mListener;
private boolean mOpenInitiated = false;
public CustomSpinner(Context context, AttributeSet attrs, int defStyleAttr, int mode) {
super(context, attrs, defStyleAttr, mode);
}
public CustomSpinner(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public CustomSpinner(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomSpinner(Context context, int mode) {
super(context, mode);
}
public CustomSpinner(Context context) {
super(context);
}
public interface OnSpinnerEventsListener {
void onSpinnerOpened();
void onSpinnerClosed();
}
@Override
public boolean performClick() {
// register that the Spinner was opened so we have a status
// indicator for the activity(which may lose focus for some other
// reasons)
mOpenInitiated = true;
if (mListener != null) {
mListener.onSpinnerOpened();
}
return super.performClick();
}
public void setSpinnerEventsListener(OnSpinnerEventsListener onSpinnerEventsListener) {
mListener = onSpinnerEventsListener;
}
/**
* Propagate the closed Spinner event to the listener from outside.
*/
public void performClosedEvent() {
mOpenInitiated = false;
if (mListener != null) {
mListener.onSpinnerClosed();
}
}
/**
* A boolean flag indicating that the Spinner triggered an open event.
*
* @return true for opened Spinner
*/
public boolean hasBeenOpened() {
return mOpenInitiated;
}
@Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
android.util.Log.d(TAG, "onWindowFocusChanged");
super.onWindowFocusChanged(hasWindowFocus);
if (hasBeenOpened() && hasWindowFocus) {
android.util.Log.i(TAG, "closing popup");
performClosedEvent();
}
}
}
答案 2 :(得分:12)
嗨朋友们,我在过去的两天里一直在努力解决这个问题,最后我得到了以下解决方案,完成了我的工作。我尝试过,它完美无缺。感谢
mSpinner.setOnTouchListener(new OnTouchListener(){
@Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN){
Toast.makeText(MapActivity.this,"down",Toast.LENGTH_LONG).show();
// Load your spinner here
}
return false;
}
});
答案 3 :(得分:1)
没有内置功能,但使用OnTouchListener
和OnItemSelectedListener
非常容易。
abstract class OnOpenListener implements OnTouchListener, OnItemSelectedListener {
public OnOpenListener(Spinner spinner) {
spinner.setOnTouchListener(this);
spinner.setOnItemSelectedListener(this);
}
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
onOpen();
}
return false;
}
@Override
public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
onClose();
}
@Override
public void onNothingSelected(AdapterView<?> arg0) {
onClose();
}
abstract public void onOpen();
abstract public void onClose();
}
然后分配适当的侦听器:
OnOpenListener onOpenListener = new OnOpenListener(mySpinner) {
@Override
public void onOpen() {
// spinner was opened
}
@Override
public void onClose() {
// spinner was closed
}
};
答案 4 :(得分:1)
我认为找到它打开和关闭的最佳方式就是这样:
如果它已关闭,现在它在适配器中调用“getDropDownView”,则可以认为它已打开。
如果调用“onItemSelected”或“onNothingSelected”,现在它已关闭。
编辑:这是一个示例代码
<div class="footerbar">
<input type='button' id='somebtn' value="some button"/>
</div> //this needs to be corrected!
答案 5 :(得分:0)
我无法找到一种方法来使用微调器获得此行为,因此对我来说唯一有用的是使用微调器(自定义)适配器:
public interface SpinnerListener {
void onSpinnerExpanded();
void onSpinnerCollapsed();
}
然后可以编写一个自定义适配器,它只是抓取“spinner扩展”视图并添加一个监听器来监听“expand”和“collapse”事件。我使用的自定义适配器是:
public class ListeningArrayAdapter<T> extends ArrayAdapter<T> {
private ViewGroup itemParent;
private final Collection<SpinnerListener> spinnerListeners = new ArrayList<SpinnerListener>();
public ListeningArrayAdapter(Context context, int resource, T[] objects) {
super(context, resource, objects);
}
// Add the rest of the constructors here ...
// Just grab the spinner view (parent of the spinner item view) and add a listener to it.
@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
if (isParentTheListView(parent)) {
itemParent = parent;
addFocusListenerAsExpansionListener();
}
return super.getDropDownView(position, convertView, parent);
}
// Assumes the item view parent is a ListView (which it is when a Spinner class is used)
private boolean isParentTheListView(ViewGroup parent) {
return (parent != itemParent && parent != null && ListView.class.isAssignableFrom(parent.getClass()));
}
// Add a focus listener to listen to spinner expansion and collapse events.
private void addFocusListenerAsExpansionListener() {
final View.OnFocusChangeListener listenerWrapper = new OnFocusChangeListenerWrapper(itemParent.getOnFocusChangeListener(), spinnerListeners);
itemParent.setOnFocusChangeListener(listenerWrapper);
}
// Utility method.
public boolean isExpanded() {
return (itemParent != null && itemParent.hasFocus());
}
public void addSpinnerListener(SpinnerListener spinnerListener) {
spinnerListeners.add(spinnerListener);
}
public boolean removeSpinnerListener(SpinnerListener spinnerListener) {
return spinnerListeners.remove(spinnerListener);
}
// Listener that listens for 'expand' and 'collapse' events.
private static class OnFocusChangeListenerWrapper implements View.OnFocusChangeListener {
private final Collection<SpinnerListener> spinnerListeners;
private final View.OnFocusChangeListener originalFocusListener;
private OnFocusChangeListenerWrapper(View.OnFocusChangeListener originalFocusListener, Collection<SpinnerListener> spinnerListeners) {
this.spinnerListeners = spinnerListeners;
this.originalFocusListener = originalFocusListener;
}
@Override
public void onFocusChange(View view, boolean hasFocus) {
if (originalFocusListener != null) {
originalFocusListener.onFocusChange(view, hasFocus); // Preserve the pre-existing focus listener (if any).
}
callSpinnerListeners(hasFocus);
}
private void callSpinnerListeners(boolean hasFocus) {
for (SpinnerListener spinnerListener : spinnerListeners) {
if (spinnerListener != null) {
callSpinnerListener(hasFocus, spinnerListener);
}
}
}
private void callSpinnerListener(boolean hasFocus, SpinnerListener spinnerListener) {
if (hasFocus) {
spinnerListener.onSpinnerExpanded();
}
else {
spinnerListener.onSpinnerCollapsed();
}
}
}
}
然后当我在我的活动或片段中使用微调器时,我所要做的就是将微调器适配器设置为上面的自定义适配器:
private ListeningArrayAdapter<CharSequence> adapter;
private Spinner buildSpinner() {
final CharSequence[] items = {"One", "Two", "Three"};
final Spinner spinner = (Spinner)getActivity().getLayoutInflater().inflate(R.layout.item_spinner, null);
adapter = new ListeningArrayAdapter<CharSequence>(getActivity(), R.layout.item_spinner_item, items);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
adapter.addSpinnerListener(new TestSpinnerListener(getActivity())); // Add your own spinner listener implementation here.
spinner.setAdapter(adapter);
return spinner;
}
我知道这有点像黑客,有点脆,但它对我有用。如果Spinner类内置了所有这些功能并允许您设置expand-collapse侦听器,那会好得多。暂时我将不得不处理这个黑客攻击。
答案 6 :(得分:0)
花了一天的时间研究所有解决方案之后,这是我轻松检测出Spinner的打开和关闭以及如何在外部Spinner上聚焦以关闭Spinner的简单解决方法。
步骤1:将 addOnWindowFocusChangeListener 添加到片段或活动中的微调器。
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val spinner = spinner_view
val arrayAdapter = ArrayAdapter<RestoreManager.ConnectionType>(context!!, R.layout.layout_backup_spinner)
arrayAdapter.setDropDownViewResource(R.layout.spinner_item)
spinner?.let {
val spinnerAdapter = SpinnerAdapter(activity!!)
it.adapter = spinnerAdapter
it.setSelection(0)
it.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parentView: AdapterView<*>, selectedItemView: View?, position: Int, id: Long) {}
override fun onNothingSelected(parentView: AdapterView<*>) {}
}
it.viewTreeObserver?.addOnWindowFocusChangeListener { hasFocus -> //This updates the arrow icon up/down depending on Spinner opening/closing
spinnerAdapter .spinnerOpen = hasFocus
spinnerAdapter .notifyDataSetChanged()
}
}
}
每次微调器打开或关闭时,都会调用addOnWindowFocusChangeListener 。当微调器打开并且用户在微调器外部点击以关闭微调器时,也会触发该事件。 通过这种方法,您可以更新SpinnerAdapter的UI。
对于我的用例,我想在微调器打开和关闭时上下显示箭头图标。因此,我在微调适配器中设置了标记spinnerAdapter.spinnerOpen
。
第2步:每次您打开或关闭Spinner时,都会在 SpinnerAdapter 中调用override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {}
。这是SpinnerAdapter中的代码:
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val spinView = if (convertView == null) {
val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
inflater.inflate(R.layout.layout_backup_spinner, null)
} else {
convertView
}
var arrowIcon = spinView.findViewById<ImageView>(R.id.arrow_icon)
if (spinnerOpen) arrowIcon.setImageResource(R.drawable.arrow_down)
else arrowIcon.setImageResource(R.drawable.arrow_up)
return spinView
}