How to dismiss the dialog with click on outside of the dialog? - android

I have implemented a custom dialog for my application. I want to implement that when the user clicks outside the dialog, the dialog will be dismissed.
What do I have to do for this?

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);
Or if your Dialog in non-model then,
1 - Set the flag-FLAG_NOT_TOUCH_MODAL for your dialog's window attribute
Window window = this.getWindow();
window.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
2 - Add another flag to windows properties,, FLAG_WATCH_OUTSIDE_TOUCH - this one is for dialog to receive touch event outside its visible region.
3 - Override onTouchEvent() of dialog and check for action type. if the action type is
'MotionEvent.ACTION_OUTSIDE' means, user is interacting outside the dialog region. So in this case, you can dimiss your dialog or decide what you wanted to perform.
view plainprint?
public boolean onTouchEvent(MotionEvent event)
{
if(event.getAction() == MotionEvent.ACTION_OUTSIDE){
System.out.println("TOuch outside the dialog ******************** ");
this.dismiss();
}
return false;
}
For more info look at How to dismiss a custom dialog based on touch points? and
How to dismiss your non-modal dialog, when touched outside dialog region

Simply use
dialog.setCanceledOnTouchOutside(true);

You can use this implementation of onTouchEvent. It prevent from reacting underneath activity to the touch event (as mentioned howettl).
#Override
public boolean onTouchEvent ( MotionEvent event ) {
// I only care if the event is an UP action
if ( event.getAction () == MotionEvent.ACTION_UP ) {
// create a rect for storing the window rect
Rect r = new Rect ( 0, 0, 0, 0 );
// retrieve the windows rect
this.getWindow ().getDecorView ().getHitRect ( r );
// check if the event position is inside the window rect
boolean intersects = r.contains ( (int) event.getX (), (int) event.getY () );
// if the event is not inside then we can close the activity
if ( !intersects ) {
// close the activity
this.finish ();
// notify that we consumed this event
return true;
}
}
// let the system handle the event
return super.onTouchEvent ( event );
}
Source: http://blog.twimager.com/2010/08/closing-activity-by-touching-outside.html

Or, if you're customizing the dialog using a theme defined in your style xml, put this line in your theme:
<item name="android:windowCloseOnTouchOutside">true</item>

