OnLongClickListener для childView отключает OnTouchListener для parentView - PullRequest
2 голосов
/ 15 сентября 2010

У меня есть AbsoluteLayout, который имеет OnTouchListener. Внутри этого макета гораздо меньше LinearLayout, расположенное динамически. OnTouchListener работает как положено.

Теперь проблема возникает, когда я добавляю LongClickListener к своему LinearLayout. Это отключает мой OnTouchListener, если касание касается LinearLayout, но оно все еще срабатывает, если LinearLayout было , а не , пораженное касанием.

Мои слушатели:

// listener on parent (AbsoluteLayout)
setOnTouchListener(new OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        Log.e("LOOOOGING");
        mLinearLayout.getHitRect(mNoteRect);
        mNoteRect.left += mX;
        mNoteRect.top += mY;
        mNoteRect.right = mNoteRect.left + mLinearLayout.getWidth();
        mNoteRect.bottom = mNoteRect.top + mLinearLayout.getHeight();
        if (mNoteRect.contains((int) event.getX(), (int) event.getY())) {
            if (event.getAction() == MotionEvent.ACTION_DOWN) {
                mStartX = (int) event.getX() - mNoteRect.left;
                mStartY = (int) event.getY() - mNoteRect.top;
                return true;
            }
            mX = (int) event.getX() - mStartX;
            mY = (int) event.getY() - mStartY;

            setPadding(mX, mY, 0, 0);
            return true;
        }
        return false;
    }
});

// listener on child (LinearLayout)
mLinearLayout.setOnLongClickListener(new OnLongClickListener() {
    @Override
    public boolean onLongClick(View v) {
        // do something...
        return true;
    }
});

Как я могу передать касание на LinearLayout, где зарегистрирован OnLongClickListener, родителю?

Ответы [ 3 ]

3 голосов
/ 16 сентября 2010

Я должен был построить свое собственное поведение при длинном клике внутри моего сенсорного прослушивателя

private Handler mLongPressHandler = new Handler();

public final Runnable mDoLongPress = new Runnable() {
    public void run() {
        // do something
    }
};

setOnTouchListener(new OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        mLinearLayout.getHitRect(mNoteRect);
        mNoteRect.left += mX;
        mNoteRect.top += mY;
        mNoteRect.right = mNoteRect.left + mLinearLayout.getWidth();
        mNoteRect.bottom = mNoteRect.top + mLinearLayout.getHeight();
        if (mNoteRect.contains((int) event.getX(), (int) event.getY())) {
            if (event.getAction() == MotionEvent.ACTION_DOWN) {
                mStartRawX = (int) event.getX();
                mStartRawY = (int) event.getY();
                mStartX = mStartRawX - mNoteRect.left;
                mStartY = mStartRawY - mNoteRect.top;
                mLongPressHandler.postDelayed(mDoLongPress, 1000);
                return true;
            }
            mX = (int) event.getX() - mStartX;
            mY = (int) event.getY() - mStartY;
            if (event.getAction() == MotionEvent.ACTION_MOVE) {
                if ((mStartRawX + 10 < (int) event.getX() || mStartRawX - 10 > (int) event.getX())
                        || (mStartRawY + 10 < (int) event.getY() || mStartRawY - 10 > (int) event.getY())) {
                    mLongPressHandler.removeCallbacks(mDoLongPress);
                }
            }
            if (event.getAction() == MotionEvent.ACTION_UP) {
                mLongPressHandler.removeCallbacks(mDoLongPress);
            }

            setPadding(mX, mY, 0, 0);
            return true;
        }
        return false;
    }
});
1 голос
/ 13 августа 2014

Идея Марага об отправке dispatchTouchEvent () из onLongClick () казалась многообещающей, но вам пришлось бы создать объект события для отправки в dispatchTouchEvent (), чтобы имитировать событие, использованное OnLongClickListener.

Я перевернул это, чтобы перехватить событие касания в родительском представлении, а затем перенаправить его в дочернее представление. Я подклассифицировал родительское представление, затем добавил этот метод:

