记录一个小需求的开发。react-native ScrollView点击分类的标签,标签实现滚动效果。这样需求可能很简单。然而菜逼的我 第一反应永远是:
通过以下的过程:
然后我想说:我会了!!!!
自我娱乐一下---来解决枯燥的记录过程;
效果大概是下面这样的:
代码实现:
直接上代码--代码注释都已经加好了--需要的小伙伴相信是看的懂的(注释我写的可仔细了
😏);
或者直接复制到自己项目查看效果
import React, {Component} from "react";
import {NativeModules, TouchableOpacity,ScrollView, StyleSheet, Text, View} from "react-native";
export default class ShowMoreImg extends Component {
scvTabs;
constructor(props) {
super(props)
this.state = {
spaceTabViewWidthList: [], //空间tab页宽度数组,为了处理点击自动滑动效果
selectIndex: 0 //当前选择的是第几个
}
}
render() {
return (
<View style={styles.content}>
<ScrollView
horizontal={true}
showsHorizontalScrollIndicator={false}
style={styles.scrollTab}
ref={(ref) => this.scvTabs = ref}
>
{this.renderSpaceNameItem()}
</ScrollView>
</View>
);
}
/**
* 组装数据-然后渲染
* @returns {Array}
*/
renderSpaceNameItem = () => {
let items = [];
let data = ["第1项", "第2项", "第3项", "第4项", "第5项", "第6项", "第7项", "第8项", "第9项", "第10项", "第11项", "第12项", "第13项"]
data.map((item, styleId) => {
items.push(this.renderItemWidthSpaceName(styleId, item))
})
return items
}
/**
* 渲染组件-和处理点击事件
* @returns {Array}
*/
renderItemWidthSpaceName = (position, spaceName) => {
let {selectIndex} = this.state;
return (
<TouchableOpacity key={position}
style={[styles.styleView]}
//onLayout事件会在布局计算完成后立即调用一次.可以拿到组件的位置宽高等属性
onLayout={(event) => {
//获取组件在当前屏幕上的绝对位置一般都是通measure
NativeModules.UIManager.measure(event.target, (x, y, width, height, left, top) => {
let viewInfo = {
'x': x,
'y': y,
'width': width,
'height': height,
'left': left,
'top': top,
}
//将各个组件的位置信息放置到数组当中
this.state.spaceTabViewWidthList[position] = viewInfo
})
}}
onPress={() => {
let x = 0
let y = this.state.spaceTabViewWidthList[position] && this.state.spaceTabViewWidthList[position].y
let viewLeft = 0
//遍历初始化时候位置信息的数组。获取当前组件的距离最左侧的距离
for (let i = 1; i < position; i++) {
viewLeft += this.state.spaceTabViewWidthList[i].width
}
//重新计算宽度:自己组件宽度的一半加上距离左侧的距离
let viewCenter = this.state.spaceTabViewWidthList[position].width / 2 + viewLeft
//如果当前距离 小于屏幕一般就不动。如果大于就盾冬相应的距离
if (viewCenter < 187) {
x = 0
} else {
x = viewCenter - 187
}
//利用组件引用调用滚动方法
this.scvTabs.scrollTo({
x: x,
y: y,
animated: true
})
this.setState({
selectIndex: position
})
}}
>
<Text
style={[styles.styleTitle, position === selectIndex && styles.check]}
numberOfLines={1}>{spaceName}</Text>
</TouchableOpacity>
)
}
}
const styles = StyleSheet.create({
content: {
flex: 1,
backgroundColor: "#FFFFFF"
},
scrollTab: {
width: 375,
height: 50,
},
styleView: {
width: 80,
height: 50,
backgroundColor: "white",
justifyContent: "center",
alignItems: "center",
},
check: {
color: "red",
fontWeight: "bold"
},
styleTitle: {
color: "black",
fontSize: 24,
fontWeight: "bold"
},
});
总结实现思路
- 1.首先使用scrollVIew作为滚动的容器。组件抛出ref引用--需要用到组件的scrollTo的方法(
用于触发滚动效果
) - 2.在渲染的时候获取每个子组件(标签)位于当前屏幕的位置-还有组件自身的宽高。进行存储--
用于处理滑动距离的计算
- 3.处理点击事件。点击可以获取到当前点击的是第几个组件-通过上面存储的数据得到当前组件的位置信息
- 4.处理滚动距离--得到自己的距离信息--如果小于当前屏幕一半。那就不滚动。不然就用自己的位置距离减去屏幕中间位置
这个是为了显示在中间-当然根据自己业务来定
- 5.通过scrollView组件的scrollTo方法 触发滚动 就可以了
文章记录到这边。网上可能已经有很多现成的组件、但是自己学习到了。就记录一下。继续加油~
补充:额外的补充
代码中获取组件距离使用了
NativeModules.UIManager.measure
而不是使用onLayout函数的结果。区别在于
onLayout:使用元素自身的onLayout属性去获取,但是这种方式有一个局限性,就是只有在初次渲染的时候才会触发这个函数,而且此种方法获取的是组件相对于父组件的位置坐标。如果我们需要随时都可以去获取组件的尺寸或者相对于屏幕的位置坐标时,这种方式并不适合。
NativeModules.UIManager.measure: UIManager.measure接受两个参数,第一个参数是通过findNodeHandle获取的组件节点,第二个参数是获取成功的回调,回调有6个参数:x,y表示组件的相对位置,width,height表示组件的宽度和高度,pageX,pageY表示组件相对于屏幕的绝对位置。
溜了溜了~