Smart textinput scroll

Summary:
This diff changes the textinput component to only scroll (and interrupt parent
views from scrolling), when it is possible for the text inside the component to
be scrolled. Before (D3735237), we would intercept all touch events on the
textinput if it's focused. But this makes it: a.) impossible to scroll a scrollview from within
a textinput that cannot be scrolled; b.) different from iOS behavior.

What the component now does is intercept move touches, and check if it can
scroll in any direction. If it does, it will intercept the touches and stop the parent
component from scrolling; otherwise, it will give the control back to the parent
component.

Note: this might change in the future to also detect the direction of the scroll, and
only block the scroll if the component can scroll in that direction. This is however not
trivial, since the scroll needs to be above some threshold of pixels. Blocking the
parent view from scrolling until that threshold is passed might cause incorrect behavior
in the parent component.

Reviewed By: astreet

Differential Revision: D3764267

fbshipit-source-id: 47e7b5e03855b3c85789e04fc31a8317afbafa84
This commit is contained in:
Andrei Coman 2016-09-06 09:45:25 -07:00 committed by Facebook Github Bot 0
parent 3efe95d989
commit 372d001a5d
1 changed files with 28 additions and 8 deletions

View File

@ -15,8 +15,8 @@ import java.util.ArrayList;
import android.content.Context;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.text.Editable;
import android.text.InputType;
import android.text.SpannableStringBuilder;
@ -29,6 +29,7 @@ import android.text.style.BackgroundColorSpan;
import android.text.style.ForegroundColorSpan;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
@ -68,12 +69,12 @@ public class ReactEditText extends EditText {
private @Nullable ArrayList<TextWatcher> mListeners;
private @Nullable TextWatcherDelegator mTextWatcherDelegator;
private int mStagedInputType;
private boolean mTextIsSelectable = true;
private boolean mContainsImages;
private boolean mBlurOnSubmit;
private @Nullable SelectionWatcher mSelectionWatcher;
private @Nullable ContentSizeWatcher mContentSizeWatcher;
private final InternalKeyListener mKeyListener;
private boolean mDetectScrollMovement = false;
private static final KeyListener sKeyListener = QwertyKeyListener.getInstanceForFullKeyboard();
@ -124,6 +125,31 @@ public class ReactEditText extends EditText {
}
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mDetectScrollMovement = true;
// Disallow parent views to intercept touch events, until we can detect if we should be
// capturing these touches or not.
this.getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
if (mDetectScrollMovement) {
if (!canScrollVertically(-1) &&
!canScrollVertically(1) &&
!canScrollHorizontally(-1) &&
!canScrollHorizontally(1)) {
// We cannot scroll, let parent views take care of these touches.
this.getParent().requestDisallowInterceptTouchEvent(false);
}
mDetectScrollMovement = false;
}
break;
}
return super.onTouchEvent(ev);
}
// Consume 'Enter' key events: TextView tries to give focus to the next TextInput, but it can't
// since we only allow JS to change focus, which in turn causes TextView to crash.
@Override
@ -252,12 +278,6 @@ public class ReactEditText extends EditText {
setKeyListener(mKeyListener);
}
@Override
public void setTextIsSelectable(boolean selectable) {
mTextIsSelectable = selectable;
super.setTextIsSelectable(selectable);
}
// VisibleForTesting from {@link TextInputEventsTestCase}.
public void requestFocusFromJS() {
mIsJSSettingFocus = true;