簡體   English   中英

當另一個微調器打開時,如何一鍵打開微調器

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

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 類層次結構

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM