[英]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.