1

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();
        }
    }
Nitish
  • 13,845
  • 28
  • 135
  • 263
  • Instead of `pwindo.setBackgroundDrawable(new ColorDrawable());`, try `pwindo.setBackgroundDrawable(new ColorDrawable(Color.RED));`. Is the area you considered to be outside in red now? – Vikram Mar 23 '15 at 14:48
  • @Vikram : I tried what you suggested. No red color appears. I have placed the popup creation on ActionBar activity which has ViewPager as its main content. I am using ViewPager to show different tabs on activity. All tabs are implemented as fragments. I am doing it wrong by adding popup on Main activity. I have edited my question which now shows how I have added the popup window. – Nitish Mar 24 '15 at 14:46
  • 1
    3 things: First, the call `pwindo.showAtLocation(view, Gravity.RIGHT | Gravity.TOP, 0, actionBar.getHeight());` should be made _after_ you have set up your `PopupWindow` - the call `pwindo.setBackgroundDrawable(new ColorDrawable(Color.RED));` should be made before `showAtLocation(...)`. Second, you shouldn't be passing `(ViewGroup) findViewById(R.id.flyoutLayoutList)` - pass `null`. Third, setting the background using `setBackgroundDrawable(..)` should be enough to get the behavior you want(I'll explain this later). The calls `setOutsideTouchable(..)` and `setTouchInterceptor` are not needed. – Vikram Mar 24 '15 at 23:27
  • @Vikram : Perfect solution. Many many thanks to you. I would never have guessed that. Please post this as your answer for future reference. – Nitish Mar 25 '15 at 04:35

4 Answers4

6

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(...).

Vikram
  • 51,313
  • 11
  • 93
  • 122
4

Try to use the code below.

 LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    // Inflate the view from a predefined XML layout
    View layout = inflater.inflate(R.layout.test, (ViewGroup) findViewById(R.id.linearLayout));

    pw = new PopupWindow(layout, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, true);

    pw.setOutsideTouchable(true);
    pw.setBackgroundDrawable(new ShapeDrawable());
    pw.setTouchInterceptor(new OnTouchListener() {

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            // here I want to close the pw when clicking outside it but
            // at all this is just an example of how it works and you can
            // implement the onTouch() or the onKey() you want
            if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
                pw.dismiss();
                return true;
            }
            return false;
        }

    });

    pw.showAtLocation(layout, Gravity.CENTER, 0, 0);
Sandeep Singh
  • 1,117
  • 2
  • 11
  • 29
3

You can use dialog.setCanceledOnTouchOutside(true); which will close the Dialog if you touch outside of the Dialog.

Something like:

Dialog dialog = new Dialog(context)
dialog.setCanceledOnTouchOutside(true);

For popup windows:

popupWindow.setOutsideTouchable(true); 
popupWindow.setTouchable(true); 
popupWindow.setBackgroundDrawable(new BitmapDrawable());
popupWindow.setTouchInterceptor(new OnTouchListener() { @Override 
        public boolean onTouch(View v, MotionEvent event) { 
           if(AppContext.isDebugMode()) {
                    Log.d("POPUP_WINDOW", "v: "+v.getTag() + " | event: "+event.getAction()); 
                    popupWindow.dismiss(); 
                    return true; 
           } 
        });
}
Satan Pandeya
  • 3,747
  • 4
  • 27
  • 53
Mohammad Arman
  • 7,020
  • 2
  • 36
  • 50
  • are you using any background drawable in your popup window? If yes then please not that setting background drawable to null kills the touch event. Have edited my answer. you can try it. – Mohammad Arman Mar 20 '15 at 12:20
  • setTouchInterceptor is not getting called. – Nitish Mar 23 '15 at 06:49
0

It works:

popup.setFocusable(false);
popup.setTouchable(true);
popup.setOutsideTouchable(false);
popup.setBackgroundDrawable(new ColorDrawable(android.graphics.Color.TRANSPARENT));
Satan Pandeya
  • 3,747
  • 4
  • 27
  • 53
gaurav gupta
  • 513
  • 1
  • 6
  • 13