React Native - как прокрутить ScrollView в заданное место после перехода с другого экрана
Можно ли сказать ScrollView для прокрутки до определенной позиции, когда мы только что перешли на текущий экран через StackNavigator?
У меня два экрана; Меню и предметы. Меню представляет собой список кнопок, по одной для каждого элемента. Экран Предметов содержит Карусель, построенную с использованием ScrollView с изображением и подробным описанием каждого Предмета.
Когда я нажимаю кнопку на экране меню, я хочу перейти к экрану "Элементы" и автоматически перейти к элементу, который представляет кнопка.
Я прочитал, что вы можете передавать параметры при использовании StackNavigator следующим образом: но я не знаю, как считать этот параметр на моем экране Items.
navigate('Items', { id: '1' })
Так возможно ли это в React Native и как мне это сделать? Или, возможно, я использую не тот навигатор?
Вот глупая версия моих двух экранов:
App.js:
const SimpleApp = StackNavigator({
Menu: { screen: MenuScreen},
Items: { screen: ItemScreen }
}
);
export default class App extends React.Component {
render() {
return <SimpleApp />;
}
}
Menu.js
export default class Menu extends React.Component {
constructor(props){
super(props)
this.seeDetail = this.seeDetail.bind(this)
}
seeDetail(){
const { navigate } = this.props.navigation;
navigate('Items')
}
render(){
<Button onPress={this.seeDetail} title='1'/>
<Button onPress={this.seeDetail} title='2'/>
}
}
Items.js
export default class Items extends React.Component {
render(){
let scrollItems = [] //Somecode that generates and array of items
return (
<View>
<View style={styles.scrollViewContainer}>
<ScrollView
horizontal
pagingEnabled
ref={(ref) => this.myScroll = ref}>
{scrollItems}
</ScrollView>
</View>
</View>
)
}
}
PS В данный момент я специально ориентируюсь на Android, но в идеале может быть кроссплатформенное решение.
2 ответа
Я прочитал, что вы можете передавать параметры при использовании StackNavigator следующим образом: но я не знаю, как считать этот параметр на моем экране Items.
Это достигается путем доступа this.props.navigation.state.params
внутри вашего дочернего компонента.
Я думаю, что лучшее время позвонить scrollTo
на вашей прокрутки ссылка, когда он впервые назначен. Вы уже даете ему ссылку и запускаете функцию обратного вызова - я бы просто настроил ее так, чтобы она также вызывала scrollTo
в то же время:
export default class Items extends React.Component {
render(){
let scrollItems = [] //Somecode that generates and array of items
const {id} = this.props.navigation.state.params;
return (
<View>
<View style={styles.scrollViewContainer}>
<ScrollView
horizontal
pagingEnabled
ref={(ref) => {
this.myScroll = ref
this.myScroll.scrollTo() // !!
}>
{scrollItems}
</ScrollView>
</View>
</View>
)
}
}
И именно поэтому я использую FlatLists или SectionLists (которые наследуются от VirtualizedList) вместо ScrollViews. VirtualizedList
имеет функцию scrollToIndex, которая гораздо более интуитивно понятна. ScrollTo ScrollView ожидает параметры x и y, означающие, что вам нужно будет рассчитать точное место для прокрутки - умножить ширину каждого элемента прокрутки на индекс элемента, на который вы прокручиваете. И если для каждого элемента задействовано заполнение, это становится более болезненным.
Вот пример прокрутки к реквизиту с идентификатором.
import React, { Component } from 'react';
import { StyleSheet, Text, View, ScrollView, TouchableOpacity, Dimensions, Alert, findNodeHandle, Image } from 'react-native';
class MyCustomScrollToElement extends Component {
constructor(props) {
super(props)
this.state = {
}
this._nodes = new Map();
}
componentDidMount() {
const data = ['First Element', 'Second Element', 'Third Element', 'Fourth Element', 'Fifth Element' ];
data.filter((el, idx) => {
if(el===this.props.id){
this.scrollToElement(idx);
}
})
}
scrollToElement =(indexOf)=>{
const node = this._nodes.get(indexOf);
const position = findNodeHandle(node);
this.myScroll.scrollTo({ x: 0, y: position, animated: true });
}
render(){
const data = ['First Element', 'Second Element', 'Third Element', 'Fourth Element', 'Fifth Element' ];
return (
<View style={styles.container}>
<ScrollView ref={(ref) => this.myScroll = ref} style={[styles.container, {flex:0.9}]} keyboardShouldPersistTaps={'handled'}>
<View style={styles.container}>
{data.map((elm, idx) => <View ref={ref => this._nodes.set(idx, ref)} style={{styles.element}}><Text>{elm}</Text></View>)}
</View>
</ScrollView>
</View>
);
}
const styles = StyleSheet.create({
container: {
flexGrow: 1,
backgroundColor:"#d7eff9"
},
element:{
width: 200,
height: 200,
backgroundColor: 'red'
}
});
export default MyCustomScrollToElement;
Да, это возможно, используя scrollTo
Метод - см. документы. Вы можете вызвать этот метод в componentDidMount
, Вам просто нужно, чтобы ссылка называлась так: this.myScroll.scrollTo(...)
, Обратите внимание, что если у вас есть массив элементов одного типа, вы должны использовать FlatList
вместо.
Для iOS - лучший способ использовать ScrollView
с contentOffset
свойство. Таким образом, он будет изначально отображаться в правильном положении. С помощьюscrollTo
добавит дополнительный лишний рендер после первого.
Для Android - другого варианта нет scrollTo