This method should completely avoid activities below the grey area retrieving click events.
Remove this line if you have it:
window.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
Put this on your activity created
getWindow().setFlags(LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH, LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
then override the touch event with this
#Override
public boolean onTouchEvent(MotionEvent ev)
{
if(MotionEvent.ACTION_DOWN == ev.getAction())
{
Rect dialogBounds = new Rect();
getWindow().getDecorView().getHitRect(dialogBounds);
if (!dialogBounds.contains((int) ev.getX(), (int) ev.getY())) {
// You have clicked the grey area
displayYourDialog();
return false; // stop activity closing
}
}
// Touch events inside are fine.
return super.onTouchEvent(ev);
}

dialog.setCanceledOnTouchOutside(true);
to close dialog on touch outside.
And if you don't want to close on touch outside, use the code below:
dialog.setCanceledOnTouchOutside(false);

You can try this :-
AlterDialog alterdialog;
alertDialog.setCanceledOnTouchOutside(true);
or
alertDialog.setCancelable(true);
And if you have a AlterDialog.Builder Then you can try this:-
alertDialogBuilder.setCancelable(true);

This code is use for when use click on dialogbox that time hidesoftinput and when user click outer side of dialogbox that time both softinput and dialogbox are close.
dialog = new Dialog(act) {
#Override
public boolean onTouchEvent(MotionEvent event) {
// Tap anywhere to close dialog.
Rect dialogBounds = new Rect();
getWindow().getDecorView().getHitRect(dialogBounds);
if (!dialogBounds.contains((int) event.getX(),
(int) event.getY())) {
// You have clicked the grey area
InputMethodManager inputMethodManager = (InputMethodManager) act
.getSystemService(act.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(dialog
.getCurrentFocus().getWindowToken(), 0);
dialog.dismiss();
// stop activity closing
} else {
InputMethodManager inputMethodManager = (InputMethodManager) act
.getSystemService(act.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(dialog
.getCurrentFocus().getWindowToken(), 0);
}
return true;
}
};

Another solution, this code was taken from android source code of Window
You should just add these Two methods to your dialog source code.
#Override
public boolean onTouchEvent(MotionEvent event) {
if (isShowing() && (event.getAction() == MotionEvent.ACTION_DOWN
&& isOutOfBounds(getContext(), event) && getWindow().peekDecorView() != null)) {
hide();
}
return false;
}
private boolean isOutOfBounds(Context context, MotionEvent event) {
final int x = (int) event.getX();
final int y = (int) event.getY();
final int slop = ViewConfiguration.get(context).getScaledWindowTouchSlop();
final View decorView = getWindow().getDecorView();
return (x < -slop) || (y < -slop)
|| (x > (decorView.getWidth()+slop))
|| (y > (decorView.getHeight()+slop));
}
This solution doesnt have this problem :
This works great except that the activity underneath also reacts to the touch event. Is there some way to prevent this? – howettl

Following has worked for me:
myDialog.setCanceledOnTouchOutside(true);

Call dialog.setCancelable(false); from your activity/fragment.

You can make a background occupying all the screen size transparent and listen to the onClick event to dismiss it.

I tried some answers still I faced a problem like when I press outside the dialog dialog was hiding but a dimmed view was showing, and pressing again would go to the parent activity. But actually I wanted to go the parent activity after first click.
So what I did was
dialog.setOnCancelListener(this);
and changed my activity to implement DialogInterface.OnCancelListener with
#Override
public void onCancel(DialogInterface dialog) {
finish();
}
And boom, it worked.

Here is the code
dialog.getWindow().getDecorView().setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent ev) {
if(MotionEvent.ACTION_DOWN == ev.getAction())
{
Rect dialogBounds = new Rect();
dialog. getWindow().getDecorView().getHitRect(dialogBounds);
if (!dialogBounds.contains((int) ev.getX(), (int) ev.getY())) {
// You have clicked the grey area
UiUtils.hideKeyboard2(getActivity());
return false; // stop activity closing
}
}
getActivity().dispatchTouchEvent(ev);
return false;
}
});
Try this one . you can hide the keyboard when you touch outside

Related

How to make Keyboard's POPUP window dismiss when not touching

I'm creating a custom soft keyboard, and created a PopupWindow to show when a key is long pressed, like when you long press E and it shows E, É, È for you to choose one. The popup has a key to close him, but I want to remove this key and make him shows just while the user is touching, then the user long press, drag to the key that he want and release.
I'm using android API 8.
The popup is created in a KeyboardView class in the onLongPress method.
final View custom = LayoutInflater.from(context)
.inflate(R.layout.popup_layout, new FrameLayout(context));
final PopupWindow popup = new PopupWindow(context);
popup.setContentView(custom);
popup.setWidth(WindowManager.LayoutParams.WRAP_CONTENT);
popup.setHeight(WindowManager.LayoutParams.WRAP_CONTENT);
popup.showAtLocation(this, Gravity.NO_GRAVITY, popupKey.x, popupKey.y-50);
The button for close the popup:
buttonCancel.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
popup.dismiss();
}
});
I think can use something with the onTouch method, but how to identify the event of releasing? And where use it? On the keyboardView, or maybe on the popup window?
I managed to do this with:
#Override
public boolean onTouchEvent(MotionEvent me){
if(popup != null && me.getAction() == MotionEvent.ACTION_UP){
popup.dismiss();
}
}
I first created a method to show on logcat the code of every touch event, then I got the code that appeared when I release the touch and compared with the documentation, and it it the code for MotionEvent.ADCTION_UP.
With this, it's just put a dismiss on the popup.
I saw that there is a private function called KeyboardView.dismissPopupKeyboard().
This is ugly, but because I am using the default PopupWindow (I didn't overrided OnLongPress()) and I saw thata KeyboardView.onClick() calls dismissPopupKeyboard() and that's it, what I did was:
#Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
if (event.getAction() == MotionEvent.ACTION_UP){
getOnKeyboardActionListener().onKey('1', null);
dismissPopupKeyboard();
}
return true; // Required for recieving subsequent events (ACTION_MOVE, ACTION_UP)
}

