Stop overriding InputView.dispatchTouchEvent

Bug: 11721001
Bug: 11976254
Change-Id: I950f13ec4084da7fd9c1c25fd7abed1e5d31ed4c
main
Tadashi G. Takaoka 2014-02-17 17:36:34 +09:00
parent 891e4860f8
commit 5cd732f8c5
1 changed files with 62 additions and 55 deletions

View File

@ -31,6 +31,7 @@ public final class InputView extends LinearLayout {
private final Rect mInputViewRect = new Rect(); private final Rect mInputViewRect = new Rect();
private KeyboardTopPaddingForwarder mKeyboardTopPaddingForwarder; private KeyboardTopPaddingForwarder mKeyboardTopPaddingForwarder;
private MoreSuggestionsViewCanceler mMoreSuggestionsViewCanceler; private MoreSuggestionsViewCanceler mMoreSuggestionsViewCanceler;
private MotionEventForwarder<?, ?> mActiveForwarder;
public InputView(final Context context, final AttributeSet attrs) { public InputView(final Context context, final AttributeSet attrs) {
super(context, attrs, 0); super(context, attrs, 0);
@ -53,42 +54,62 @@ public final class InputView extends LinearLayout {
} }
@Override @Override
public boolean dispatchTouchEvent(final MotionEvent me) { public boolean onInterceptTouchEvent(final MotionEvent me) {
final Rect rect = mInputViewRect; final Rect rect = mInputViewRect;
getGlobalVisibleRect(rect); getGlobalVisibleRect(rect);
final int x = (int)me.getX() + rect.left; final int index = me.getActionIndex();
final int y = (int)me.getY() + rect.top; final int x = (int)me.getX(index) + rect.left;
final int y = (int)me.getY(index) + rect.top;
// The touch events that hit the top padding of keyboard should be // The touch events that hit the top padding of keyboard should be forwarded to
// forwarded to {@link SuggestionStripView}. // {@link SuggestionStripView}.
if (mKeyboardTopPaddingForwarder.dispatchTouchEvent(x, y, me)) { if (mKeyboardTopPaddingForwarder.onInterceptTouchEvent(x, y, me)) {
mActiveForwarder = mKeyboardTopPaddingForwarder;
return true; return true;
} }
// To cancel {@link MoreSuggestionsView}, we should intercept a touch event to // To cancel {@link MoreSuggestionsView}, we should intercept a touch event to
// {@link MainKeyboardView} and dismiss the {@link MoreSuggestionsView}. // {@link MainKeyboardView} and dismiss the {@link MoreSuggestionsView}.
if (mMoreSuggestionsViewCanceler.dispatchTouchEvent(x, y, me)) { if (mMoreSuggestionsViewCanceler.onInterceptTouchEvent(x, y, me)) {
mActiveForwarder = mMoreSuggestionsViewCanceler;
return true; return true;
} }
return super.dispatchTouchEvent(me);
mActiveForwarder = null;
return false;
}
@Override
public boolean onTouchEvent(final MotionEvent me) {
if (mActiveForwarder == null) {
return super.onTouchEvent(me);
}
final Rect rect = mInputViewRect;
getGlobalVisibleRect(rect);
final int index = me.getActionIndex();
final int x = (int)me.getX(index) + rect.left;
final int y = (int)me.getY(index) + rect.top;
return mActiveForwarder.onTouchEvent(x, y, me);
} }
/** /**
* This class forwards series of {@link MotionEvent}s from <code>Forwarder</code> view to * This class forwards series of {@link MotionEvent}s from <code>SenderView</code> to
* <code>Receiver</code> view. * <code>ReceiverView</code>.
* *
* @param <Sender> a {@link View} that may send a {@link MotionEvent} to <Receiver>. * @param <SenderView> a {@link View} that may send a {@link MotionEvent} to <ReceiverView>.
* @param <Receiver> a {@link View} that receives forwarded {@link MotionEvent} from * @param <ReceiverView> a {@link View} that receives forwarded {@link MotionEvent} from
* <Forwarder>. * <SenderView>.
*/ */
private static abstract class MotionEventForwarder<Sender extends View, Receiver extends View> { private static abstract class
protected final Sender mSenderView; MotionEventForwarder<SenderView extends View, ReceiverView extends View> {
protected final Receiver mReceiverView; protected final SenderView mSenderView;
protected final ReceiverView mReceiverView;
private boolean mIsForwardingEvent;
protected final Rect mEventSendingRect = new Rect(); protected final Rect mEventSendingRect = new Rect();
protected final Rect mEventReceivingRect = new Rect(); protected final Rect mEventReceivingRect = new Rect();
public MotionEventForwarder(final Sender senderView, final Receiver receiverView) { public MotionEventForwarder(final SenderView senderView, final ReceiverView receiverView) {
mSenderView = senderView; mSenderView = senderView;
mReceiverView = receiverView; mReceiverView = receiverView;
} }
@ -96,12 +117,12 @@ public final class InputView extends LinearLayout {
// Return true if a touch event of global coordinate x, y needs to be forwarded. // Return true if a touch event of global coordinate x, y needs to be forwarded.
protected abstract boolean needsToForward(final int x, final int y); protected abstract boolean needsToForward(final int x, final int y);
// Translate global x-coordinate to <code>Receiver</code> local coordinate. // Translate global x-coordinate to <code>ReceiverView</code> local coordinate.
protected int translateX(final int x) { protected int translateX(final int x) {
return x - mEventReceivingRect.left; return x - mEventReceivingRect.left;
} }
// Translate global y-coordinate to <code>Receiver</code> local coordinate. // Translate global y-coordinate to <code>ReceiverView</code> local coordinate.
protected int translateY(final int y) { protected int translateY(final int y) {
return y - mEventReceivingRect.top; return y - mEventReceivingRect.top;
} }
@ -109,49 +130,36 @@ public final class InputView extends LinearLayout {
// Callback when a {@link MotionEvent} is forwarded. // Callback when a {@link MotionEvent} is forwarded.
protected void onForwardingEvent(final MotionEvent me) {} protected void onForwardingEvent(final MotionEvent me) {}
// Dispatches a {@link MotioneEvent} to <code>Receiver</code> if needed and returns true. // Returns true if a {@link MotionEvent} is needed to be forwarded to
// Otherwise returns false. // <code>ReceiverView</code>. Otherwise returns false.
public boolean dispatchTouchEvent(final int x, final int y, final MotionEvent me) { public boolean onInterceptTouchEvent(final int x, final int y, final MotionEvent me) {
// Forwards a {link MotionEvent} only if both <code>Sender</code> and // Forwards a {link MotionEvent} only if both <code>SenderView</code> and
// <code>Receiver</code> are visible. // <code>ReceiverView</code> are visible.
if (mSenderView.getVisibility() != View.VISIBLE || if (mSenderView.getVisibility() != View.VISIBLE ||
mReceiverView.getVisibility() != View.VISIBLE) { mReceiverView.getVisibility() != View.VISIBLE) {
return false; return false;
} }
final Rect sendingRect = mEventSendingRect; mSenderView.getGlobalVisibleRect(mEventSendingRect);
mSenderView.getGlobalVisibleRect(sendingRect); if (!mEventSendingRect.contains(x, y)) {
if (!mIsForwardingEvent && !sendingRect.contains(x, y)) {
return false; return false;
} }
boolean shouldForwardToReceiver = false; if (me.getActionMasked() == MotionEvent.ACTION_DOWN) {
// If the down event happens in the forwarding area, successive
switch (me.getActionMasked()) { // {@link MotionEvent}s should be forwarded to <code>ReceiverView</code>.
case MotionEvent.ACTION_DOWN:
// If the down event happens in the forwarding area, successive {@link MotionEvent}s
// should be forwarded.
if (needsToForward(x, y)) { if (needsToForward(x, y)) {
mIsForwardingEvent = true; return true;
shouldForwardToReceiver = true;
} }
break;
case MotionEvent.ACTION_MOVE:
shouldForwardToReceiver = mIsForwardingEvent;
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
shouldForwardToReceiver = mIsForwardingEvent;
mIsForwardingEvent = false;
break;
} }
if (!shouldForwardToReceiver) {
return false; return false;
} }
final Rect receivingRect = mEventReceivingRect; // Returns true if a {@link MotionEvent} is forwarded to <code>ReceiverView</code>.
mReceiverView.getGlobalVisibleRect(receivingRect); // Otherwise returns false.
// Translate global coordinates to <code>Receiver</code> local coordinates. public boolean onTouchEvent(final int x, final int y, final MotionEvent me) {
mReceiverView.getGlobalVisibleRect(mEventReceivingRect);
// Translate global coordinates to <code>ReceiverView</code> local coordinates.
me.setLocation(translateX(x), translateY(y)); me.setLocation(translateX(x), translateY(y));
mReceiverView.dispatchTouchEvent(me); mReceiverView.dispatchTouchEvent(me);
onForwardingEvent(me); onForwardingEvent(me);
@ -189,8 +197,7 @@ public final class InputView extends LinearLayout {
protected int translateY(final int y) { protected int translateY(final int y) {
final int translatedY = super.translateY(y); final int translatedY = super.translateY(y);
if (isInKeyboardTopPadding(y)) { if (isInKeyboardTopPadding(y)) {
// The forwarded event should have coordinates that are inside of // The forwarded event should have coordinates that are inside of the target.
// the target.
return Math.min(translatedY, mEventReceivingRect.height() - 1); return Math.min(translatedY, mEventReceivingRect.height() - 1);
} }
return translatedY; return translatedY;
@ -200,8 +207,8 @@ public final class InputView extends LinearLayout {
/** /**
* This class forwards {@link MotionEvent}s happened in the {@link MainKeyboardView} to * This class forwards {@link MotionEvent}s happened in the {@link MainKeyboardView} to
* {@link SuggestionStripView} when the {@link MoreSuggestionsView} is showing. * {@link SuggestionStripView} when the {@link MoreSuggestionsView} is showing.
* {@link SuggestionStripView} dismisses {@link MoreSuggestionsView} when it receives those * {@link SuggestionStripView} dismisses {@link MoreSuggestionsView} when it receives any event
* events. * outside of it.
*/ */
private static class MoreSuggestionsViewCanceler private static class MoreSuggestionsViewCanceler
extends MotionEventForwarder<MainKeyboardView, SuggestionStripView> { extends MotionEventForwarder<MainKeyboardView, SuggestionStripView> {