Avoiding PopupWindow dismissal after touching outside

后端 未结 8 1950
灰色年华
灰色年华 2020-12-31 05:07

I would like to use a PopupWindow with following behaviours/features:

  • It is focusable (has interactive controls inside eg. buttons)
  • The View \'under\'
相关标签:
8条回答
  • 2020-12-31 05:45

    Try

    pw.setBackgroundDrawable(null);
    
    0 讨论(0)
  • 2020-12-31 05:46

    Solution is:

    popupWindow.setFocusable(true);

    popupWindow.update();

    Thanks to: http://android-er.blogspot.ch/2012/04/disable-outside-popupwindow-by.html

    0 讨论(0)
  • 2020-12-31 05:57

    Nothing suggested here, or elsewhere seemed to work for me. So I did this:

    popupWindow.setTouchInterceptor(new View.OnTouchListener() {
                @Override
                public boolean onTouch(View view, MotionEvent motionEvent) {
                    if (motionEvent.getX() < 0 || motionEvent.getX() > viewWidth) return true;
                    if (motionEvent.getY() < 0 || motionEvent.getY() > viewHight) return true;
    
                    return false;
                }
            });
    

    If the touch is within the bounds of the popupWIndow, the touch event not consumed (so buttons or scrollviews will work). If the touch is outside the bounds, the touch is consumed and not passed to the popupWindow so it is NOT dismissed.

    0 讨论(0)
  • 2020-12-31 06:00

    First of all, you must figure out that why the popupWindow is dismissed when you touch outside.

    After read the source code of PopupWindow and the resource file styles.xml,

    <style name="Widget.PopupWindow">
        <item name="popupBackground">@drawable/editbox_dropdown_background_dark</item>
        <item name="popupAnimationStyle">@style/Animation.PopupWindow</item>
    </style>
     <style name="Widget">
        <item name="textAppearance">?textAppearance</item>
    </style>
    

    So there is nothing like the dialog theme:

    <style name="Theme.Dialog">
    <item name="windowCloseOnTouchOutside">@bool/config_closeDialogWhenTouchOutside</item>
    </style name="Theme.Dialog">
    

    But Somethings happen when PopupWindow.setBackgroundDrawable() is called,

    private void preparePopup(WindowManager.LayoutParams p) {
        if (mContentView == null || mContext == null || mWindowManager == null) {
            throw new IllegalStateException("You must specify a valid content view by "
                    + "calling setContentView() before attempting to show the popup.");
        }
    
        if (mBackground != null) {
            final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
            int height = ViewGroup.LayoutParams.MATCH_PARENT;
            if (layoutParams != null &&
                    layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
                height = ViewGroup.LayoutParams.WRAP_CONTENT;
            }
    
            // when a background is available, we embed the content view
            // within another view that owns the background drawable
            PopupViewContainer popupViewContainer = new PopupViewContainer(mContext);
            PopupViewContainer.LayoutParams listParams = new PopupViewContainer.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT, height
            );
            popupViewContainer.setBackgroundDrawable(mBackground);
            popupViewContainer.addView(mContentView, listParams);
    
            mPopupView = popupViewContainer;
        } else {
            mPopupView = mContentView;
        }
        mPopupViewInitialLayoutDirectionInherited =
                (mPopupView.getRawLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT);
        mPopupWidth = p.width;
        mPopupHeight = p.height;
    }
    

    A container view "PopupViewContainer" is created.

     private class PopupViewContainer extends FrameLayout {
        private static final String TAG = "PopupWindow.PopupViewContainer";
    
        public PopupViewContainer(Context context) {
            super(context);
        }
    
    
        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            if (mTouchInterceptor != null && mTouchInterceptor.onTouch(this, ev)) {
                return true;
            }
            return super.dispatchTouchEvent(ev);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            final int x = (int) event.getX();
            final int y = (int) event.getY();
    
            if ((event.getAction() == MotionEvent.ACTION_DOWN)
                    && ((x < 0) || (x >= getWidth()) || (y < 0) || (y >= getHeight()))) {
                dismiss();
                return true;
            } else if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
                dismiss();
                return true;
            } else {
                return super.onTouchEvent(event);
            }
        }
    

    }

    Now you know the reason which matters. So there are two ways in which you can aviod PopupWindow dismissal after touching outside. 1.just as @Raaga did. remove pw.setBackgroundDrawable(new BitmapDrawable());

    1. you can implement a OnTouchListener to filter the touche events outside the PopupWindow.
    0 讨论(0)
  • 2020-12-31 06:02

    Just remove pw.setBackgroundDrawable(new BitmapDrawable());

    0 讨论(0)
  • 2020-12-31 06:03

    it is possible to setFocusable(false) for PopupWindow

    buttons still be clickable, but without visual clicking behaviour (some custom handler to force show click?)

    below is sample for floating window with "always on top" option

    original layout near floating window is fully operational in both cases, moreover, it is possible to use dialogs and other popups when window is still floating

    also the window is reusable

    final static int buttonAlpha = 0xDF;
    final static float buttonTextSize = 12f;
    
    public final void addPopupButton(LinearLayout linearLayout, String title, android.view.View.OnClickListener onClickListener)
    {
        Button button = new Button(this.getContext());
        button.setText(title);
        button.setTextSize(buttonTextSize);
        button.getBackground().setAlpha(buttonAlpha);
        button.setOnClickListener(onClickListener);
        linearLayout.addView(button, LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
    }
    
    public final Button addPopupCheckbox(LinearLayout linearLayout, String title, boolean isChecked, android.view.View.OnClickListener onClickListener)
    {
        final Button button = new Button(getContext());
        button.setText(title);
        button.setTextSize(buttonTextSize);
        final int buttonHeight = button.getHeight();
        setButtonChecked(button, isChecked);
        button.setHeight(buttonHeight);
        button.getBackground().setAlpha(buttonAlpha);
        button.setOnClickListener(onClickListener);
        linearLayout.addView(button, LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
        return button;
    }
    
    public final void setButtonChecked(Button button, boolean isChecked)
    {
        button.setCompoundDrawablesWithIntrinsicBounds(Resources.getSystem().getIdentifier(isChecked ? "android:drawable/btn_check_on" : "android:drawable/btn_check_off", null, null), 0, 0, 0);
    }
    
    private boolean isMenuAlwaysOnTop = true;
    private PopupWindow popupWindowMenuV2 = null;
    
    public final void popupMenuNav2()
    {
        if (popupWindowMenuV2 == null)
        {
            // [start] layout
    
            ScrollView scrollView = new ScrollView(this.getContext());
    
            final LinearLayout linearLayoutNavigation = new LinearLayout(this.getContext());
            linearLayoutNavigation.setOrientation(LinearLayout.VERTICAL);
            linearLayoutNavigation.setBackgroundColor(0x7FFFFFFF);
            linearLayoutNavigation.setPadding(20, 10, 20, 10);
    
            scrollView.addView(linearLayoutNavigation, LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
    
            popupWindowMenuV2 = new PopupWindow(this);
            popupWindowMenuV2.setBackgroundDrawable(new BitmapDrawable());
            popupWindowMenuV2.setWidth(WindowManager.LayoutParams.WRAP_CONTENT);
            popupWindowMenuV2.setHeight(WindowManager.LayoutParams.WRAP_CONTENT);
            popupWindowMenuV2.setTouchable(true);
            popupWindowMenuV2.setOutsideTouchable(!isMenuAlwaysOnTop);
            popupWindowMenuV2.setFocusable(!isMenuAlwaysOnTop);
            popupWindowMenuV2.setTouchInterceptor(new OnTouchListener()
            {
                @Override
                public boolean onTouch(View v, MotionEvent event)
                {
                    if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_OUTSIDE)
                    {
                        if (!isMenuAlwaysOnTop)
                            popupWindowMenuV2.dismiss();
                        else
                            return false;
                        return true;
                    }
                    return false;
                }
            });
            popupWindowMenuV2.setContentView(scrollView);
    
            // [end] layout
    
            // [start] always on top checkbox
    
            final Button buttonMenuAlwaysOnTop = addPopupCheckbox(linearLayoutNavigation, "always on top", isMenuAlwaysOnTop, null);
            buttonMenuAlwaysOnTop.setOnClickListener(
                    new OnClickListener()
                    {
                        @Override
                        public void onClick(View vv)
                        {
                            isMenuAlwaysOnTop = !isMenuAlwaysOnTop;
                            setButtonChecked(buttonMenuAlwaysOnTop, isMenuAlwaysOnTop);
                            popupWindowMenuV2.dismiss();
                            popupWindowMenuV2.setOutsideTouchable(!isMenuAlwaysOnTop);
                            popupWindowMenuV2.setFocusable(!isMenuAlwaysOnTop);
                            popupWindowMenuV2.showAtLocation(((Activity) getContext()).getWindow().getDecorView(), Gravity.CENTER_VERTICAL + Gravity.RIGHT, 0, 0);
                        }
                    });
    
            // [end] always on top checkbox
    
            addPopupButton(linearLayoutNavigation, "some button",
                    new OnClickListener()
                    {
                        @Override
                        public void onClick(View vv)
                        {
                            if (!isMenuAlwaysOnTop)
                                popupWindowMenuV2.dismiss();
                            someAction();
                        }
                    });
    
        }
    
        popupWindowMenuV2.showAtLocation(((Activity) getContext()).getWindow().getDecorView(), Gravity.CENTER_VERTICAL + Gravity.RIGHT, 0, 0);
    }
    
    // somewhere in handler:
                if (someCondition)
                {
                    if (popupWindowMenuV2 != null && popupWindowMenuV2.isShowing())
                        popupWindowMenuV2.dismiss();
                    else
                        popupMenuNav2();
                    return true;
                }
    
    0 讨论(0)
提交回复
热议问题