How to dismiss a pop_up window when clicking outside?

I want to know how to close a pop_up window once the user clicks outside it, I had a look at PopupWindow - Dismiss when clicked outside but without any luck, and I tried that code:
pw.setBackgroundDrawable(null);
pw.setOutsideTouchable(true);
pw.setTouchInterceptor(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event)
{
if(event.getAction() == MotionEvent.ACTION_OUTSIDE)
{
pw.dismiss();
return true;
}
return false;
}
});
Try this out.Hope it works :)
solution 1:
popupWindow.setFocusable(true);
popupWindow.update();
If this dont work.Then you can try this out.
solution 2:
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);
Or if your Dialog in non-model then,
1 - Set the flag-FLAG_NOT_TOUCH_MODAL for your dialog's window attribute
Window window = this.getWindow();
window.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
2 - Add another flag to windows properties,, FLAG_WATCH_OUTSIDE_TOUCH - this one is for dialog to receive touch event outside its visible region.
3 - Override onTouchEvent() of dialog and check for action type. if the action type is 'MotionEvent.ACTION_OUTSIDE' means, user is interacting outside the dialog region. So in this case, you can dimiss your dialog or decide what you wanted to perform. view plainprint?
public boolean onTouchEvent(MotionEvent event)
{
if(event.getAction() == MotionEvent.ACTION_OUTSIDE){
System.out.println("TOuch outside the dialog ******************** ");
this.dismiss();
}
return false;
}
Ok so fixed in the end.
First made the main layout which the popup sits on a relative layout. Then placed a full screen blank layout on top which I made invisible and transparent.
Then show when the popup is shown, set the full screen panel visible with setVisibility(View.VISIBLE); and hide when popup is hidden with setVisibility(View.GONE);
Also need to return true from an on touch listener for the layout with (To stop touch events passing back to the main layout):
blocker.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}
});
And give the popup window the properties:
setTouchable(true);
setOutsideTouchable(false);
Cheers
you should'nt set the BackgroundDrawable to null, because that kills the OnTouchListener ;
you should replace pw.setBackgroundDrawable(null); by pw.setBackgroundDrawable(new BitmapDrawable())
Better use a dialog Fragment for the same. It is made for the popup functionality and closes by default on pressing outside the dialog Fragment or using the hard back button.

How to handle touch outside the view in Android?

