-
Notifications
You must be signed in to change notification settings - Fork 24.7k
Implement TextInput onContentSizeChange #8457
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
589f1e1
4da65ea
1e1b928
44004de
a7f28dd
ee6dcf6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -76,18 +76,21 @@ var TextEventsExample = React.createClass({ | |
class AutoExpandingTextInput extends React.Component { | ||
constructor(props) { | ||
super(props); | ||
this.state = {text: '', height: 0}; | ||
this.state = { | ||
text: 'React Native enables you to build world-class application experiences on native platforms using a consistent developer experience based on JavaScript and React. The focus of React Native is on developer efficiency across all the platforms you care about — learn once, write anywhere. Facebook uses React Native in multiple production apps and will continue investing in React Native.', | ||
height: 0, | ||
}; | ||
} | ||
render() { | ||
return ( | ||
<TextInput | ||
{...this.props} | ||
multiline={true} | ||
onChange={(event) => { | ||
this.setState({ | ||
text: event.nativeEvent.text, | ||
height: event.nativeEvent.contentSize.height, | ||
}); | ||
onContentSizeChange={(event) => { | ||
this.setState({height: event.nativeEvent.contentSize.height}); | ||
}} | ||
onChangeText={(text) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe leave this until we've implemented There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
this.setState({text}); | ||
}} | ||
style={[styles.default, {height: Math.max(35, this.state.height)}]} | ||
value={this.state.text} | ||
|
@@ -412,19 +415,19 @@ exports.examples = [ | |
render: function() { | ||
return ( | ||
<View> | ||
<TextInput | ||
<TextInput | ||
style={[styles.singleLine, {fontFamily: 'sans-serif'}]} | ||
placeholder="Custom fonts like Sans-Serif are supported" | ||
/> | ||
<TextInput | ||
<TextInput | ||
style={[styles.singleLine, {fontFamily: 'sans-serif', fontWeight: 'bold'}]} | ||
placeholder="Sans-Serif bold" | ||
/> | ||
<TextInput | ||
<TextInput | ||
style={[styles.singleLine, {fontFamily: 'sans-serif', fontStyle: 'italic'}]} | ||
placeholder="Sans-Serif italic" | ||
/> | ||
<TextInput | ||
<TextInput | ||
style={[styles.singleLine, {fontFamily: 'serif'}]} | ||
placeholder="Serif" | ||
/> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -102,18 +102,21 @@ class AutoExpandingTextInput extends React.Component { | |
|
||
constructor(props) { | ||
super(props); | ||
this.state = {text: '', height: 0}; | ||
this.state = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here about FlowType: Should FlowType State type be added for this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is not needed as flow can infer the types from the object literal. |
||
text: 'React Native enables you to build world-class application experiences on native platforms using a consistent developer experience based on JavaScript and React. The focus of React Native is on developer efficiency across all the platforms you care about — learn once, write anywhere. Facebook uses React Native in multiple production apps and will continue investing in React Native.', | ||
height: 0, | ||
}; | ||
} | ||
render() { | ||
return ( | ||
<TextInput | ||
{...this.props} | ||
multiline={true} | ||
onChange={(event) => { | ||
this.setState({ | ||
text: event.nativeEvent.text, | ||
height: event.nativeEvent.contentSize.height, | ||
}); | ||
onChangeText={(text) => { | ||
this.setState({text}); | ||
}} | ||
onContentSizeChange={(event) => { | ||
this.setState({height: event.nativeEvent.contentSize.height}); | ||
}} | ||
style={[styles.default, {height: Math.max(35, this.state.height)}]} | ||
value={this.state.text} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -85,9 +85,6 @@ public void scrollTo( | |
Map getExportedCustomDirectEventTypeConstants() { | ||
return MapBuilder.builder() | ||
.put(ScrollEventType.SCROLL.getJSEventName(), MapBuilder.of("registrationName", "onScroll")) | ||
.put( | ||
ContentSizeChangeEvent.EVENT_NAME, | ||
MapBuilder.of("registrationName", "onContentSizeChange")) | ||
.build(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can't get rid of this. We use this internally in apps. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The event was moved here https://github.com/facebook/react-native/pull/8457/files#diff-0404c7801b7b68d8a58a11f338c5d666R74 since onContentSizeChange is now used by scollview and textinput. Is there a difference between doing it in the manager vs in uimanager that I'm not aware of? |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
/** | ||
* 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; | ||
|
||
public interface ContentSizeWatcher { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The coding convention is Listener instead of Watcher I think There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess we could rename both to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually the name is probably based on |
||
public void onLayout(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 content size changes. | ||
*/ | ||
public class ReactContentSizeChangedEvent extends Event<ReactTextChangedEvent> { | ||
|
||
public static final String EVENT_NAME = "topContentSizeChange"; | ||
|
||
private int mContentWidth; | ||
private int mContentHeight; | ||
|
||
public ReactContentSizeChangedEvent( | ||
int viewId, | ||
long timestampMs, | ||
int contentSizeWidth, | ||
int contentSizeHeight) { | ||
super(viewId, timestampMs); | ||
mContentWidth = contentSizeWidth; | ||
mContentHeight = contentSizeHeight; | ||
} | ||
|
||
@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 contentSize = Arguments.createMap(); | ||
contentSize.putDouble("width", mContentWidth); | ||
contentSize.putDouble("height", mContentHeight); | ||
eventData.putMap("contentSize", contentSize); | ||
|
||
eventData.putInt("target", getViewTag()); | ||
return eventData; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -71,6 +71,7 @@ public class ReactEditText extends EditText { | |
private boolean mContainsImages; | ||
private boolean mBlurOnSubmit; | ||
private @Nullable SelectionWatcher mSelectionWatcher; | ||
private @Nullable ContentSizeWatcher mContentSizeWatcher; | ||
private final InternalKeyListener mKeyListener; | ||
|
||
private static final KeyListener sKeyListener = QwertyKeyListener.getInstanceForFullKeyboard(); | ||
|
@@ -102,15 +103,30 @@ public ReactEditText(Context context) { | |
// TODO: t6408636 verify if we should schedule a layout after a View does a requestLayout() | ||
@Override | ||
public boolean isLayoutRequested() { | ||
return false; | ||
// If we are watching and updating container height based on content size | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you return |
||
// then we don't want to scroll right away. This isn't perfect -- you might | ||
// want to limit the height the text input can grow to. Possible solution | ||
// is to add another prop that determines whether we should scroll to end | ||
// of text. | ||
if (mContentSizeWatcher != null) { | ||
return isMultiline(); | ||
} else { | ||
return false; | ||
} | ||
} | ||
|
||
@Override | ||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) { | ||
if (mContentSizeWatcher != null) { | ||
mContentSizeWatcher.onLayout(); | ||
} | ||
} | ||
|
||
// 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 | ||
public boolean onKeyUp(int keyCode, KeyEvent event) { | ||
if (keyCode == KeyEvent.KEYCODE_ENTER && | ||
((getInputType() & InputType.TYPE_TEXT_FLAG_MULTI_LINE) == 0 )) { | ||
if (keyCode == KeyEvent.KEYCODE_ENTER && !isMultiline()) { | ||
hideSoftKeyboard(); | ||
return true; | ||
} | ||
|
@@ -162,6 +178,10 @@ public void removeTextChangedListener(TextWatcher watcher) { | |
} | ||
} | ||
|
||
public void setContentSizeWatcher(ContentSizeWatcher contentSizeWatcher) { | ||
mContentSizeWatcher = contentSizeWatcher; | ||
} | ||
|
||
@Override | ||
protected void onSelectionChanged(int selStart, int selEnd) { | ||
super.onSelectionChanged(selStart, selEnd); | ||
|
@@ -212,7 +232,7 @@ public void setInputType(int type) { | |
mStagedInputType = type; | ||
// Input type password defaults to monospace font, so we need to re-apply the font | ||
super.setTypeface(tf); | ||
|
||
// We override the KeyListener so that all keys on the soft input keyboard as well as hardware | ||
// keyboards work. Some KeyListeners like DigitsKeyListener will display the keyboard but not | ||
// accept all input from it | ||
|
@@ -329,6 +349,10 @@ private TextWatcherDelegator getTextWatcherDelegator() { | |
return mTextWatcherDelegator; | ||
} | ||
|
||
private boolean isMultiline() { | ||
return (getInputType() & InputType.TYPE_TEXT_FLAG_MULTI_LINE) != 0; | ||
} | ||
|
||
/* package */ void setGravityHorizontal(int gravityHorizontal) { | ||
if (gravityHorizontal == 0) { | ||
gravityHorizontal = mDefaultGravityHorizontal; | ||
|
@@ -447,7 +471,7 @@ public void onTextChanged(CharSequence s, int start, int before, int count) { | |
@Override | ||
public void afterTextChanged(Editable s) { | ||
if (!mIsSettingTextFromJS && mListeners != null) { | ||
for (android.text.TextWatcher listener : mListeners) { | ||
for (TextWatcher listener : mListeners) { | ||
listener.afterTextChanged(s); | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should FlowType State type be added for this?