官方文档: https://github.com/Flipkart/recyclerlistview/blob/master/README.md
第一步
npm install --save recyclerlistview
or
yarn add recyclerlistview
第二步
主文件
/*
* @Author: ChenShy
* @Date: 2022-06-01 15:42:54
* @Last Modified by: ChenShy
* @Description: 标尺滑动输入组件
*/
import React, {Component} from 'react';
import {View, StyleSheet, ActivityIndicator, Dimensions, Text, Image} from 'react-native';
import {sc} from 'react-native-size-matters';
import {RecyclerListView, DataProvider} from 'recyclerlistview';
import {LayoutUtil, ViewSelector, ImageRenderer, DataCall,} from './LayoutUtil';
const {width, height} = Dimensions.get('window');
export default class SetTargetWeight extends Component {
constructor(props) {
super(props);
this.state = {
dataProvider: new DataProvider((r1, r2) => {
return r1 !== r2;
}),
layoutProvider: LayoutUtil.getLayoutProvider(4),
images: [],
count: 0,
viewType: 0,
contentOffset: 0,
};
this.inProgressNetworkReq = false;
this.recycler = null;
}
componentWillMount() {
this.fetchMoreData();
}
viewChangeHandler = viewType => {
//We will create a new layout provider which will trigger context preservation maintaining the first visible index
this.setState({
layoutProvider: LayoutUtil.getLayoutProvider(viewType),
viewType: viewType,
});
};
async fetchMoreData() {
if (!this.inProgressNetworkReq) {
//To prevent redundant fetch requests. Needed because cases of quick up/down scroll can trigger onEndReached
//more than once
this.inProgressNetworkReq = true;
// const images = await DataCall.get(this.state.count, 20);
let images = []
for (let i = 97; i < 250; i++) {
images.push(i)
}
this.inProgressNetworkReq = false;
this.setState({
dataProvider: this.state.dataProvider.cloneWithRows(
this.state.images.concat(images)
),
images: this.state.images.concat(images),
count: this.state.count + 20,
});
}
}
componentDidMount() {
setTimeout(() => {
this.recycler.scrollToIndex(100, true) // 定位到第n个
}, 200);
}
rowRenderer = (type, data, index) => {
//We have only one view type so not checks are needed here
// return <ImageRenderer imageUrl={data} />;
return <View style={{
width: sc(81),
marginTop: sc(13),
height: sc(22),
borderRightColor: '#ABD0E0',
borderRightWidth: sc(1),
flexDirection: 'row',
alignItems: 'flex-end'
}}>
{[1, 2, 3, 4, 5, 6, 7, 8, 9,].map((item, idx) => {
return <View style={{
height: sc(13),
width: sc(9),
borderLeftColor: '#ABD0E0',
borderLeftWidth: sc(idx == 0 ? 0 : 1)
}}>
{idx == 0 ? index == 0 ? null : <Text style={{
fontSize: sc(12),
color: '#999999',
width: sc(50),
textAlign: 'center',
position: 'absolute',
zIndex: 100,
bottom: sc(-17),
left: sc(-25)
}}>
{data}.{item}斤
</Text> : null}
</View>
})}
</View>
};
handleListEnd = () => {
this.fetchMoreData();
//This is necessary to ensure that activity indicator inside footer gets rendered. This is required given the implementation I have done in this sample
this.setState({});
};
renderFooter = () => {
//Second view makes sure we don't unnecessarily change height of the list on this event. That might cause indicator to remain invisible
//The empty view can be removed once you've fetched all the data
return this.inProgressNetworkReq
? <ActivityIndicator
style={{margin: 100}}
size="large"
color={'black'}
/>
: <View style={{height: 60}}/>;
};
handleScroll = (e) => {
console.log(e.nativeEvent.contentOffset.x);
this.setState({
contentOffset: (e.nativeEvent.contentOffset.x),
})
}
render() {
//Only render RLV once you have the data
return (
<View style={styles.container}>
{/* <ViewSelector
viewType={this.state.viewType}
viewChange={this.viewChangeHandler}
/> */}
{this.state.count >= 0
? <>
<View style={{
width: sc(64),
height: sc(95),
position: 'absolute',
top: 0,
zIndex: 99,
// backgroundColor: 'red',
left: (width - sc(64)) / 2,
alignItems: 'center',
borderWidth: sc(1),
borderColor: 'red'
}}>
<Text style={{
height: sc(40),
fontSize: sc(26),
textAlign: 'center',
color: '#22B49C',
width: sc(64)
}}>{this.state.contentOffset}</Text>
<Image style={{width: sc(19.6), height: sc(35), backgroundColor: 'rgba(0,0,0,.1)'}}
resizeMode={'contain'}
source={require('../../../../assets/images/common/Tape.png')}/>
</View>
<View style={{
height: sc(52),
marginTop: sc(41),
marginHorizontal: sc(10),
justifyContent: 'center'
}}>
<RecyclerListView
ref={ref => this.recycler = ref}
isHorizontal={true}
style={{flex: 1, backgroundColor: '#F6FDFF',}}
// contentContainerStyle={{ margin: 30 }}
onEndReached={this.handleListEnd}
dataProvider={this.state.dataProvider}
layoutProvider={this.state.layoutProvider}
rowRenderer={this.rowRenderer}
renderFooter={this.renderFooter}
onScroll={this.handleScroll}
renderAheadOffset={1000}
/>
</View>
</>
: null}
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
// marginTop: sc(30),
// alignItems: 'stretch',
// justifyContent: 'space-between',
},
});
配置文件LayoutUtil.js
import React from 'react';
import {Image, Platform, View, TouchableHighlight, Text} from 'react-native';
import {LayoutProvider} from 'recyclerlistview';
import {Dimensions} from 'react-native';
import {sc} from 'react-native-size-matters';
const {width, height} = Dimensions.get('window');
const isIOS = Platform.OS === 'ios';
export class LayoutUtil {
static getWindowWidth() {
// To deal with precision issues on android
return Math.round(Dimensions.get('window').width * 1000) / 1000 - 6; //Adjustment for margin given to RLV;
}
static getLayoutProvider(type) {
switch (type) {
case 0:
return new LayoutProvider(
() => {
return 'VSEL'; //Since we have just one view type
},
(type, dim, index) => {
const columnWidth = LayoutUtil.getWindowWidth() / 3;
switch (type) {
case 'VSEL':
if (index % 3 === 0) {
dim.width = 3 * columnWidth;
dim.height = 300;
} else if (index % 2 === 0) {
dim.width = 2 * columnWidth;
dim.height = 250;
} else {
dim.width = columnWidth;
dim.height = 250;
}
break;
default:
dim.width = 0;
dim.heigh = 0;
}
}
);
case 1:
return new LayoutProvider(
() => {
return 'VSEL';
},
(type, dim) => {
switch (type) {
case 'VSEL':
dim.width = LayoutUtil.getWindowWidth() / 2;
dim.height = 250;
break;
default:
dim.width = 0;
dim.heigh = 0;
}
}
);
case 4:
return new LayoutProvider(
() => {
return 'VSEL';
},
(type, dim) => {
switch (type) {
case 'VSEL':
// dim.width = LayoutUtil.getWindowWidth() / 2;
dim.width = sc(81);
dim.height = height;
break;
default:
dim.width = 0;
dim.heigh = 0;
}
}
);
case 2:
return new LayoutProvider(
() => {
return 'VSEL';
},
(type, dim) => {
switch (type) {
case 'VSEL':
dim.width = LayoutUtil.getWindowWidth();
dim.height = 200;
break;
default:
dim.width = 0;
dim.heigh = 0;
}
}
);
default:
return new LayoutProvider(
() => {
return 'VSEL';
},
(type, dim) => {
switch (type) {
case 'VSEL':
dim.width = LayoutUtil.getWindowWidth();
dim.height = 300;
break;
default:
dim.width = 0;
dim.heigh = 0;
}
}
);
}
}
}
export class DataCall {
// Just simulating incremental loading, don't infer anything from here
static async get(start, count) {
const responseHusky = await fetch('https://dog.ceo/api/breed/husky/images');
const responseBeagle = await fetch(
'https://dog.ceo/api/breed/beagle/images'
);
const responseJsonHusky = await responseHusky.json();
const responseJsonBeagle = await responseBeagle.json();
const fullData = responseJsonHusky.message.concat(
responseJsonBeagle.message
);
const filteredData = fullData.slice(
start,
Math.min(fullData.length, start + count)
);
return filteredData;
}
}
export class ImageRenderer extends React.Component {
shouldComponentUpdate(newProps) {
console.log(this.props.imageUrl);
return this.props.imageUrl !== newProps.imageUrl;
}
componentWillUpdate() {
//On iOS while recycling till the new image is loaded the old one remains visible. This forcefully hides the old image.
//It is then made visible onLoad
if (isIOS && this.imageRef) {
this.imageRef.setNativeProps({
opacity: 0,
});
}
}
handleOnLoad = () => {
if (isIOS && this.imageRef) {
this.imageRef.setNativeProps({
opacity: 1,
});
}
};
render() {
return (
<View
style={{
flex: 1,
// margin: 3,
// backgroundColor: 'yellow',
// height: 40,
}}>
<Image
ref={ref => {
this.imageRef = ref;
}}
style={{
flex: 1,
}}
onLoad={this.handleOnLoad}
source={{uri: this.props.imageUrl}}
/>
</View>
);
}
}
export class ViewSelector extends React.Component {
constructor(props) {
super(props);
this.currentView = 0;
}
shouldComponentUpdate(newProps) {
return this.props.viewType !== newProps.viewType;
}
onPressHandler = () => {
this.currentView = (this.currentView + 1) % 4;
this.props.viewChange(this.currentView);
};
render() {
return (
<TouchableHighlight
style={{
height: 60,
paddingTop: 20,
backgroundColor: 'black',
alignItems: 'center',
justifyContent: 'space-around',
}}
onPress={this.onPressHandler}>
<Text style={{color: 'white'}}>
Tap to Change View Type: {this.props.viewType}
</Text>
</TouchableHighlight>
);
}
}
这本来是想用来做滑动输入组件的
但是还没做完,具体滚动值还没来得及换算,以后有时间再完善
2022年06月02日16:51:52