Android - перехватывать и передавать все сенсорные события - PullRequest
13 голосов
/ 27 июля 2011

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

Меня интересуют все MotionEvents (не только DOWN), поэтому onInterceptTouchEvent () здесь не применяется, как если бы я возвращал true, мое наложение будет использовать все события, а если false, то получит только события DOWN (то же самое относится и к onTouch ).

Я думал, что мог бы переопределить dispatchTouchEvent Activity (MotionEvent ev) и вызвать настраиваемое сенсорное событие в моем оверлее, но это приводит к тому, что входные координаты не переводятся в зависимости от положения моего вида (например, все события будут проходить кажется, что происходит примерно на 20 пикселей ниже фактического касания, поскольку системная панель не учитывается).

Ответы [ 5 ]

7 голосов
/ 28 июля 2011

Я решил это не идеальным решением, но работает в этой ситуации.

Я создал еще один ViewGroup, который содержит кликабельный дочерний элемент, ширина и высота которого установлены на fill_parent (это было мое требование перед добавлением эффектов) и переопределены onIntercept...(). В onIntercept... я передаю событие в свой оверлейный вид пользовательскому методу onDoubleTouch(MotionEvent) и выполняю там соответствующую обработку, не прерывая маршрутизацию платформ входных событий и позволяя базовой группе просмотра работать как обычно.

Легко!

6 голосов
/ 12 февраля 2016

Единственный способ создать такой перехватчик - использовать пользовательский макет ViewGroup.

Но реализации ViewGroup.onInterceptTouchEvent () просто недостаточно в любом случае, потому что дочерние представления могут вызывать ViewParent.requestDisallowInterceptTouchEvent () , и если это делает дочерний вид, ваш вид перестанет получать вызовы interceptTouchEvent (см. здесь для примера, когда вы можете это сделать, когда я ребенок).

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

import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.FrameLayout;

/**
 * A FrameLayout that allow setting a delegate for intercept touch event
 */
public class InterceptTouchFrameLayout extends FrameLayout {
    private boolean mDisallowIntercept;

    public interface OnInterceptTouchEventListener {
        /**
         * If disallowIntercept is true the touch event can't be stealed and the return value is ignored.
         * @see android.view.ViewGroup#onInterceptTouchEvent(android.view.MotionEvent)
         */
        boolean onInterceptTouchEvent(InterceptTouchFrameLayout view, MotionEvent ev, boolean disallowIntercept);

        /**
         * @see android.view.View#onTouchEvent(android.view.MotionEvent)
         */
        boolean onTouchEvent(InterceptTouchFrameLayout view, MotionEvent event);
    }

    private static final class DummyInterceptTouchEventListener implements OnInterceptTouchEventListener {
        @Override
        public boolean onInterceptTouchEvent(InterceptTouchFrameLayout view, MotionEvent ev, boolean disallowIntercept) {
            return false;
        }
        @Override
        public boolean onTouchEvent(InterceptTouchFrameLayout view, MotionEvent event) {
            return false;
        }
    }

    private static final OnInterceptTouchEventListener DUMMY_LISTENER = new DummyInterceptTouchEventListener();

    private OnInterceptTouchEventListener mInterceptTouchEventListener = DUMMY_LISTENER;

    public InterceptTouchFrameLayout(Context context) {
        super(context);
    }

    public InterceptTouchFrameLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public InterceptTouchFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public InterceptTouchFrameLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyle) {
        super(context, attrs, defStyleAttr, defStyle);
    }

    @Override
    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
        getParent().requestDisallowInterceptTouchEvent(disallowIntercept);
        mDisallowIntercept = disallowIntercept;
    }

    public void setOnInterceptTouchEventListener(OnInterceptTouchEventListener interceptTouchEventListener) {
        mInterceptTouchEventListener = interceptTouchEventListener != null ? interceptTouchEventListener : DUMMY_LISTENER;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        boolean stealTouchEvent = mInterceptTouchEventListener.onInterceptTouchEvent(this, ev, mDisallowIntercept);
        return stealTouchEvent && !mDisallowIntercept || super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        boolean handled = mInterceptTouchEventListener.onTouchEvent(this, event);
        return handled || super.onTouchEvent(event);
    }
}

Этот класс предоставляет setInterceptTouchEventListener() для установки пользовательского перехватчика.

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

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

2 голосов
/ 05 сентября 2012

Вот что я сделал, основываясь на ответе Дори, который использовал на InterceptTouch. , ,

float xPre = 0;

@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
    if(Math.abs(xPre - event.getX()) < 40){
        return false;
    }
    if(event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_UP  || event.getAction() == MotionEvent.ACTION_MOVE){
        xPre = event.getX();
    }
    return true;
}

Как правило, когда выполняется какое-либо важное движение, запомните, где оно произошло, с помощью xPre; затем, если эта точка находится достаточно близко к следующей точке, чтобы сказать, onClick / onTouch дочернего представления возвращает false и позволяет onTouch делать то, что по умолчанию.

1 голос
/ 24 мая 2017

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

Например, если в корневом представлении A есть два дочерних элемента X и Y, и вы хотите перехватить касание пальцем в A и хотите передать остальное касаниедля дочерних представлений (X и Y) затем переопределите OnTouchListner для X и Y, возвращая false для удара и true для отдыха.И переопределите OnTouchListner для A, проведя пальцем, возвращая true, а остальные возвращая false.

0 голосов
/ 24 января 2018

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

Я наконец нашел решение, которое работает. Используя библиотеку RxBinding, вы можете наблюдать все сенсорные события, включая события, которые потребляются перекрывающимися дочерними представлениями.

Фрагмент кода Kotlin

RxView.touches(view)
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(

        object  : Observer<MotionEvent> {
            override fun onCompleted() {
            }

            override fun onNext(t: MotionEvent?) {
                Log.d("motion event onNext $t")
            }

            override fun onError(e: Throwable?) {
            }
        }
    )

См. здесь и здесь для получения более подробной информации.

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