我正在使用React Native编写应用程序。它有一个ListView
来显示项目列表。
当有新的项目可用时,我会在底部添加新项目。
我想在ListView
中滚动到底部以自动显示新项目。我怎么能这样做?
答案 0 :(得分:10)
从React Native 0.41开始,ListView
和ScrollView
都有scrollToEnd()
个方法。 ListView
记录在https://facebook.github.io/react-native/docs/listview.html#scrolltoend
在呈现时,您需要使用ref
存储对ListView
的引用:
<ListView
dataSource={yourDataSource}
renderRow={yourRenderingCallback}
ref={listView => { this.listView = listView; }}
</ListView>
然后,您只需拨打this.listView.scrollToEnd()
即可滚动到列表底部。如果您希望每次ListView
的内容发生更改(例如,添加内容时),请在ListView
的{{3}}道具回调中执行此操作,就像ScrollView
一样。
答案 1 :(得分:8)
我为 ScrollView
解决了这个问题。这是一个简单的例子:
class MessageList extends Component {&#xA; componentDidUpdate(){&#xA; let innerScrollView = this._scrollView.refs.InnerScrollView;&#xA;让scrollView = this._scrollView.refs.ScrollView;&#xA;&#xA; requestAnimationFrame(()=&gt; {&#xA; innerScrollView.measure((innerScrollViewX,innerScrollViewY,innerScrollViewWidth,innerScrollViewHeight)=&gt; {&#xA; scrollView.measure((scrollViewX,scrollViewY,scrollViewWidth,scrollViewHeight)=&gt; { &#xA; var scrollTo = innerScrollViewHeight - scrollViewHeight + innerScrollViewY;&#xA;&#xA; if(innerScrollViewHeight&lt; scrollViewHeight){&#xA; return;&#xA;}&#xA;&#xA; this。 _scrollView.scrollTo(scrollTo);&#xA;});&#xA;});&#xA;});&#xA; }&#XA;&#XA; render(){&#xA; return(&#xA;&lt; ScrollView ref = {component =&gt; this._scrollView = component}&gt;&#xA; {this.props.messages.map((message,i)=&gt; {&#xA;返回&lt;文字键= {i}&gt; {message}&lt; / Text&gt ;;&#xA;})}&#xA;&lt; / ScrollView&gt;&#xA;);&#xA; }&#xA;}&#xA;
&#xA;&#xA; 感谢 @ ccheever
&#XA;答案 2 :(得分:7)
我遇到了同样的问题,并提出了这个解决方案:
render()
{
if("listHeight" in this.state &&
"footerY" in this.state &&
this.state.footerY > this.state.listHeight)
{
var scrollDistance = this.state.listHeight - this.state.footerY;
this.refs.list.getScrollResponder().scrollTo(-scrollDistance);
}
return (
<ListView ref="list"
onLayout={(event) => {
var layout = event.nativeEvent.layout;
this.setState({
listHeight : layout.height
});
}}
renderFooter={() => {
return <View onLayout={(event)=>{
var layout = event.nativeEvent.layout;
this.setState({
footerY : layout.y
});
}}></View>
}}
/>
)
}
基本上,我渲染一个空页脚以确定列表底部的Y偏移量。从这里我可以根据列表容器高度派生到底部的滚动偏移。
注意:最后一个if条件检查内容长度是否超出列表高度,如果是,则仅滚动。您是否需要这个取决于您的设计!
希望这个解决方案可以帮助其他人处于相同的位置。
FWIW我不喜欢另一个答案中讨论的InvertibleScrollView插件,因为它对整个列表和每个列表项进行了比例转换......听起来很贵!
答案 3 :(得分:7)
这个问题有一个非常简单的解决方案。 您可以将ListView包装在Scrollview组件中。 这将提供确定列表底部位置的所有必要方法。
首先包装listView
<ScrollView>
<MyListViewElement />
</ScrollView>
然后使用返回组件(scrollView)高度的onLayout方法。并将其保存到州。
// add this method to the scrollView component
onLayout={ (e) => {
// get the component measurements from the callbacks event
const height = e.nativeEvent.layout.height
// save the height of the scrollView component to the state
this.setState({scrollViewHeight: height })
}}
然后使用返回内部组件(listView)高度的onContentSizeChange方法。并将其保存到州。每次从列表中添加或删除元素或更改高度时都会发生这种情况。基本上每当有人向您的列表添加新消息时。
onContentSizeChange={ (contentWidth, contentHeight) => {
// save the height of the content to the state when there’s a change to the list
// this will trigger both on layout and any general change in height
this.setState({listHeight: contentHeight })
}}
要滚动,您需要在ScrollView中找到scrollTo方法。您可以通过将其保存到状态ref来访问它。
<ScrollView
ref={ (component) => this._scrollView = component }
…
>
</ScrollView>
现在,您拥有计算所需的一切,并触发滚动到列表底部。您可以选择在组件中的任何位置执行此操作,我会将其添加到componentDidUpdate()
,因此无论何时呈现组件,它都会scrollTo在底部。
componentDidUpdate(){
// calculate the bottom
const bottomOfList = this.state.listHeight - this.state.scrollViewHeight
// tell the scrollView component to scroll to it
this.scrollView.scrollTo({ y: bottomOfList })
}
就是这样。 这就是你的ScrollView最终应该是什么样子
<ScrollView
ref={ (component) => this._scrollView = component }
onContentSizeChange={ (contentWidth, contentHeight) => {
this.setState({listHeight: contentHeight })
}}
onLayout={ (e) => {
const height = e.nativeEvent.layout.heigh
this.setState({scrollViewHeight: height })
}}
>
<MyListViewElement />
</ScrollView>
一种简单的方式 我使用ListView并发现使用scrollView更容易做到这一点,为了简单起见,我推荐它。 这是我的消息模块的直接副本,用于滚动到底部功能。希望能帮助到你。
class Messages extends Component {
constructor(props){
super(props)
this.state = {
listHeight: 0,
scrollViewHeight: 0
}
}
componentDidUpdate(){
this.scrollToBottom()
}
scrollToBottom(){
const bottomOfList = this.state.listHeight - this.state.scrollViewHeight
console.log('scrollToBottom');
this.scrollView.scrollTo({ y: bottomOfList })
}
renderRow(message, index){
return (
<Message
key={message.id}
{...message}
/>
);
}
render(){
return(
<ScrollView
keyboardDismissMode="on-drag"
onContentSizeChange={ (contentWidth, contentHeight) => {
this.setState({listHeight: contentHeight })
}}
onLayout={ (e) => {
const height = e.nativeEvent.layout.height
this.setState({scrollViewHeight: height })
}}
ref={ (ref) => this.scrollView = ref }>
{this.props.messages.map( message => this.renderRow(message) )}
</ScrollView>
)
}
}
export default Messages
答案 4 :(得分:3)
请检查一下: https://github.com/650Industries/react-native-invertible-scroll-view
此组件反转原始滚动视图。因此,当新物品到达时,它会自动滚动到底部。
但请注意两件事
您的“数据阵列”也需要还原。也就是说,它应该是
[new_item, old_item]
任何新到货的项目都应插入最前面。
虽然他们在自述文件示例中使用ListView
,但在将此插件与ListView
一起使用时仍存在一些缺陷。相反,我建议你只使用ScrollView
,它效果很好。
倒置滚动视图的示例:
var MessageList = React.createClass({
propTypes: {
messages: React.PropTypes.array,
},
renderRow(message) {
return <Text>{message.sender.username} : {message.content}</Text>;
},
render() {
return (
<InvertibleScrollView
onScroll={(e) => console.log(e.nativeEvent.contentOffset.y)}
scrollEventThrottle={200}
renderScrollView={
(props) => <InvertibleScrollView {...props} inverted />
}>
{_.map(this.props.messages, this.renderRow)}
</InvertibleScrollView>
);
}
});
答案 5 :(得分:1)
我担心这不是一个超级干净的方法。因此,我们将手动完成,不要担心它的简单性和不凌乱。
第1步:在您的州
中声明这些变量constructor(props) {
super(props);
this.state={
lastRowY:0,
}
}
第2步:创建scrollToBottom函数,如下所示:
scrollToBottom(){
if(!!this.state.lastRowY){
let scrollResponder = this.refs.commentList.getScrollResponder();
scrollResponder.scrollResponderScrollTo({x: 0, y: this.state.lastRowY, animated: true});
}
}
第3步:将以下属性添加到ListView
:
ref
可以访问它(在此示例中为commentList
)
ref="commentList"
您的行元素上的renderRow中的以下onLayout
函数:
onLayout={(event) => {
var {y} = event.nativeEvent.layout;
this.setState({
lastRowY : y
});
您的ListView
应该是这样的:
<ListView
ref="commentList"
style={styles.commentsContainerList}
dataSource={this.state.commentDataSource}
renderRow={()=>(
<View
onLayout={(event)=>{
let {y} = event.nativeEvent.layout;
this.setState({
lastRowY : y
});
}}
/>
</View>
)}
/>
第4步:然后在您的代码中的任意位置,只需致电this.scrollToBottom();
。
享受..
答案 6 :(得分:0)
对于那些只想用ListView实现它的人
// initialize necessary variables
componentWillMount() {
// initialize variables for ListView bottom focus
this._content_height = 0;
this._view_height = 0;
}
render() {
return (
<ListView
...other props
onLayout = {ev => this._scrollViewHeight = ev.nativeEvent.layout.height}
onContentSizeChange = {(width, height)=>{
this._scrollContentHeight = height;
this._focusOnBottom();
}}
renderScrollComponent = {(props) =>
<ScrollView
ref = {component => this._scroll_view = component}
onLayout = {props.onLayout}
onContentSizeChange = {props.onContentSizeChange} /> } />
);
}
_focusOnLastMessage() {
const bottom_offset = this._content_height - this._view_height;
if (bottom_offset > 0 &&)
this._scroll_view.scrollTo({x:0, y:scrollBottomOffsetY, false});
}
您可以在任何地方使用_focusOnLastMessage函数,例如,每当内容大小发生变化时我都会使用它。我用react-native@0.32
测试了代码答案 7 :(得分:0)
因此,为了自动滚动到ListView的底部,您应该将prop'onContentSizeChange'添加到ListView并从其参数中获取相应的内容,如下所示:
<ListView
ref='listView'
onContentSizeChange={(contentWidth, contentHeight) => {
this.scrollTo(contentHeight);
}}
...
/>
所以对于我的情况,我应该垂直渲染我的列表,这就是我使用contentHeight的原因,如果是横向列表,你只需要使用contentWeight。
这里scrollTo函数应该是:
scrollTo = (y) => {
if (y<deviceHeight-120) {
this.refs.listView.scrollTo({ y: 0, animated: true })
}else{
let bottomSpacing = 180;
if (this.state.messages.length > 0) {
bottomSpacing = 120;
}
this.refs.listView.scrollTo({ y: y - deviceHeight + bottomSpacing, animated: true })
}
}
就是这样。我希望我的这个解释可以帮助别人节省时间。
答案 8 :(得分:0)
从@jonasb扩展 在下面尝试以下代码,使用onContentSizeChange事件执行scrollToEnd方法
<ListView
ref={ ( ref ) => this.scrollView = ref }
dataSource={yourDataSource}
renderRow={yourRenderingCallback}
onContentSizeChange={ () => {
this.scrollView.scrollToEnd( { animated: true })
}}
</ListView>
答案 9 :(得分:0)
我结合了对我有用的最简单的解决方案。 这样,ScrollView不仅会随着内容变化而滚动到底部,也会随着高度变化而滚动到底部。仅在ios上测试过。
<ScrollView
ref="scrollView"
onContentSizeChange={(width, height) =>
this.refs.scrollView.scrollTo({ y: height })
}
onLayout={e => {
const height = e.nativeEvent.layout.height;
this.refs.scrollView.scrollTo({ y: height });
}
}
>
......
</ScrollView>
“ react-native”:“ 0.59.10”,
答案 10 :(得分:-1)
这是我在React Native 0.14中使用的内容。只要以下情况,此ListView包装器就会滚动到底部:
它反映了大多数聊天应用程序的标准行为。
此实现取决于RN0.14 ListView的实现细节,因此可能需要进行调整以与未来的React Native版本兼容
var React = require('react-native');
var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');
var RCTUIManager = require('NativeModules').UIManager;
var {
ListView,
} = React;
export default class AutoScrollListView extends React.Component {
componentWillMount() {
this._subscribableSubscriptions = [];
}
componentDidMount() {
this._addListenerOn(RCTDeviceEventEmitter, 'keyboardWillShow', this._onKeyboardWillShow);
this._addListenerOn(RCTDeviceEventEmitter, 'keyboardWillHide', this._onKeyboardWillHide);
var originalSetScrollContentLength = this.refs.listView._setScrollContentLength;
var originalSetScrollVisibleLength = this.refs.listView._setScrollVisibleLength;
this.refs.listView._setScrollContentLength = (left, top, width, height) => {
originalSetScrollContentLength(left, top, width, height);
this._scrollToBottomIfContentHasChanged();
};
this.refs.listView._setScrollVisibleLength = (left, top, width, height) => {
originalSetScrollVisibleLength(left, top, width, height);
this._scrollToBottomIfContentHasChanged();
};
}
componentWillUnmount() {
this._subscribableSubscriptions.forEach(
(subscription) => subscription.remove()
);
this._subscribableSubscriptions = null;
}
render() {
return
}
_addListenerOn = (eventEmitter, eventType, listener, context) => {
this._subscribableSubscriptions.push(
eventEmitter.addListener(eventType, listener, context)
);
}
_onKeyboardWillShow = (e) => {
var animationDuration = e.duration;
setTimeout(this._forceRecalculationOfLayout, animationDuration);
}
_onKeyboardWillHide = (e) => {
var animationDuration = e.duration;
setTimeout(this._forceRecalculationOfLayout, animationDuration);
}
_forceRecalculationOfLayout = () => {
requestAnimationFrame(() => {
var scrollComponent = this.refs.listView.getScrollResponder();
if (!scrollComponent || !scrollComponent.getInnerViewNode) {
return;
}
RCTUIManager.measureLayout(
scrollComponent.getInnerViewNode(),
React.findNodeHandle(scrollComponent),
() => {}, //Swallow error
this.refs.listView._setScrollContentLength
);
RCTUIManager.measureLayoutRelativeToParent(
React.findNodeHandle(scrollComponent),
() => {}, //Swallow error
this.refs.listView._setScrollVisibleLength
);
});
}
_scrollToBottomIfContentHasChanged = () => {
var scrollProperties = this.refs.listView.scrollProperties;
var hasContentLengthChanged = scrollProperties.contentLength !== this.previousContentLength;
var hasVisibleLengthChanged = scrollProperties.visibleLength !== this.previousVisibleLength;
this.previousContentLength = scrollProperties.contentLength;
this.previousVisibleLength = scrollProperties.visibleLength;
if(!hasContentLengthChanged && !hasVisibleLengthChanged) {
return;
}
this.scrollToBottom();
}
scrollToBottom = () => {
var scrollProperties = this.refs.listView.scrollProperties;
var scrollOffset = scrollProperties.contentLength - scrollProperties.visibleLength;
requestAnimationFrame(() => {
this.refs.listView.getScrollResponder().scrollTo(scrollOffset);
});
}
}
答案 11 :(得分:-1)
这是另一种变化。当有人评论时,我个人使用它滚动到列表视图的底部。我更喜欢其他例子,因为它更简洁。
listViewHeight可以通过各种方式确定,但我个人从动画中获取它,用于动画列表视图高度以避开键盘。
DataGridView
答案 12 :(得分:-1)
最喜欢@ComethTheNerd的解决方案,这是一个更常规的(通过所有airbnb的ESlinting规则)版本:
state={
listHeight: 0,
footerY: 0,
}
// dummy footer to ascertain the Y offset of list bottom
renderFooter = () => (
<View
onLayout={(event) => {
this.setState({ footerY: event.nativeEvent.layout.y });
}}
/>
);
...
<ListView
ref={(listView) => { this.msgListView = listView; }}
renderFooter={this.renderFooter}
onLayout={(event) => {
this.setState({ listHeight: event.nativeEvent.layout.height });
}}
/>
并调用scrollToBottom
方法,如下所示:
componentDidUpdate(prevProps, prevState) {
if (this.state.listHeight && this.state.footerY &&
this.state.footerY > this.state.listHeight) {
// if content is longer than list, scroll to bottom
this.scrollToBottom();
}
}
scrollToBottom = () => {
const scrollDistance = this.state.footerY - this.state.listHeight;
this.msgListView.scrollTo({ y: scrollDistance, animated: true });
}