Android make view disappear by clicking outside of it

佐手、 提交于 2019-11-29 22:55:25

An easy/stupid way:

  • Create a dummy empty view (let's say ImageView with no source), make it fill parent

  • If it is clicked, then do what you want to do.

You need to have the root tag in your XML file to be a RelativeLayout. It will contain two element: your dummy view (set its position to align the Parent Top). The other one is your original view containing the views and the button (this view might be a LinearLayout or whatever you make it. don't forget to set its position to align the Parent Top)

Hope this will help you, Good Luck !

britzl

This is an old question but I thought I'd give an answer that isn't based on onTouch events. As was suggested by RedLeader it's also possible to achieve this using focus events. I had a case where I needed to show and hide a bunch of buttons arranged in a custom popup, ie the buttons were all placed in the same ViewGroup. Some things you need to do to make this work:

  1. The view group that you wish to hide needs to have View.setFocusableInTouchMode(true) set. This can also be set in XML using android:focusableintouchmode.

  2. Your view root, i.e. the root of your entire layout, probably some kind of Linear or Relative Layout, also needs to be able to be focusable as per #1 above

  3. When the view group is shown you call View.requestFocus() to give it focus.

  4. Your view group need to either override View.onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) or implement your own OnFocusChangeListener and use View.setOnFocusChangeListener()

  5. When the user taps outside your view focus is transferred to either the view root (since you set it as focusable in #2) or to another view that inherently is focusable (EditText or similar)

  6. When you detect focus loss using one of the methods in #4 you know that focus has be transferred to something outside your view group and you can hide it.

I guess this solution doesn't work in all scenarios, but it worked in my specific case and it sounds as if it could work for the OP as well.

Find the view rectangle, and then detect whether the click event is outside the view.

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    Rect viewRect = new Rect();
    mTooltip.getGlobalVisibleRect(viewRect);
    if (!viewRect.contains((int) ev.getRawX(), (int) ev.getRawY())) {
        setVisibility(View.GONE);
    }
    return true;
}

If you want to use the touch event other place, try

return super.dispatchTouchEvent(ev);

I've been looking for a way to close my view when touching outside and none of these methods fit my needs really well. I did find a solution and will just post it here in case anyone is interested.

I have a base activity which pretty much all my activities extend. In it I have:

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    if (myViewIsVisible()){
            closeMyView();
        return true;
    }
    return super.dispatchTouchEvent(ev);
}

So if my view is visible it will just close, and if not it will behave like a normal touch event. Not sure if it's the best way to do it, but it seems to work for me.

I needed the specific ability to not only remove a view when clicking outside it, but also allow the click to pass through to the activity normally. For example, I have a separate layout, notification_bar.xml, that I need to dynamically inflate and add to whatever the current activity is when needed.

If I create an overlay view the size of the screen to receive any clicks outside of the notification_bar view and remove both these views on a click, the parent view (the main view of the activity) has still not received any clicks, which means, when the notification_bar is visible, it takes two clicks to click a button (one to dismiss the notification_bar view, and one to click the button).

To solve this, you can just create your own DismissViewGroup that extends ViewGroup and overrides the following method:

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    ViewParent parent = getParent();
    if(parent != null && parent instanceof ViewGroup) {
        ((ViewGroup) parent).removeView(this);
    }
    return super.onInterceptTouchEvent(ev);
}

And then your dynamically added view will look a little like:

<com.example.DismissViewGroup android:id="@+id/touch_interceptor_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/transparent" ...
    <LinearLayout android:id="@+id/notification_bar_view" ...

This will allow you to interact with the view, and the moment you click outside the view, you both dismiss the view and interact normally with the activity.

Implement onTouchListener(). Check that the coordinates of the touch are outside of the coordinates of your view.

There is probably some kind of way to do it with onFocus(), etc. - But I don't know it.

I've created custom ViewGroup to display info box anchored to another view (popup balloon). Child view is actual info box, BalloonView is fullscreen for absolute positioning of child, and intercepting touch.

public BalloonView(View anchor, View child) {
    super(anchor.getContext());
    //calculate popup position relative to anchor and do stuff
    init(...);
    //receive child via constructor, or inflate/create default one
    this.child = child;
    //this.child = inflate(...);
    //this.child = new SomeView(anchor.getContext());
    addView(child);
    //this way I don't need to create intermediate ViewGroup to hold my View
    //but it is fullscreen (good for dialogs and absolute positioning)
    //if you need relative positioning, see @iturki answer above 
    ((ViewGroup) anchor.getRootView()).addView(this);
}

private void dismiss() {
    ((ViewGroup) getParent()).removeView(this);
}

Handle clicks inside child:

child.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        //write your code here to handle clicks inside
    }
});

To dismiss my View by click outside WITHOUT delegating touch to underlying View:

BalloonView.this.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        dismiss();
    }
});

To dismiss my View by click outside WITH delegating touch to underlying View:

@Override
public boolean onTouchEvent(MotionEvent event) {
    dismiss();
    return false; //allows underlying View to handle touch
}

To dismiss on Back button pressed:

//do this in constructor to be able to intercept key
setFocusableInTouchMode(true);
requestFocus();

@Override
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_BACK) {
        dismiss();
        return true;
    }
    return super.onKeyPreIme(keyCode, event);
}

base on Kai Wang answer : i suggest first check visibility of Your view , base on my scenario when user clicked on fab myView become visible and then when user click outside myView disappears

  @Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    Rect viewRect = new Rect();
    myView.getGlobalVisibleRect(viewRect);
    if (myView.getVisibility() == View.VISIBLE && !viewRect.contains((int) ev.getRawX(), (int) ev.getRawY())) {
        goneAnim(myView);
        return true;
    }
    return super.dispatchTouchEvent(ev);
}

thank @ituki for idea

FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/search_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#80000000"
android:clickable="true">

<LinearLayout
    android:clickable="true" // not trigger
    android:layout_width="match_parent"
    android:layout_height="300dp" 
    android:background="#FFF"
    android:orientation="vertical"
    android:padding="20dp">

    ...............

</LinearLayout>
</FrameLayout>

and java code

mContainer = (View) view.findViewById(R.id.search_container);
    mContainer.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            if(event.getAction() == MotionEvent.ACTION_DOWN){
                Log.d("aaaaa", "outsite");
                return true;
            }
            return false;
        }
    });

it's work when touch outside LinearLayout

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!