Android сделать вид исчезнет, ​​нажав за пределами него - PullRequest
31 голосов
/ 14 июля 2011

У меня есть некоторые виды, которые я вижу после нажатия кнопки.Я хочу, чтобы они исчезли, если я нажму за пределами этих представлений.

Как это будет сделано на Android?

Кроме того, я понимаю, что «кнопка назад» также может помочь пользователям Android в этом - я мог бы использовать это в качестве дополнительного способа закрытия представлений - но некоторые планшеты даже не используют «физический»«Кнопка возврата больше, это было очень подчеркнуто.

Ответы [ 9 ]

33 голосов
/ 14 июля 2011

Простой / тупой способ:

  • Создать пустое пустое представление (скажем, ImageView без источника), заставить его заполнить родительский

  • Если щелкнуть по нему, то делай, что хочешь.

Вам необходимо иметь корневой тег в вашем XML-файле, чтобы он был RelativeLayout. Он будет содержать два элемента: ваш фиктивный вид (установите его положение на align the Parent Top). Другой - ваш исходный вид, содержащий виды и кнопку (это может быть LinearLayout или что-то еще, что вы делаете. Не забудьте установить его положение на align the Parent Top)

Надеюсь, это поможет вам, удачи!

24 голосов
/ 09 февраля 2013

Это старый вопрос, но я подумал, что дам ответ, который не основан на onTouch событиях. Как было предложено RedLeader, этого также можно достичь, используя события фокуса. У меня был случай, когда мне нужно было показать и скрыть связку кнопок, расположенных в пользовательском всплывающем окне, то есть все кнопки были расположены в одном и том же ViewGroup. Некоторые вещи, которые вам нужно сделать, чтобы сделать эту работу:

  1. Для группы просмотра, которую вы хотите скрыть, должна быть установлена ​​View.setFocusableInTouchMode(true). Это также может быть установлено в XML с помощью android:focusableintouchmode.

  2. Ваш корень представления, т. Е. Корень всего макета, возможно, какой-то линейный или относительный макет, также должен иметь возможность фокусироваться в соответствии с номером 1 выше

  3. Когда отображается группа представлений, вы звоните View.requestFocus(), чтобы выделить ее.

  4. Ваша группа просмотра должна либо переопределить View.onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect), либо реализовать собственную OnFocusChangeListener и использовать View.setOnFocusChangeListener()

  5. Когда пользователь, касающийся за пределами вашего фокуса вида, переносится либо в корень вида (поскольку вы установили его как фокусируемый в # 2), либо в другой вид, который по своей природе является фокусируемым (EditText или аналогичный)

  6. Когда вы обнаруживаете потерю фокуса одним из методов из # 4, вы знаете, что фокус перенесен на что-то, находящееся за пределами вашей группы просмотра, и вы можете скрыть это.

Полагаю, это решение не работает во всех сценариях, но оно работает в моем конкретном случае и звучит так, как будто оно может работать и для OP.

22 голосов
/ 20 августа 2015

Найдите прямоугольник вида и затем определите, находится ли событие щелчка за пределами вида.

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

Если вы хотите использовать событие касания в другом месте, попробуйте

return super.dispatchTouchEvent(ev);
10 голосов
/ 26 февраля 2014

Я искал способ закрыть взгляд при касании снаружи, и ни один из этих методов не соответствовал моим потребностям. Я нашел решение и просто опубликую его здесь на случай, если кому-то будет интересно.

У меня есть базовое занятие, которое распространяется почти на все мои действия. В ней у меня есть:

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

Так что, если мой взгляд виден, он просто закроется, а если нет, он будет вести себя как обычное событие касания. Не уверен, что это лучший способ сделать это, но мне кажется, это работает.

2 голосов
/ 03 сентября 2014

Мне нужна была особая способность не только удалять вид при клике за его пределами, но и позволять клику проходить к действию в обычном режиме. Например, у меня есть отдельный макет Notification_bar.xml, который мне нужно динамически раздувать и добавлять к любой текущей активности, когда это необходимо.

Если я создаю представление наложения по размеру экрана, чтобы получать какие-либо щелчки за пределами представления панели уведомлений, и удаляю оба этих представления по щелчку, родительское представление (основной вид действия) все еще не получало никаких щелчков, это означает, что когда панель уведомлений видима, для нажатия кнопки требуется два щелчка (один для закрытия представления панели уведомлений, а другой для нажатия кнопки).

Чтобы решить эту проблему, вы можете просто создать собственную DismissViewGroup, которая расширяет ViewGroup и переопределяет следующий метод:

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

И тогда ваш динамически добавленный вид будет выглядеть примерно так:

<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" ...

Это позволит вам взаимодействовать с представлением, и в тот момент, когда вы щелкаете за пределами представления, вы оба отклоняете представление и нормально взаимодействуете с действием.

0 голосов
/ 11 сентября 2018

на основе ответа Кай Вана: я предлагаю сначала проверить видимость Вашего представления, основываясь на моем сценарии, когда пользователь нажимает на fab myView, становятся видимыми, а затем, когда пользователь щелкает вне myView, исчезает

  @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);
}
0 голосов
/ 28 января 2016

спасибо @ituki за идею

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>

и код Java

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

это работает, когда касание вне LinearLayout

0 голосов
/ 05 ноября 2015

Я создал пользовательскую ViewGroup для отображения информационного окна, привязанного к другому представлению (всплывающее окно).Дочернее представление - это актуальное информационное окно, BalloonView - полноэкранное отображение для абсолютного позиционирования дочернего элемента и перехвата касания.

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

Обрабатывать щелчки внутри дочернего элемента:

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

Чтобы закрыть мой просмотр, щелкнув его снаружиБЕЗ делегирования касания нижележащему представлению:

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

Чтобы закрыть мой вид, щелкнув снаружи, НЕ делегировав касание нижележащему представлению:

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

Чтобы отклонить нажатие кнопки «Назад»:

//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);
}
0 голосов
/ 14 июля 2011

Реализация onTouchListener().Убедитесь, что координаты касания находятся за пределами координат вашего взгляда.

Возможно, есть какой-то способ сделать это с помощью onFocus() и т. Д. - но я этого не знаю.

...