I've found implementation of "Undo Bar" used in Gmail application for Android. "UndoBar" is basically a View that is displayed on top of the layout.
Unfortunately it's not complete - it has no functionality of dismissing bar by touching screen outside the bar.
I've implemented FrameLayout that overrides onInterceptTouchEvent to handle bar dismissing but touching Action Bar does nothing.
Is there any way to handle such events from Action Bar?
Below there is an Image with "UndoBar"shown. What I want to achieve to handle touch in Action bar represented by red dot.
Try to override dispatchTouchEvent of your activity.
dispatchTouchEvent(MotionEvent event){
int x= event.getRawX();
int y= event.getRawY();
if(/*check bounds of your view*/){
// set your views visiblity to gone or what you want.
}
//for prevent consuming the event.
return super().dispatchTouchEvent(event);
}
update
dispatchTouchEvent(MotionEvent event){
int x= event.getRawX();
int y= event.getRawY();
return super().dispatchTouchEvent(event)||[YourView].onTouch(event);
}
To catch touch events for the whole screen including the ActionBar add a view to the Window.
View overlayView = new View(this);
WindowManager.LayoutParams p = new WindowManager.LayoutParams();
p.gravity = Gravity.TOP;
p.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
p.token = overlayView.getWindowToken();
overlayView.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View view, MotionEvent event) {
// Get the action bar
int actionBarHeight = actionBar.getHeight();
if ((event.getAction() == MotionEvent.ACTION_DOWN)
&& (event.getRawY() < actionBarHeight)) {
// Touch inside the actionBar so let's consume it
// Do something
return true;
}
return false;
}
});
WindowManager mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
mWindowManager.addView(overlayView, p);
Hope this helps.
You should just override Activity's dispatchTouchEvent(ev: MotionEvent) method
Look at this example of dismissing indefinite Snackbar
class MainActivity : AppCompatActivity() {
private var snackbar: Snackbar? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
// show indefinite snackbar
snackbar = Snackbar.make(coordinator_layout, "Hello world!", Snackbar.LENGTH_INDEFINITE).apply {
show()
}
}
/**
* On each touch event:
* Check is [snackbar] present and displayed
* and dismiss it if user touched anywhere outside it's bounds
*/
override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
// dismiss shown snackbar if user tapped anywhere outside snackbar
snackbar?.takeIf { it.isShown }?.run {
val touchPoint = Point(Math.round(ev.rawX), Math.round(ev.rawY))
if (!isPointInsideViewBounds(view, touchPoint)) {
dismiss()
snackbar = null // set snackbar to null to prevent this block being executed twice
}
}
// call super
return super.dispatchTouchEvent(ev)
}
/**
* Defines bounds of displayed view and check is it contains [Point]
* #param view View to define bounds
* #param point Point to check inside bounds
* #return `true` if view bounds contains point, `false` - otherwise
*/
private fun isPointInsideViewBounds(view: View, point: Point): Boolean = Rect().run {
// get view rectangle
view.getDrawingRect(this)
// apply offset
IntArray(2).also { locationOnScreen ->
view.getLocationOnScreen(locationOnScreen)
offset(locationOnScreen[0], locationOnScreen[1])
}
// check is rectangle contains point
contains(point.x, point.y)
}
}
Or check out the full gist
Have you tried an onTouchEvent on your FrameLayout..?
I haven't tried it but i think its makes sense to me that in case of MotionEvent on that layout ACTION_OUTSIDE action should get fired.
Try the following.
public boolean onTouchEvent(MotionEvent event)
{
if(event.getAction() == MotionEvent.ACTION_OUTSIDE){
undoBar.setVisibility(View.GONE);
}
return true;
}
There are 2 possible solutions.
As I mentioned in the comments you can either implement a scroll listener on your ListView and simple called hideUndoBar(true) on the slightest scroll.
OR
You can modify the UndoBarController. You'll notice that the undo bar is simply a View slap a OnFocusChange listener onto the View in the constructor and in the show method setFocus to the view.
In your OnFocusChange check to see if the view has lost focus and call hideUndoBar(true).
Update
I've created a Gist here https://gist.github.com/atgheb/5551961
showing how to change the UndoBarController to add the feature where it hides when it loses focus.
I haven't test it but I dont see why it won't work.
params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL|WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
PixelFormat.TRANSPARENT);
what you actually need is :
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL|WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
Documentation says:
Window flag: if you have set FLAG_NOT_TOUCH_MODAL, you can set this
flag to receive a single special MotionEvent with the action
MotionEvent.ACTION_OUTSIDE for touches that occur outside of your
window.
I wasn't satisfied with any of the answers I found here. My solution is to attach a touch listener to the parent view like so:
((View)getParent()).setOnTouchListener((v, event) -> {
return true;
});
You can get all the touch events in your Activity by using the below code. I guess this code would get the touches from ActionBar as well.
public class MainActivity extends Activity {
...
// This example shows an Activity, but you would use the same approach if
// you were subclassing a View.
#Override
public boolean onTouchEvent(MotionEvent event){
int action = MotionEventCompat.getActionMasked(event);
switch(action) {
case (MotionEvent.ACTION_DOWN) :
Log.d(DEBUG_TAG,"Action was DOWN");
return true;
case (MotionEvent.ACTION_MOVE) :
Log.d(DEBUG_TAG,"Action was MOVE");
return true;
case (MotionEvent.ACTION_UP) :
Log.d(DEBUG_TAG,"Action was UP");
return true;
case (MotionEvent.ACTION_CANCEL) :
Log.d(DEBUG_TAG,"Action was CANCEL");
return true;
case (MotionEvent.ACTION_OUTSIDE) :
Log.d(DEBUG_TAG,"Movement occurred outside bounds " +
"of current screen element");
return true;
default :
return super.onTouchEvent(event);
}
}

Android dialog set cancel on touch out side

I have this custom dialog inside an Activty which is inside ActivityGroup.
I want the dialog to dismiss when clicked outside, and tried everything i found online to make it work..
I've tried the setCanceledOnTouchOutside(true) - didn't work
I've tried:
public boolean onTouchEvent ( MotionEvent event ) {
// I only care if the event is an UP action
if ( event.getAction () == MotionEvent.ACTION_UP ) {
// create a rect for storing the window rect
Rect r = new Rect ( 0, 0, 0, 0 );
// retrieve the windows rect
this.getWindow ().getDecorView ().getHitRect ( r );
Log.i(r.toShortString(),r.toShortString());
// check if the event position is inside the window rect
boolean intersects = r.contains ( (int) event.getX (), (int) event.getY () );
// if the event is not inside then we can close the activity
if ( !intersects ) {
// close the activity
this.dismiss ();
// notify that we consumed this event
return true;
}
}
and it didn't work too..
as i see in the LogCat - i think that from some reason the dialog window size is full screened that why i have no "outside" to touch..
i think it might have to do something with the activity group.. any suggestions ?
Ok so after lots of thinking i found out the most simple solution:
The Problem:
From some reason - although the theme I've used is a dialog and not a full screen display - the getWindow().getDecorView() returns a View which covers the whole screen.
The Solution:
in my XML file I gave the root element an id and I've changed the function above as follow:
private View rootView;
public BaseDialog(Context context, int theme) {
super(context, theme);
//I don't think the next 2 lines are really important - but I've added them for safety
setCancelable(true);
setCanceledOnTouchOutside(true);
}
public void setRootView(int resourceId)
{
this.rootView = findViewById(resourceId);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
Rect rect = new Rect();
rootView.getHitRect(rect);
if (!rect.contains((int)event.getX(), (int)event.getY()))
{
this.dismiss();
return true;
}
return false;
}
Hope it will help someone... :)

Detect click over a dialog window in Android

I have a dialog:
final Dialog dialog = new Dialog(context);
dialog.setContentView(R.layout.location_dialog);
dialog.setTitle("My dialog");
dialog.setMessage("My dialog's content");
dialog.setCancelable(true);
dialog.setCanceledOnTouchOutside(true);
dialog.show();
I want to be able to detect touches over and outside the dialog box's lines.
I can easily detect any touches outside the dialog box area with the build-in method
dialog.setCanceledOnTouchOutside(true);
But how can I detect the touches inside this area?
Create an extension of Dialog and override necessary method: dispatchTouchEvent or onTouchEvent (From docs: This is most useful to process touch events that happen outside of your window bounds, where there is no view to receive it.)
Updated:
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Rect dialogBounds = new Rect();
getWindow().getDecorView().getHitRect(dialogBounds);
if (dialogBounds.contains((int) ev.getX(), (int) ev.getY())) {
Log.d("test", "inside");
} else {
Log.d("test", "outside");
}
return super.dispatchTouchEvent(ev);
}

Categories

Resources