繁体   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