巧用Scrollview实现Tab组件动态切换
实现效果:
不仅能左右滑动,同时还能够在点击的时候自动滑动,将点击的位置滑动到正中间。
代码实现
import React from 'react'
import { Dimensions, ScrollView, Text, TouchableOpacity, View, StyleSheet } from 'react-native'
import LinearGradient from 'react-native-linear-gradient'
const { width } = Dimensions.get('window');
const deviceWidth = width;
const tabs = ['北京', '上海', '深圳', '呼和浩特', '南京', '哈尔滨', '乌鲁木齐', '广州'];
⚠️该类组件不能继承PureComponent,当继承PureComponent时可能导致进入时第一个选项不会显示下面的渐变线
export default class ScrollTab extends React.Component {
constructor(props) {
super(props);
this.state = {
curIndex: 0,
linearGradientWidth: []
}
this.scroll = null;
this.laout_list = [];
this.scrollW = 0;
}
render() {
return (
<ScrollView
ref={e => this.scroll = e}
horizontal
directionalLockEnabled
showsHorizontalScrollIndicator={false}
// snapToAlignment会定义停驻点与滚动视图之间的关系
// start (默认) 会将停驻点对齐在左侧(水平)或顶部(垂直)
// center 会将停驻点对齐到中间
// end 会将停驻点对齐到右侧(水平)或底部(垂直)
snapToAlignment="center"
>
{/* 具体需要展示的样式 */}
{
tabs.map((item, index) => {
return (
<TouchableOpacity
onPress={() => this.setIndex(index)}
// onLayout属性获取当前组件的位置和属性信息{width,height,x,y}
onLayout={e => this.setlaout(e.nativeEvent.layout, index)}
key={item}
style={styles.tabItem}
>
<View>
<Text style={styles.tabWord} onLayout={({ nativeEvent: e }) => this.handleTextLength({ e, index })}>{item}</Text>
{this.linearTab(index)}
</View>
</TouchableOpacity>
)
})
}
</ScrollView>
)
}
setlaout(layout, index) {
this.laout_list[index] = layout;
// 统计总宽度
this.scrollW += layout.width;
}
// 核心: 需要滑动的位置=点击位置的左边距-APP屏幕/2+点击位置的宽度/2
setIndex(index, bl = true) {
if (this.state.curIndex !== index) {
this.setState({
curIndex: index
})
}
if (!this.scroll) return;
let layout = this.laout_list[index];
let rx = deviceWidth / 2;
let sx = layout.x - rx + layout.width / 2;
// 不满足滑动条件,停留不动
if (sx < 0) sx = 0;
// 正常滑动
sx < this.scrollW - deviceWidth && this.scroll.scrollTo({ x: sx, animated: bl });
// 靠后部分则直接滑到底部
sx >= this.scrollW - deviceWidth && this.scroll.scrollToEnd({ animated: bl });
}
// 设置文字下面的线的长度
handleTextLength = ({ e, index }) => {
const { linearGradientWidth } = this.state;
linearGradientWidth[index] = e.layout.width;
this.setState({
linearGradientWidth
})
}
linearTab = (index) => {
const { curIndex, linearGradientWidth } = this.state;
if (curIndex == index) {
return (
<View style={styles.linearGradient}>
<LinearGradient
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 0 }}
colors={['skyblue', 'blue']}
style={{
height: 10,
width: linearGradientWidth[index]
}}
/>
</View>
)
}
return null
}
const styles = StyleSheet.create({
tabItem: {
marginRight: 10
},
tabWord: {
color: ' #333',
fontSize: 24,
paddingVertical: 10
},
linearGradient: {
position: 'absolute',
left: 0,
bottom: -1,
borderTopLeftRadius: 4,
borderTopRightRadius: 4
}
})