2

Please note: This issue is about Android PopupWindow, not Dialog.

I'm trying to make an Android PopupWindow non modal:

  • When clicking INSIDE of the window, I want the window's views to react to the click normally
  • When clicking OUTSIDE of the window, I want the click to be normally received (I mean the MotionEvent) by other graphical elements outside of the window as if the PopupWindow wasn't shown.

I've set the touch listener on the content view in the PopupWindow to test if the event is outside of the PopupWindow, and if so to return false. I would expect then the MotionEvent to be propagated to the view where I clicked. But it seems that since those views are outside of the current window then they don't receive the event.

How can I propagate the event to the view located where I clicked even if it's outside of the PopupWindow?

public class NonModalPopupWindow extends PopupWindow { private static final String TAG = "NonModalPopupWindow"; public NonModalPopupWindow(View contentView, int width, int height) { super(width, height); setContentView(contentView); contentView.setOnTouchListener(createOnTouchListener()); setFocusable(true); } private View.OnTouchListener createOnTouchListener() { return new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { final int x = (int) event.getX(); final int y = (int) event.getY(); final boolean isTouchOutside = (x < 0) || (x >= getWidth()) || (y < 0) || (y >= getHeight()); if (isTouchOutside || event.getAction() == MotionEvent.ACTION_OUTSIDE) { Log.e(TAG, "Touched OUTSIDE"); // How to propagate this event so that it's received by the View shown // At the location where I clicked, outside of the PopupWindow? return false; } else { Log.e(TAG, "Touched INSIDE"); return true; } } }; } 

}

3 Answers 3

1

try this:

setOutsideTouchable(true);

call this in your constructor

Sign up to request clarification or add additional context in comments.

Comments

0

Just an addition for DialogFragment peradventure you don't want to use an activity or Theme.Dialog

Chooser.xml

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/main_layout" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:layout_margin="@dimen/activity_horizontal_margin" android:background="@drawable/dialog_bg"> <RelativeLayout android:id="@+id/layout_Camera" android:layout_width="0dp" android:layout_weight = "0.25" android:layout_height="wrap_content"> <ImageView android:id="@+id/camera" android:layout_width="32dp" android:layout_height="32dp" android:layout_centerHorizontal="true" android:src="@drawable/ic_menu_camera_holo_dark" /> <TextView android:id="@+id/tvCamera" android:layout_margin="@dimen/margin_very_small" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:textAlignment="center" android:layout_below="@id/camera" android:text="Camera"/> </RelativeLayout> <RelativeLayout android:id="@+id/layout_Gallery" android:layout_width="0dp" android:layout_weight ="0.25" android:layout_height="wrap_content"> <ImageView android:id="@+id/gallery" android:layout_width="32dp" android:layout_height="32dp" android:layout_centerHorizontal="true" android:src="@drawable/ic_menu_photo_gallery" /> <TextView android:id="@+id/tvGallery" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:textAlignment="center" android:layout_below="@id/gallery" android:text="Gallery"/> </RelativeLayout> <RelativeLayout android:id="@+id/layout_RemovePhoto" android:layout_width="0dp" android:layout_weight="0.25" android:layout_height="wrap_content"> <ImageView android:id="@+id/removePhoto" android:layout_width="32dp" android:layout_height="32dp" android:layout_centerHorizontal="true" android:src="@drawable/ic_menu_remove"/> <TextView android:id="@+id/tvRemovePhoto" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:textAlignment="center" android:layout_below="@id/removePhoto" android:text="Remove Photo"/> </RelativeLayout> </LinearLayout> 

Transparent background in the drawable folder

dialog_bg.xml

<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android"> <solid android:color="@color/off_white"/> <corners android:radius="15dp" /> <padding android:left="20dp" android:top="20dp" android:right="20dp" android:bottom="20dp" /> <margin android:left="50dp" android:bottom="50dp" android:right="50dp" android:top="50dp" /> </shape> 

PhotoChooserFragment

public class photoChooserFragment extends DialogFragment { onItemSelectedListener listener; ImageView imgCamera, imgGallery, imgRemovePhoto; TextView tvCamera, tvGallery, tvRemovePhoto; public interface onItemSelectedListener { void onItemSelected(int position); } public void onItemSelected(int position) { listener.onItemSelected(position); } @Override public void onAttach(Activity activity) { super.onAttach(activity); // Activities containing this fragment must implement its listener. if (!(activity instanceof onItemSelectedListener)) { throw new IllegalStateException( "Activity must implement fragment's callbacks."); } listener = (onItemSelectedListener) activity; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.chooser, null, false); imgCamera = view.findViewById(R.id.camera); imgGallery = view.findViewById(R.id.gallery); imgRemovePhoto = view.findViewById(R.id.removePhoto); tvCamera = view.findViewById(R.id.tvCamera); tvGallery = view.findViewById(R.id.tvGallery); tvRemovePhoto = view.findViewById(R.id.tvRemovePhoto); Window window = getDialog().getWindow(); window.requestFeature(Window.FEATURE_NO_TITLE); window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); WindowManager.LayoutParams windowLP = window.getAttributes(); //window.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, // WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL); window.addFlags(WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH); //window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND); window.setGravity(Gravity.BOTTOM); windowLP.gravity = Gravity.BOTTOM; window.setAttributes(windowLP); getDialog().setCanceledOnTouchOutside(true); return view; } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); imgCamera.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dismiss(); onItemSelected(1); } }); tvCamera.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dismiss(); onItemSelected(1); } }); imgGallery.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dismiss(); onItemSelected(2); } }); tvGallery.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dismiss(); onItemSelected(2); } }); imgRemovePhoto.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dismiss(); onItemSelected(3); } }); tvRemovePhoto.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dismiss(); onItemSelected(3); } }); } @Override public void onDetach() { super.onDetach(); // Reset the active listener interface to null. listener = null; } } 

Then in your activity add the interface

implements photoChooserFragment.onItemSelectedListener 

and ensure it is implemented

 @Override public void onItemSelected(int position){ switch (position){ case 1: //camera GetImageFromCamera(); break; case 2: //gallery ChooseImageFromGallery(); break; } } 

Now you can call the fragment, a button will do, in mine I used a floatingactionbutton

faBtnCamera.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //Show menu photoChooserFragment dialog = new photoChooserFragment(); dialog.show(getSupportFragmentManager(), "chooser"); } }); 

Comments

0

For Android versions >=29, you can tell the PopupWindow to transmit touches to the view behind it using

myPopup.setTouchModal(false);

The touch goes thru, and the popup stays up.

I don't know if that lets you ALSO touch INSIDE the popup, because my popup has no controls. But it's still a cleaner solution than playing with lots of Listeners...

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.