@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
    // this allows us to catch touch events on the list view if a child button is in the same location, then pass them on to child buttons so they can use them, too
    // we don't have to worry about move events because currently none of the child buttons use them
    int touchX = Math.round(event.getX());
    int touchY = Math.round(event.getY());
    Rect touchRect = new Rect(touchX, touchY, touchX, touchY);
    Log.d("onInterceptTouchEvent", "got touch at " + touchRect);
    switch (event.getAction() & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN:
        case MotionEvent.ACTION_UP:
        case MotionEvent.ACTION_CANCEL:
            for (View cell : ViewUtils.getSubviews(this)) {
                int cellX = Math.round(cell.getX());
                int cellY = Math.round(cell.getY());
                Rect cellRect = new Rect(cellX, cellY, cellX + cell.getWidth(), cellY + cell.getHeight());
                if (Rect.intersects(touchRect, cellRect)) {
                    for (View button : ViewUtils.getSubviews((ViewGroup) cell)) {
                        if (button instanceof ImageButton) {
                            int buttonX = Math.round(button.getX()) + cellX;
                            int buttonY = Math.round(button.getY()) + cellY;
                            Rect buttonRect = new Rect(buttonX, buttonY, buttonX + button.getWidth(), buttonY + button.getHeight());
                            Log.d("onInterceptTouchEvent", "found button at " + buttonRect);
                            if (Rect.intersects(touchRect, buttonRect)) {
                                Log.d("onInterceptTouchEvent", "forward touch to button");
                                button.dispatchTouchEvent(event);
                                break;
                            }
                        }
                    }
                    break;
                }
            }
            break;
    }
    return true;
}

В моем случае родительское представление - это ListView, а дочерние представления - это кнопки ImageButtons внутри ячеек таблицы. Таким образом, этот код перебирает ячейки таблицы, затем кнопки в каждой ячейке, чтобы найти кнопку, которая соответствует местоположению касания, и передает касание этой кнопке. Все мои кнопки - это кнопки ImageButton, которые используют OnClickListener или OnLongClickListener, поэтому я не пересылаю события ACTION_MOVE, но вы можете при необходимости.

Вот метод getSubViews (), использованный выше:

public static ArrayList<View> getSubviews(ViewGroup viewGroup) {
    ArrayList<View> subviews = new ArrayList<View>();
    for (int i=0; i<viewGroup.getChildCount(); i++) {
        subviews.add(viewGroup.getChildAt(i));
    }
    return subviews;
}

Обновление: более простая версия

Приведенный выше код должен работать для конкретной ситуации получения прикосновений к родительскому представлению и длительных нажатий на дочернем представлении. Но я обнаружил, что это не поддерживает регулярные клики в дочерних представлениях. Я думаю, что это связано с тем, что onInterceptTouchEvent () обрабатывает события ACTION_DOWN иначе, чем другие события. Документация для этого метода очень запутанная.

Однако вот более простой подход, который должен поддерживать все виды событий касания и нажатия в родительском или дочернем представлениях. Как и выше, это требует создания подкласса родительского представления. Также требуется установить метод onTouch непосредственно в этом классе, а не использовать setOnTouchListener () в другом классе:

@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
    this.onTouch(this, event); // send the touch to the onTouch method below
    return false; // then let the touch proceed to child buttons
    // return true = this method and this view's onTouch receives events; return false = this method and children's onTouch receive events; remove this method = only children's onTouch receive events
}

@Override
public boolean onTouch(View v, MotionEvent event) {
    // work with this touch event here
    return false; // then let the touch continue to other applicable views
    // return true = only this view receives events; return false = this view and other applicable views receive events
}
1 голос
/ 15 сентября 2010

Вы пробовали что-то подобное?

   // listener on child (LinearLayout)
mLinearLayout.setOnLongClickListener(new OnLongClickListener() {
    @Override
    public boolean onLongClick(View v) {
        AbsoluteLayout.requestFocus();
        //do something else
        return true;
    }
});

Из того, что я прочитал, прикосновение эквивалентно получению фокуса.( обработка событий пользовательского интерфейса )

edit: проверка документации absoluteLayout, может быть, это может помочь: dispatchTouchEvent (MotionEvent ev) .Попробуйте поиграть с ним, звучит как запуск его из публичного логического onLongClick (View v) может помочь

Передать событие движения сенсорного экрана вниз к целевому представлению или этому представлениюесли это цель.

...