Add onSelectionChange for Android TextInput

Summary:
public

Add an onSelectionChange method to TextInput that works on Android same as iOS

Reviewed By: andreicoman11

Differential Revision: D2780131

fb-gh-sync-id: 9b3b8fbd9ea653d43e3107a338e4bc08bde2e8c6
This commit is contained in:
Dave Miller 2016-01-07 07:36:51 -08:00 committed by facebook-github-bot-3
parent 857dd59340
commit 6dc6794881
5 changed files with 160 additions and 1 deletions

View File

@ -216,6 +216,10 @@ var TextInput = React.createClass({
* Callback that is called when text input ends.
*/
onEndEditing: PropTypes.func,
/**
* Callback that is called when the text input selection is changed
*/
onSelectionChange: PropTypes.func,
/**
* Callback that is called when the text input's submit button is pressed.
* Invalid if multiline={true} is specified.
@ -474,6 +478,17 @@ var TextInput = React.createClass({
},
_renderAndroid: function() {
var onSelectionChange;
if (this.props.selectionState || this.props.onSelectionChange) {
onSelectionChange = (event: Event) => {
if (this.props.selectionState) {
var selection = event.nativeEvent.selection;
this.props.selectionState.update(selection.start, selection.end);
}
this.props.onSelectionChange && this.props.onSelectionChange(event);
};
}
var autoCapitalize = UIManager.UIText.AutocapitalizationType[this.props.autoCapitalize];
var textAlign =
UIManager.AndroidTextInput.Constants.TextAlign[this.props.textAlign];
@ -489,6 +504,7 @@ var TextInput = React.createClass({
if (childCount > 1) {
children = <Text>{children}</Text>;
}
var textContainer =
<AndroidTextInput
ref="input"
@ -505,6 +521,7 @@ var TextInput = React.createClass({
onFocus={this._onFocus}
onBlur={this._onBlur}
onChange={this._onChange}
onSelectionChange={onSelectionChange}
onTextInput={this._onTextInput}
onEndEditing={this.props.onEndEditing}
onSubmitEditing={this.props.onSubmitEditing}

View File

@ -64,6 +64,7 @@ public class ReactEditText extends EditText {
private @Nullable TextWatcherDelegator mTextWatcherDelegator;
private int mStagedInputType;
private boolean mContainsImages;
private @Nullable SelectionWatcher mSelectionWatcher;
public ReactEditText(Context context) {
super(context);
@ -150,6 +151,27 @@ public class ReactEditText extends EditText {
}
}
@Override
protected void onSelectionChanged(int selStart, int selEnd) {
super.onSelectionChanged(selStart, selEnd);
if (mSelectionWatcher != null && hasFocus()) {
mSelectionWatcher.onSelectionChanged(selStart, selEnd);
}
}
@Override
protected void onFocusChanged(
boolean focused, int direction, Rect previouslyFocusedRect) {
super.onFocusChanged(focused, direction, previouslyFocusedRect);
if (focused && mSelectionWatcher != null) {
mSelectionWatcher.onSelectionChanged(getSelectionStart(), getSelectionEnd());
}
}
public void setSelectionWatcher(SelectionWatcher selectionWatcher) {
mSelectionWatcher = selectionWatcher;
}
/*protected*/ int getStagedInputType() {
return mStagedInputType;
}
@ -170,7 +192,8 @@ public class ReactEditText extends EditText {
mStagedInputType = type;
}
/* package */ void requestFocusFromJS() {
// VisibleForTesting from {@link TextInputEventsTestCase}.
public void requestFocusFromJS() {
mIsJSSettingFocus = true;
requestFocus();
mIsJSSettingFocus = false;

View File

@ -166,6 +166,15 @@ public class ReactTextInputManager extends
(int) Math.ceil(PixelUtil.toPixelFromSP(fontSize)));
}
@ReactProp(name = "onSelectionChange", defaultBoolean = false)
public void setOnSelectionChange(final ReactEditText view, boolean onSelectionChange) {
if (onSelectionChange) {
view.setSelectionWatcher(new ReactSelectionWatcher(view));
} else {
view.setSelectionWatcher(null);
}
}
@ReactProp(name = "placeholder")
public void setPlaceholder(ReactEditText view, @Nullable String placeholder) {
view.setHint(placeholder);
@ -418,6 +427,40 @@ public class ReactTextInputManager extends
});
}
private class ReactSelectionWatcher implements SelectionWatcher {
private ReactEditText mReactEditText;
private EventDispatcher mEventDispatcher;
private int mPreviousSelectionStart;
private int mPreviousSelectionEnd;
public ReactSelectionWatcher(ReactEditText editText) {
mReactEditText = editText;
ReactContext reactContext = (ReactContext) editText.getContext();
mEventDispatcher = reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher();
}
@Override
public void onSelectionChanged(int start, int end) {
// Android will call us back for both the SELECTION_START span and SELECTION_END span in text
// To prevent double calling back into js we cache the result of the previous call and only
// forward it on if we have new values
if (mPreviousSelectionStart != start || mPreviousSelectionEnd != end) {
mEventDispatcher.dispatchEvent(
new ReactTextInputSelectionEvent(
mReactEditText.getId(),
SystemClock.uptimeMillis(),
start,
end
)
);
mPreviousSelectionStart = start;
mPreviousSelectionEnd = end;
}
}
}
@Override
public @Nullable Map getExportedViewConstants() {
return MapBuilder.of(

View File

@ -0,0 +1,58 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.facebook.react.views.textinput;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.uimanager.events.Event;
import com.facebook.react.uimanager.events.RCTEventEmitter;
/**
* Event emitted by EditText native view when the text selection changes.
*/
/* package */ class ReactTextInputSelectionEvent
extends Event<ReactTextInputSelectionEvent> {
private static final String EVENT_NAME = "topSelectionChange";
private int mSelectionStart;
private int mSelectionEnd;
public ReactTextInputSelectionEvent(
int viewId,
long timestampMs,
int selectionStart,
int selectionEnd) {
super(viewId, timestampMs);
mSelectionStart = selectionStart;
mSelectionEnd = selectionEnd;
}
@Override
public String getEventName() {
return EVENT_NAME;
}
@Override
public void dispatch(RCTEventEmitter rctEventEmitter) {
rctEventEmitter.receiveEvent(getViewTag(), getEventName(), serializeEventData());
}
private WritableMap serializeEventData() {
WritableMap eventData = Arguments.createMap();
WritableMap selectionData = Arguments.createMap();
selectionData.putInt("start", mSelectionStart);
selectionData.putInt("end", mSelectionEnd);
eventData.putMap("selection", selectionData);
return eventData;
}
}

View File

@ -0,0 +1,18 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.facebook.react.views.textinput;
/**
* Implement this interface to be informed of selection changes in the ReactTextEdit
* This is used by the ReactTextInputManager to forward events from the EditText to JS
*/
interface SelectionWatcher {
public void onSelectionChanged(int start, int end);
}