今天我进一步进行完善,主要是把暴露的接口写完了,我还会进一步完善,如果在项目中跑起来没什么问题的话,我就会上传到 npm
,以供大家学习和使用,当然如果能封装更好的,大家可以一块学习。
一、props
下面看 props
:
- data 这个参数必传。跟
SectionList
相似,只不过我的数组属性是items
, 而SectionList
的是data
;
[
{
...
items: []
}
]
- selectedIndexes 初始化已选中的下标,如:
// 这里面的元素个数取决于你的总列数
[0, 0, 0] // 代表选中第一列的第一个,第二列的第一个,第三列的第一个
[2, 1, 0] // 代表选中第一列的第三个,第二列的第二个,第三个的第一个
// 加入说你传入的是 [1] 那么就会选中第一列的第二个,第二列的第二个,第三列的第二个,也就相当于 [1, 1, 1]
// 如果传入的是 [1, 2] 那么就会选中第一列的第二个,第二列的第三个,第三列的第三个,也就相当于 [1, 2, 2]
// 如果不传,就默认: [0, 0, 0]
- renderItems, renderItem 这个参数必传。这个就是
FlatList
的renderItem
,这两个的优先级renderItems
>renderItem
。
// 如果传入的 renderItem 而没有传入 renderItems ,
// 那么所有列都使用相同的 renderItem ,否则按照 renderItems 数组里面的渲染各自的
// 先假设总的列数是 n
// 渲染规则跟上面的 selectedIndexes 相同
// 比如 [()=>null] ,相当于 [()=>,()=>null,()=>null]
// 比如 [()=>null,()=><Text>test</Text>] ,相当于 [()=>null,()=><Text>test</Text>, ()=><Text>test</Text>]
// 值得说的是回调的参数
// ({item, index, isSelected}) => null
// 其中 item 代表每一列的每一项数据
// index 代表下标
// isSelected 代表当前行是否被选中
- n 这个就是代表总列数,这个参数必传。
- style 最外面容器的样式。
- widthRadio 这个是每一列的宽度占比。
// 比如: ['30%', '30%', '30%'],那么就代表三列的宽度都是屏幕宽度的一半
// 比如: [240, '40%'],那么代表第一列 240 ,第二列占屏幕的宽度百分之四十
// 如果有缺省值,也就是传入 n > 数组的长度,比如 n 我传入的是 5 ,而数组 ['30%', '30%', '30%'] ,
// 那么相当于 ['30%', '30%', '30%', '30%', '30%'] 这样的话有些列就不会在屏幕上显示,只需要滑动就能看到
- flatListContainerStyle 这个代表所有列的容器的样式。
- ListHeaderComponent 跟
FlatList
相似,只不过不同的是这个不会随着列表的上下滑动而滑动。 - ListFooterComponent 同上。
- styles 这个代表每一个
FlatList
的style
,目前的功能是一一对应的,以后也会像上面的selectedIndexes
一样。 - moreFlatListProps 这个是
FlatList
的props
,也是一一对应的,同样以后会实现像上面的效果。 - scrollViewProps 这个就是
ScrollView
的props
。 - itemPress 这个就是每一项的点击事件。
// itemPress 回调的参数就是每一列的数据,除 items 外的数据,像下面实现:
const itemPress = (item, i) => {}
// 这里面的 i 是代表第几列,第一列是 0 以此类推。
二、效果图
三、示例代码
const DefaultPicker = (props) => {
const renderItemTitle = useCallback(({item, index, isSelected}) => {
const style = {
borderLeftWidth: 4,
borderLeftColor: isSelected ? colors.primary : '#fff',
backgroundColor: isSelected ? '#F8F8F8' : '#fff',
};
return (
<View style={style}>
<Text key={index} style={styles.itemTitleStyle}>
{item?.name || ''}
</Text>
</View>
);
}, []);
const renderItemOther = useCallback(({item, index, isSelected}) => {
const style = {
color: isSelected ? colors.primary : '#505050',
};
return (
<Text key={index} style={[styles.itemOtherStyle, style]}>
{item?.name || ''}
</Text>
);
}, []);
return (
<View style={styles.fullscreen}>
<View style={styles.fullscreen} />
<Picker
{...props}
style={styles.pickerStyle}
styles={[styles.f1BgColor, styles.f23BgColor, styles.f23BgColor]}
ListHeaderComponent={() => ListHeaderComponent(props.disappear)}
renderItems={[renderItemTitle, renderItemOther]}
n={3}
moreFlatListProps={[
{keyExtractor: (item, index) => item + index},
{keyExtractor: (item, index) => item + index},
{keyExtractor: (item, index) => item + index},
]}
/>
</View>
);
};
四、源码
import React, {useCallback, useState} from 'react';
import {
ScrollView,
View,
StyleSheet,
FlatList,
Dimensions,
Pressable,
} from 'react-native';
const selectData = (index, indexes, data) => {
let tempData = data;
for (let i = 0; i < index; i++) {
tempData = tempData?.[indexes[i]]?.items || [];
}
return tempData;
};
/**
* 初始化已经选择的数组
* @param {Array<number>} selectedIndexes 传入的已经选择的下标
* @param {number} n 列的个数
*/
const initSelectedIndexes = (selectedIndexes = [], n) => {
const tempIndexes = [...selectedIndexes];
for (let i = selectedIndexes?.length || 0; i < n; i++) {
tempIndexes.push(0);
}
return tempIndexes;
};
/**
* 得到模拟数组
* @param {number} n 所有数据
*/
const getSimulateArray = (n) => {
return new Array(n).fill(1);
};
/**
*
* @param {Array<number|string>} widthRadio 各宽度比例
*/
const getWidths = (widthRadio, n) => {
const width = Dimensions.get('screen').width;
const len = widthRadio?.length || 0;
const tempWidths = [];
for (let i = 0; i < len; i++) {
if (typeof widthRadio[i] === 'string') {
const radio = parseFloat(widthRadio[i]) / 100;
tempWidths.push(width * radio);
} else {
tempWidths.push(widthRadio[i]);
}
}
// 如果还有没有添加到数组中的就默认使用最后一个宽度,如果前面都没有值
// ,那么就定义宽度为总宽度的 1/3
const equalWidth =
tempWidths[tempWidths.length - 1] || parseFloat((width / 3).toFixed(2));
for (let j = len; j < n; j++) {
tempWidths.push(equalWidth);
}
return tempWidths;
};
const getComponent = (ele = null) => {
let temp;
if (typeof ele === 'function') {
temp = ele;
} else {
temp = () => ele;
}
return temp;
};
const getRenderItem = (renderItems = [], n) => {
if (!renderItems) {
throw new Error('您至少传入一个 renderItem');
}
const tempRenderItems = [];
if (Array.isArray(renderItems)) {
tempRenderItems.push(...renderItems);
} else {
tempRenderItems.push(renderItems);
}
const len = tempRenderItems.length;
const sameRenderItem = tempRenderItems[len - 1];
for (let i = len; i < n; i++) {
tempRenderItems.push(sameRenderItem);
}
return tempRenderItems;
};
/**
* @typedef {{
* data: Array,
* selectedIndexes?: Array<number>,
* renderItems: Array<({item: any, index: number, isSelected: boolean}) => React.ReactElement>,
* renderItem: {item: any, index: number}) => React.ReactElement,
* styles?: Array<import('react-native').ViewStyle>,
* style?: import('react-native').ViewStyle,
* flatListContainerStyle?: import('react-native').ViewStyle,
* widthRadio?: Array<number|string>,
* ListHeaderComponent?: () => React.ReactElement | import('react').ReactElement | null,
* ListFooterComponent?: () => React.ReactElement | import('react').ReactElement | null,
* moreFlatListProps?: Array<import('react-native').FlatListProps>,
* scrollViewProps?: import('react-native').ScrollViewProps,
* n: number,
* itemPress?: (item: any, n: number) => void
* }} PickerType
* @param {PickerType} param0
*/
const Picker = ({
data,
selectedIndexes,
renderItems,
renderItem,
style,
flatListContainerStyle,
widthRadio,
ListHeaderComponent,
ListFooterComponent,
styles,
moreFlatListProps,
scrollViewProps,
n,
itemPress,
}) => {
const [indexes, setIndexes] = useState(
initSelectedIndexes(selectedIndexes, n),
);
const onPress = useCallback((i, index) => {
setIndexes((oldIndexes) => {
oldIndexes[i] = index;
for (let j = i + 1; j < 3; j++) {
oldIndexes[j] = 0;
}
return [...oldIndexes];
});
}, []);
const toRenderItem = useCallback(
({item, index}, i) => {
const setItem = {...item};
delete setItem.items;
return (
<Pressable
onPress={() => {
onPress(i, index);
itemPress?.(setItem, i);
}}>
{getRenderItem(renderItems || renderItem, n)[i]({
item,
index,
isSelected: indexes[i] === index,
})}
</Pressable>
);
},
[renderItems, renderItem, n, indexes, onPress, itemPress],
);
return (
<View style={[pickerStyles.container, style]}>
{getComponent(ListHeaderComponent)()}
<ScrollView {...scrollViewProps} horizontal={true}>
<View style={[pickerStyles.listContainer, flatListContainerStyle]}>
{getSimulateArray(n).map((_, index) => {
return (
<FlatList
{...(moreFlatListProps?.[index] || {})}
style={[
pickerStyles.flatListStyle,
styles?.[index] || {},
{width: getWidths(widthRadio, n)[index]},
]}
data={selectData(index, indexes, data)}
renderItem={({item, index: i}) =>
toRenderItem({item, index: i}, index)
}
/>
);
})}
</View>
</ScrollView>
{getComponent(ListFooterComponent)()}
</View>
);
};
const pickerStyles = StyleSheet.create({
container: {
flex: 1,
},
listContainer: {
flex: 1,
flexDirection: 'row',
backgroundColor: '#fff',
},
flatListStyle: {
height: '100%',
},
});
export default React.memo(Picker);
这篇主要是介绍 props
的和我实战的实例。以后还会完善,最终要达到上传到 npm
的目的。如果以后在使用过程中遇到性能的问题我也会进行优化。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!