How to open a spinner with one click when another spinner is open

回眸只為那壹抹淺笑 提交于 2021-02-07 11:26:41

问题


I have few spinners on a form. When I click on any of them it opens and shows the options. When I click on any another spinner it closes the open spinner but then I need to click again on the desired spinner in order to open it.

I would like to catch the first click on the 2nd spinner so that I can close the first open spinner and then open the clicked spinner.

I saw posts about closing the spinner on any click outside, but that is not enough because I still do not know that the desired spinner was clicked (the onclick() of the 2nd spinner is not fired on).


回答1:


I'm a bit busy to write and give you some code, but getting help from links like this you can get some snippet. so here's my idea. At first when you create your view, fetch the coordinate of spinners, for example spinner A, B and C. Also write a function (for example foo(...)) which returns the coordinate of clicked point but doesn't consume it. Then register A,B and C onItemSelectedListeners and in their onNothingSelected check if clicked point is in other two areas of spinners or not(using foo(...) you wrote earlier). This way you can get what you want. Sorry I'm both busy and lazy, but this works. I hope I've helped you.

Update(for receiving touches):

Okay, I tried several approaches, to be honest none of them worked, even making activity intercepting touch events didn't help, so I guess the case with opened spinner is like pop ups, in that case one solution comes to my mind.

You need to put a transparent layer on top of all your views to intercept the touch, but it should be like what is used in lock screen so that no other dialog or view can be on top of that. If you have written lock screen app or tried to do it you know how to do it.

Yet for such a case permission will be overkill and I don't suggest you to do it unless you find another peaceful way.




回答2:


Here is a Custom spinner which does exactly what you need,

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();
    }
}

I have used Java Reflection to access the popup and overridden the TouchInteceptor. By this way, you will be able to control the touch event on your activity and in turn you will be able to open the next spinner without having to click twice.

To get a clear idea, clone my sample from Github and run the same. Please let me know if you have any doubts.




回答3:


Write this line of code wherever you are implementing setOnItemClickListener for your spinner :

spinner.requestFocusFromTouch();

This will tell the spinner to invoke with one click and display the list of items.




回答4:


Try this one line code to open your spinner without click on it

 ((Spinner) findViewById(R.id.selected_area_spinner)).performClick();

But use this code after initializing your spinner




回答5:


Try this code its help you

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();
        }
    }
}



回答6:


anotherSpinner.setSelection(open_item_index);




回答7:


This is almost impossible.

Spinner Class hierarchy

Spinner -> AbsSpinner -> AdapterView<SpinnerAdapter> -> ViewGroup -> View

From 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
 */

We have no access to the click event.

OnClickListener uses public interface DialogInterface.

Best I can suggest is use a popup menu instead of a Spinner if this "feature" bugs you.

@Zvi @Erfan Mowlaei does not seem to post any code. Here's my idea of his idea:

        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);             
                        }
                    });
                }
            }
        });

Note: As I said none of these methods can work, because the events are generated and consumed in the runtime library. They are NEVER exposed.

Other things that do not work:

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();
        }
       }
    });

A new hope Episode IV ?

Your problem does raise an entry in logcat every time your perform the "Zvi maneuver":

08-14 01:19:55.575: W/InputEventReceiver(8676): 
Attempted to finish an input event but
the input event receiver has already been disposed.

I don't hold much hope for this solving the problem.



来源:https://stackoverflow.com/questions/38673832/how-to-open-a-spinner-with-one-click-when-another-spinner-is-open

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!