Search code examples
androidandroid-popupwindow

Popup window dismiss on outside touch not working


Please check what I have tried so far before down voting for duplicate.
I am trying to dismiss the popup window on outside touch. I have tried every solution available but it is not working.

1st try :

pwindo.setBackgroundDrawable(new BitmapDrawable());
            pwindo.setFocusable(true);
            pwindo.setOutsideTouchable(true);

2nd try :

pwindo.setBackgroundDrawable(new ColorDrawable());
            pwindo.setFocusable(true);
            pwindo.setOutsideTouchable(true);

3rd try :

pwindo.setBackgroundDrawable(new ColorDrawable());
            pwindo.setFocusable(true);
            pwindo.setOutsideTouchable(false);

4th try :

        pwindo.setFocusable(true);
        pwindo.setOutsideTouchable(false);  

5th try :

        pwindo.setOutsideTouchable(true);  

6th try :

        pwindo.setOutsideTouchable(false);  

7th try :

        pwindo.setFocusable(true);  

Nothing is working.

Updated:

public void addFlyout()
{
    try {
        LayoutInflater inflater = TabActivity.this.getLayoutInflater();
        Display display = getWindowManager().getDefaultDisplay();
        Method mGetRawH = Display.class.getMethod("getRawHeight");
        Method mGetRawW = Display.class.getMethod("getRawWidth");
        int rawHeight = (Integer) mGetRawH.invoke(display);
        View view = inflater.inflate(R.layout.flyout_list_layout,
                (ViewGroup) findViewById(R.id.flyoutLayoutList));
        Dialog dialog = new Dialog(TabActivity.this);
        pwindo = new PopupWindow(view, 340, rawHeight- actionBar.getHeight(), true);
        pwindo.showAtLocation(view, Gravity.RIGHT | Gravity.TOP, 0, actionBar.getHeight());
        pwindo.setBackgroundDrawable(new ColorDrawable(Color.RED));
       // pwindo.setTouchable(true);
        pwindo.setOutsideTouchable(true);
        pwindo.setTouchInterceptor(new View.OnTouchListener() {
            @Override public boolean onTouch(View v, MotionEvent event)
            {

                if (event.getAction() == MotionEvent.ACTION_OUTSIDE)
                {
                    pwindo.dismiss();
                    //Log.e(TAG, "some event happened outside window: action outside");
                    return true;
                }
               // Log.e(TAG, "some event happened outside window");
                return false;
            }
        });

        listFlyout = (ListView) view.findViewById(R.id.list_slideList);
        ArrayAdapter<String> adapter = new ArrayAdapter<String>(TabActivity.this, R.layout.drawer_item, R.id.content, tabs);
        listFlyout.setAdapter(adapter);
    }
    catch (Exception ex)
    {
        ex.printStackTrace();
    }
} 

And

@Override
    public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
        // When the given tab is selected, switch to the corresponding page in
        // the ViewPager.
        try {
            if(tab.getPosition() == 4)
            {
                addFlyout();
            }
            else
            mViewPager.setCurrentItem(tab.getPosition());
        }
        catch (Exception ex)
        {
            ex.printStackTrace();
        }
    }

Solution

  • Setting a background on the PopupWindow ensures that it will be dismissible by a click outside its bounds.

    showAtLocation(...):

    public void showAtLocation(IBinder token, int gravity, int x, int y) {
        ....
    
        preparePopup(p);
    
        ....
    }
    

    preparePopup(...) checks if a background is set. If it is, the content view that you pass (in the constructor) is added to a custom FrameLayout. The background is then set on this custom FrameLayout:

    if (mBackground != null) {
        ....
    
        // 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.setBackground(mBackground);
        popupViewContainer.addView(mContentView, listParams);
    
        ....
    } else {
        ....
    }
    

    Since you are calling setBackgroundDrawable(...) after showAtLocation(...), preparePopup(...) does not place the content view inside a PopupViewContainer.

    PopupViewContainer also overrides onTouchEvent(...) to provide the 'dismissed-when-touched-outside' behavior:

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

    Clearly, all you require is to provide a background, and provide it before calling showAtLocation(...).