Pregunta

Tengo una AbsoluteLayout que tiene una OnTouchListener. Dentro de este esquema hay un LinearLayout mucho más pequeño colocado de forma dinámica. El OnTouchListener funciona como se espera.

Ahora el problema viene cuando agrego un LongClickListener a mi LinearLayout. Que desactiva mi OnTouchListener si el contacto golpea el LinearLayout, pero aún así se activa si el LinearLayout fue no hit por el tacto.

Mis oyentes:

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

¿Cómo puedo delegar el toque en la LinearLayout donde está registrado el OnLongClickListener, a los padres?

¿Fue útil?

Solución

tuve que construir mi propio comportamiento longclick dentro de mi ontouchlistener

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

Otros consejos

Have you tried something like this?

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

From what I've read, touch is equivalent to gaining focus. (handling UI events)

edit: checking the absoluteLayout documentation, maybe this could help: dispatchTouchEvent(MotionEvent ev). Try playing with it, sounds like launching it from the public boolean onLongClick(View v) could help

Pass the touch screen motion event down to the target view, or this view if it is the target.

Maragues's idea to send dispatchTouchEvent() from onLongClick() seemed promising, but you would have to build an event object to send to dispatchTouchEvent() to mimic the event that was consumed by the OnLongClickListener.

I flipped this around to intercept the touch event in the parent view, then forward it to the child view. I subclassed the parent view, then added this method:

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

In my case, the parent view is a ListView and the child views are ImageButtons inside the table cells. So this code iterates through the table cells, then the buttons in each cell, to find a button that matches the touch location, and forwards the touch to that button. My buttons are all ImageButtons that use OnClickListener or OnLongClickListener, so I'm not forwarding ACTION_MOVE events, but you could if needed.

Here's the getSubViews() method used above:

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

Update: Simpler Version

The code above should work for the specific situation of receiving touches on a parent view and long clicks on a child view. But I found that this does not support regular clicks in child views. I think this is related to the way that onInterceptTouchEvent() handles ACTION_DOWN events differently than it handles other events. The documentation for this method is very confusing.

However, here's a simpler approach that should support all kinds of touch and click events in either the parent or the child views. As above, this requires subclassing the parent view. It also requires setting the onTouch method directly in that class rather than using setOnTouchListener() in another class:

@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
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top