[英]How to open a spinner with one click when another spinner is open
我的表格上有几个spinners
。 当我单击其中任何一个时,它会打开并显示选项。 当我单击任何其他spinner
它会关闭打开的spinner
但随后我需要再次单击所需的spinner
才能打开它。
我想捕捉第二个微调器的第一次点击,以便我可以关闭第一个打开的微调器,然后打开点击的微调器。
我看到了有关在外部单击时关闭微调器的帖子,但这还不够,因为我仍然不知道是否单击了所需的微调器(第二个微调器的onclick()
没有被触发)。
我有点忙不过来写,并给你一些代码,但像链接获取帮助这个你可以得到一些片段。 所以这是我的想法。 首先,当您创建视图时,获取微调器的坐标,例如微调器 A、B 和 C。还要编写一个函数(例如foo(...)
),它返回单击点的坐标但不消耗它. 然后注册 A、B 和 C onItemSelectedListeners
并在它们的onNothingSelected
检查单击的点是否在微调器的其他两个区域中(使用您之前编写的foo(...)
)。 这样你就可以得到你想要的东西。 对不起,我既忙又懒,但这有效。 我希望我已经帮助了你。
更新(用于接收触摸):
好吧,我尝试了几种方法,老实说它们都不起作用,即使使活动拦截触摸事件也无济于事,所以我猜打开微调器的情况就像弹出窗口,在这种情况下,我想到了一个解决方案。
您需要在所有视图的顶部放置一个透明层以拦截触摸,但它应该像锁定屏幕中使用的那样,以便没有其他对话框或视图可以在其之上。 如果您编写过锁屏应用程序或尝试过这样做,您就知道该怎么做。
然而,对于这种情况,许可将是矫枉过正,除非您找到另一种和平的方式,否则我不建议您这样做。
这是一个自定义微调器,它完全符合您的需要,
PopupActionSpinner.java
package com.selva.widget;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ListPopupWindow;
import android.widget.PopupWindow;
import android.widget.Spinner;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class PopupActionSpinner extends Spinner {
private static final String M_POPUP = "mPopup";
private static final String DROPDOWN_POPUP = "DropdownPopup";
private static final String IS_SHOWING = "isShowing";
private static final String DIALOG_POPUP = "DialogPopup";
private static final int MODE_UNKNOWN = -1;
private PopupTouchInterceptor popupTouchInterceptor;
private Field mFieldSpinnerPopup = null;
private PopupActionSpinner[] mPopupActionSpinnersArr = null;
/**
* Construct a new spinner with the given context's theme and the supplied attribute set.
*
* @param context The Context the view is running in, through which it can
* access the current theme, resources, etc.
* @param attrs The attributes of the XML tag that is inflating the view.
*/
public PopupActionSpinner(Context context, AttributeSet attrs) {
super(context, attrs);
}
/**
* Define your own Interface to control what happens when the Spinner dropdown popup is open.
* @param mpopupTouchInterceptor
*/
public void setPopupTouchInterceptor(PopupTouchInterceptor mpopupTouchInterceptor,
PopupActionSpinner[] allSpinners )
{
this.popupTouchInterceptor = mpopupTouchInterceptor;
this.mPopupActionSpinnersArr = allSpinners;
}
@Override
public boolean performClick() {
boolean handled = true;
try {
handled = super.performClick();
// reflecting Spinner.mPopup field
if (isFieldSpinnerPopupNull()) {
mFieldSpinnerPopup = this.getClass().getSuperclass().getDeclaredField(M_POPUP);
}
// disable access checks to Spinner.mPopup
mFieldSpinnerPopup.setAccessible(true);
// get value of mPopup field
Object spinnerPopup = mFieldSpinnerPopup.get(this);
// reflecting SpinnerPopup.isShowing()
Method isShowing = mFieldSpinnerPopup.getType()
.getDeclaredMethod(IS_SHOWING, (Class[]) null);
// calling Spinner.mPopup.isShowing()
boolean isShowingResult = (boolean) isShowing.invoke(spinnerPopup, (Object[]) null);
if (isShowingResult) {
// check if mFieldSpinnerPopup is a dialog popup
if (getSpinnerMode() == MODE_DIALOG) {
//Do Nothing
} else if (getSpinnerMode() == MODE_DROPDOWN) {
// reflecting Spinner.mPopup.mPopup
Field fieldPopupWindow = ListPopupWindow.class.getDeclaredField(M_POPUP);
fieldPopupWindow.setAccessible(true);
((PopupWindow) fieldPopupWindow.get(spinnerPopup)).setTouchInterceptor(new OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
if(!isViewInBounds(view,event.getRawX(),event.getRawY())) {
for (View spinnerView : mPopupActionSpinnersArr)
if (isPointInsideView(event.getRawX(), event.getRawY(), spinnerView)) {
popupTouchInterceptor.onTouchIntercepted(spinnerView);
break;
}
}
break;
}
}
return false;
}
});
fieldPopupWindow.setAccessible(false);
}
}
// enable access checks to Spinner.mPopup
mFieldSpinnerPopup.setAccessible(false);
} catch (Exception exception) {
}
return handled;
}
public static boolean isPointInsideView(float x, float y, View view){
int location[] = new int[2];
view.getLocationOnScreen(location);
int viewX = location[0];
int viewY = location[1];
//point is inside view bounds
if(( x > viewX && x < (viewX + view.getWidth())) &&
( y > viewY && y < (viewY + view.getHeight()))){
return true;
} else {
return false;
}
}
private boolean isViewInBounds(View view, float x, float y) {
Rect outRect = new Rect();
int[] location = new int[2];
view.getDrawingRect(outRect);
view.getLocationOnScreen(location);
outRect.offset(location[0], location[1]);
return outRect.contains((int)x, (int)y);
}
/**
* Returns a constant describing how the user selects choices from the spinner.
*
* @return the choosing mode of this <code>{@link Spinner}</code>
*/
public int getSpinnerMode() {
int result = MODE_UNKNOWN;
try {
// reflecting Spinner.mPopup field
if (isFieldSpinnerPopupNull()) {
mFieldSpinnerPopup = this.getClass().getSuperclass().getDeclaredField(M_POPUP);
}
// get Spinner.DropdownPopup class name
mFieldSpinnerPopup.setAccessible(true);
String spinnerPopupClassName = mFieldSpinnerPopup.get(this).getClass().getSimpleName();
mFieldSpinnerPopup.setAccessible(false);
switch (spinnerPopupClassName) {
case DIALOG_POPUP:
result = MODE_DIALOG;
break;
case DROPDOWN_POPUP:
result = MODE_DROPDOWN;
break;
default:
}
} catch (Exception exception) {
exception.printStackTrace();
}
return result;
}
public boolean isFieldSpinnerPopupNull() {
return mFieldSpinnerPopup == null;
}
@Override
public int getId() {
return super.getId();
}
}
我已经使用 Java 反射来访问弹出窗口并覆盖了 TouchInteceptor。 通过这种方式,您将能够控制您的 Activity 上的触摸事件,反过来您将能够打开下一个微调器而无需单击两次。
为了得到一个清晰的想法,从 Github 克隆我的示例并运行相同的。 如果您有任何疑问,请告诉我。
在您为微调器实现 setOnItemClickListener 的任何地方编写这行代码:
spinner.requestFocusFromTouch();
这将告诉微调器一键调用并显示项目列表。
试试这个单行代码来打开你的微调器而不用点击它
((Spinner) findViewById(R.id.selected_area_spinner)).performClick();
但是在初始化微调器后使用此代码
试试这个代码它可以帮助你
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
/**
* Created by riyazudinp on 8/11/2016.
*/
public class MyActivity extends AppCompatActivity implements AdapterView.OnItemSelectedListener, View.OnClickListener {
private String dataForSpinnerOne[] = {"A", "B", "C", "D", "E"};
private String dataForSpinnerTwo[] = {"F", "G", "H", "I", "J"};
private String dataForSpinnerThree[] = {"K", "L", "M", "N", "O"};
private Spinner spinnerOne;
private Spinner spinnerTwo;
private Spinner spinnerThree;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
spinnerOne = (Spinner) findViewById(R.id.spinner1);
spinnerTwo = (Spinner) findViewById(R.id.spinner2);
spinnerThree = (Spinner) findViewById(R.id.spinner3);
ArrayAdapter<String> adapter1 = new ArrayAdapter<>(MyActivity.this, android.R.layout.simple_list_item_1, dataForSpinnerOne);
ArrayAdapter<String> adapter2 = new ArrayAdapter<>(MyActivity.this, android.R.layout.simple_list_item_1, dataForSpinnerTwo);
ArrayAdapter<String> adapter3 = new ArrayAdapter<>(MyActivity.this, android.R.layout.simple_list_item_1, dataForSpinnerThree);
spinnerOne.setAdapter(adapter1);
spinnerTwo.setAdapter(adapter2);
spinnerThree.setAdapter(adapter3);
spinnerOne.setOnItemSelectedListener(this);
spinnerTwo.setOnItemSelectedListener(this);
spinnerThree.setOnItemSelectedListener(this);
spinnerOne.setOnClickListener(this);
spinnerTwo.setOnClickListener(this);
spinnerThree.setOnClickListener(this);
}
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
if (view == spinnerOne) {
//Spinner One Clicked
//add your code when spinner iten selected
}
if (view == spinnerTwo) {
//Spinner Two Clicked
//add your code when spinner iten selected
}
if (view == spinnerThree) {
//Spinner Three Clicked
//add your code when spinner iten selected
}
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
@Override
public void onClick(View view) {
if (view == spinnerOne) {
spinnerOne.performClick();
spinnerTwo.performClick();
spinnerThree.performClick();
}
if (view == spinnerTwo) {
spinnerOne.performClick();
spinnerTwo.performClick();
spinnerThree.performClick();
}
if (view == spinnerThree) {
spinnerOne.performClick();
spinnerTwo.performClick();
spinnerThree.performClick();
}
}
}
anotherSpinner.setSelection(open_item_index);
这几乎是不可能的。
Spinner -> AbsSpinner -> AdapterView<SpinnerAdapter> -> ViewGroup -> View
来自 Spinner.Java
/**
* <p>A spinner does not support item click events. Calling this method
* will raise an exception.</p>
* <p>Instead use {@link AdapterView#setOnItemSelectedListener}.
*
* @param l this listener will be ignored
*/
我们无权访问 click 事件。
OnClickListener uses public interface DialogInterface.
如果此“功能”让您感到困扰,我建议最好使用弹出菜单而不是 Spinner。
@Zvi @Erfan Mowlaei 似乎没有发布任何代码。 这是我对他的想法的看法:
ViewTreeObserver vto1 = spinnerOne.getViewTreeObserver();
vto1.addOnGlobalLayoutListener(new OnGlobalLayoutListener()
{
@Override
public void onGlobalLayout()
{
if( !spinnerOne.getViewTreeObserver().isAlive() )
{
Log.e(TAG,"OnGlobalLayoutListener: ***NOT alive***");
// only need to calculate once, so remove listener
// viewToMeasure.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}else
{
Log.e(TAG,"OnGlobalLayoutListener: ***alive***");
/*when you post a message to a View,
the messages will only be delivered after
the View has been fully initialized (including being measured)*/
spinnerOne .post(new Runnable()
{
@Override
public void run()
{
Log.e(TAG,"OnGlobalLayoutListener: ***run test***");
// safe to get height and width here
spinnerTwo.setSelected(false);
spinnerThree.setSelected(false);
spinnerOne.performClick();
// LayerDrawable ld = (LayerDrawable)spinnerOne.getBackground();//cast problem
// ld.setLayerInset(1, 0, spinnerOne.getHeight() / 2, 0, 0);
}
});
}
}
});
注意:正如我所说,这些方法都不能工作,因为事件是在运行时库中生成和使用的。 他们永远不会暴露。
不工作的其他事项:
spinnerOne.setOnFocusChangeListener(new OnFocusChangeListener()
{
@Override
public void onFocusChange(View v, boolean hasFocus)
{
if(hasFocus)
{
spinnerOne.performClick();
Log.v(TAG, "onFocusChange spinnerOne GOT the focus");
Toast.makeText(getApplicationContext(), "spinnerOne got the focus", Toast.LENGTH_SHORT).show();
}else
{
Log.v(TAG, "onFocusChange spinnerOne LOST the focus");
Toast.makeText(getApplicationContext(), "spinnerOne lost the focus", Toast.LENGTH_SHORT).show();
}
}
});
每次执行“Zvi 操作”时,您的问题都会在 logcat 中引发一个条目:
08-14 01:19:55.575: W/InputEventReceiver(8676):
Attempted to finish an input event but
the input event receiver has already been disposed.
我对解决这个问题不抱太大希望。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.