import { View, Text, TouchableWithoutFeedback, TouchableOpacity, FlatList, } from 'react-native' import React,{ PureComponent, Component, } from 'react' import _ from 'lodash' import styleSheet, {getThemeColor} from '../utils/styleSheet' import {Callback} from "modern/types/lang"; import Modal from './Modal' import {getScreenHeight, getScreenWidth} from "modern/consts/ui-common"; const styles = styleSheet.create({ container:{ flex:1, backgroundColor: 0x00000074, alignItems: 'center', justifyContent: 'flex-end', }, contentView:{ flexDirection:'row', justifyContent: 'space-around', backgroundColor: 'white' }, stage:{ top:0, bottom:0, left:0, right:0, position:'absolute', zIndex:10, flexDirection:'row', justifyContent: 'space-around', }, maskView:{ left:0, right:0, position:'absolute', top:0, bottom:0, zIndex:0, justifyContent:'space-between' }, emptyView:{ height:50, flexDirection:'row', justifyContent:'space-between', paddingHorizontal:20, backgroundColor: 'white', }, maskStyle:{ width:'100%', backgroundColor:'rgba(232,232,232,0.9)', borderColor:"rgb(181,181,181)" }, headerBtnView:{ height:50,justifyContent: 'center' }, cancelText:{ fontSize:16,color:'gray' } }) interface ISHeadProps { customHead:any, confirm:Callback, hide:Callback, headOptions:any } const Head:React.FC<ISHeadProps> = React.memo(({customHead,confirm,hide,headOptions})=>{ if(customHead){ return customHead } const { cancelTextStyle={}, cancelBtnView={}, confirmBtnView={}, confirmTextStyle={}, leftText='取消', rightText='确定', headerContainView={}, } = headOptions return <View style={[styles.emptyView,headerContainView]}> <TouchableOpacity style={[styles.headerBtnView,cancelBtnView]} onPress={hide}> <Text style={[styles.cancelText,cancelTextStyle]}>{leftText}</Text> </TouchableOpacity> <TouchableOpacity onPress={confirm} style={[styles.headerBtnView,confirmBtnView]}> <Text style={[{color:getThemeColor(),fontSize:16},confirmTextStyle]}>{rightText}</Text> </TouchableOpacity> </View> }) interface ISEmpty { hide:Callback } const Empty:React.FC<ISEmpty> = React.memo(({hide})=>{ return <TouchableWithoutFeedback onPress={hide}> <View style={{flex:1,width:getScreenWidth()}} /> </TouchableWithoutFeedback> }) interface ISMaskView { maxLength:number, itemHeihght:number, maskOptions:any, } const MaskView:React.FC<ISMaskView> = React.memo(({maxLength,itemHeihght,maskOptions={}})=>{ const { upMaskView, bottomMaskView } = maskOptions const mid = Math.floor(maxLength/2) return <View style={[styles.maskView]}> <View style={[styles.maskStyle,{height:itemHeihght*mid,borderBottomWidth:1,},upMaskView]}/> <View style={[styles.maskStyle,{height:itemHeihght*mid,borderTopWidth:1,},bottomMaskView]}/> </View> }) interface ISourceType { id:string, name:string, sub?:ISourceType[] [propname:string]:any, } interface ISitemOptons { itemHeihght?:number, maxLength?:5 | 7 | 9, ActiveTextStyle?:any, normalTextStyle?:any, } interface ISProps { sourceData:ISourceType[], cancel?:Callback, confirm:Callback, selectData:ISourceType[], pickerType:'scroll' | 'click' | 'all', customHead:any, headOptions:any, maskOptions:any, itemOptons:itemOptons, } interface ISState { visible:boolean, floor:number, } function WithHeadAndMethod(WrapComponent:any){ return class PickerAlertView extends PureComponent<ISProps,ISState>{ public selectedData:ISourceType[] =[] public static defaultProps = { itemOptons:{}, selectData:[], headOptions:{}, pickerType:'all', maskOptions:{} } constructor(props:ISProps) { super(props); this.state = { visible:false, floor:this.getMaxFloor(), } } public getMaxFloor = ()=>{ const { sourceData } = this.props if(_.isEmpty(sourceData)) return 0 let floor:number = 1 function treeData(arr:any){ const sub:any = arr.find((item:any)=>item?.sub?.length > 0) if(sub){ floor = floor 1 let arr2:any = [] arr.forEach((item:any)=>{ if(item?.sub?.length>0){ arr2 = arr2.concat(item.sub) } }) treeData(arr2) } } treeData(sourceData) return floor } public show = ()=>{ this.setState({visible:true}) } public confirm = ()=>{ const { confirm=_.noop } = this.props this.hide() confirm(this.selectedData) } public hide = ()=>{ const { cancel=_.noop } = this.props cancel() this.setState({visible:false}) } public getSelected = ()=>this.selectedData private setSelected = (item:ISourceType,floor:number)=>{ this.selectedData[floor] = item } public componentDidUpdate(preProps,prevState,snapshot){ if(!_.isEqual(this.props.sourceData,preProps.sourceData)){ this.setState({ floor:this.getMaxFloor() }) } } public render(){ const { visible, floor, } = this.state const { selectData, sourceData, pickerType, customHead, headOptions, maskOptions, itemOptons, } = this.props if(!visible) return null if(!_.isEmpty(selectData) && selectData.length !== floor ) return null if(_.isEmpty(sourceData)) return null const { maxLength=9, itemHeihght=50, ActiveTextStyle={}, normalTextStyle={} } = itemOptons return ( <Modal visible={visible} animationType="slide" transparent={true} > <View style={styles.container} > <Empty hide={this.hide}/> <View style={{ width:getScreenWidth()}}> <Head headOptions={headOptions} hide={this.hide} confirm={this.confirm} customHead={customHead} /> <View style={[{height:itemHeihght*maxLength},styles.contentView]}> <View style={styles.stage}> <WrapComponent sourceList={sourceData} setSelect={this.setSelected} floor={floor} currentFloor={0} maxLength={maxLength} defaultSelected = {selectData} itemHeihght={itemHeihght} ActiveTextStyle={ActiveTextStyle} normalTextStyle={normalTextStyle} pickerType={pickerType} /> </View> <MaskView maxLength={maxLength} itemHeihght={itemHeihght} maskOptions={maskOptions} /> </View> </View> </View> </Modal> ) } } } interface ISItemProps { sourceList:ISourceType[], setSelect:Callback, floor:number, currentFloor:number, maxLength: 5 | 7 | 9, defaultSelected:ISourceType[], itemHeihght:number, ActiveTextStyle:any, normalTextStyle:any, pickerType:string } interface ISItemState { nextData:ISourceType[], currentChoose:ISourceType, initialScrollIndex:number, renderList:any[], } class PickerAlertViewItem extends PureComponent<ISItemProps,ISItemState> { public scrollView:any public flatEl:any public currentItem:any public itemEL:any public config:any = { waitForInteraction: false, itemVisiblePercentThreshold:100, } public constructor(props:ISItemProps) { super(props); this.state = { nextData:this.defaultRenderList(), currentChoose:this.getDefaultChoose(), initialScrollIndex:this.getInitialScrollIndex(), renderList:this.flatListData(), } } public getInitialScrollIndex = ()=>{ const {defaultSelected,currentFloor,sourceList } = this.props if(_.isEmpty(defaultSelected)) return 0 const initIndex = sourceList.findIndex(item=>item.id === defaultSelected[currentFloor]?.id) return initIndex<0?0:initIndex } public defaultRenderList = ()=>{ const { currentFloor, floor, defaultSelected, sourceList,} = this.props const getDefaultList = ()=>{ if(currentFloor 1 === floor ) return [] return sourceList.find(value=>value.id === defaultSelected[currentFloor].id)?.sub || [] } const getNoFefault = ()=>this.props.sourceList?.[0]?.sub || [] return _.isEmpty(defaultSelected)?getNoFefault():getDefaultList() } public getDefaultChoose = ()=>{ const { defaultSelected, currentFloor, sourceList } = this.props return _.isEmpty(defaultSelected)?sourceList?.[0]:defaultSelected[currentFloor] } public mid = ()=>{ const { maxLength } = this.props return Math.floor(maxLength/2) } public flatListData = (nextData:any[])=>{ const { sourceList } = this.props const mid = this.mid() const arr = Array(mid).fill({id:'',name:'',sub:[]}) const currentData = nextData?nextData:sourceList return [...arr,...currentData,...arr] } public getItemLayout = (data:any,index:any) =>{ const { itemHeihght } = this.props return {length: itemHeihght, offset: itemHeihght * index, index} } public renderItem = ({item,index,separators}:any)=>{ const { itemHeihght ,ActiveTextStyle,normalTextStyle,pickerType} = this.props const {currentChoose} = this.state const itemPress = (pickerType === 'scroll' || item.id === currentChoose?.id)?_.noop:this.clickItem return <TouchableWithoutFeedback onPress={()=>itemPress(item,index)}> <Text numberOfLines={1} style={[ { height:itemHeihght, textAlign:'center', lineHeight:itemHeihght, opacity:0.8, paddingHorizontal: 10, }, item.id === currentChoose?.id?ActiveTextStyle:normalTextStyle ]} >{item.name}</Text> </TouchableWithoutFeedback> } public clickItem =(item:ISourceType,index:any)=>{ const mid = this.mid() if(index<mid) return if(!item) return this.scrollToIndex(index) this.chooseItem(index,item) } public scrollToIndex = (index:any)=>{ if(this.flatEl){ this.flatEl.scrollToIndex({ index, viewPosition:0.5, animated:true }) } } public chooseItem = (index:number,item:ISourceType)=>{ const { setSelect,currentFloor } = this.props setSelect(item,currentFloor) this.setState({ currentChoose:item || {}, nextData:item?.sub || [] },()=>{ if(this.itemEL) this.itemEL.reduction() }) } public reduction=()=>{ const currentChoose = this.props.sourceList?.[0] || {} const nextData = currentChoose?.sub || [] const mid = this.mid() this.setState({ currentChoose, nextData, renderList:this.flatListData() },()=>{ _.delay(()=>this.clickItem(currentChoose,mid),60) }) } public componentDidMount(){ this.initSelected() } public initSelected = ()=>{ const {setSelect,currentFloor } = this.props const { currentChoose } = this.state setSelect(currentChoose,currentFloor) if(this.flatEl) this.flatEl.recordInteraction() } public render(){ const { floor, currentFloor, setSelect, defaultSelected, maxLength, itemHeihght=50, ActiveTextStyle, normalTextStyle, pickerType } = this.props const { nextData, initialScrollIndex, renderList, } = this.state const nextFloor = currentFloor 1 return ( <> <FlatList style={{flex:1 }} ref={ref=>this.flatEl = ref} showsHorizontalScrollIndicator={false} showsVerticalScrollIndicator={false} keyExtractor={(item: object, index: number) => String(index)} renderItem={this.renderItem} viewabilityConfig={this.config} data={renderList} getItemLayout={this.getItemLayout} initialScrollIndex={initialScrollIndex} onMomentumScrollEnd={this._moveEnd} scrollEnabled={pickerType === 'click'?false:true} /> { floor > nextFloor ? <PickerAlertViewItem ref={ref=>this.itemEL = ref} sourceList={nextData} floor={floor} setSelect={setSelect} currentFloor={nextFloor} defaultSelected={defaultSelected} maxLength={maxLength} itemHeihght={itemHeihght} ActiveTextStyle={ActiveTextStyle} normalTextStyle={normalTextStyle} pickerType={pickerType} />: null } </> ) } private _moveEnd=(e)=>{ const {itemHeihght} = this.props const { renderList } = this.state const contentOffset = e.nativeEvent.contentOffset.y; const mid = this.mid() const index = Math.round(contentOffset/itemHeihght) mid this.clickItem(renderList[index],index) } } export default WithHeadAndMethod(PickerAlertViewItem) ,我来为大家科普一下关于reactnative设置最小高度?以下内容希望对你有帮助!

reactnative设置最小高度(React-native多级联动PickerAlert封装)

reactnative设置最小高度

import { View, Text, TouchableWithoutFeedback, TouchableOpacity, FlatList, } from 'react-native' import React,{ PureComponent, Component, } from 'react' import _ from 'lodash' import styleSheet, {getThemeColor} from '../utils/styleSheet' import {Callback} from "modern/types/lang"; import Modal from './Modal' import {getScreenHeight, getScreenWidth} from "modern/consts/ui-common"; const styles = styleSheet.create({ container:{ flex:1, backgroundColor: 0x00000074, alignItems: 'center', justifyContent: 'flex-end', }, contentView:{ flexDirection:'row', justifyContent: 'space-around', backgroundColor: 'white' }, stage:{ top:0, bottom:0, left:0, right:0, position:'absolute', zIndex:10, flexDirection:'row', justifyContent: 'space-around', }, maskView:{ left:0, right:0, position:'absolute', top:0, bottom:0, zIndex:0, justifyContent:'space-between' }, emptyView:{ height:50, flexDirection:'row', justifyContent:'space-between', paddingHorizontal:20, backgroundColor: 'white', }, maskStyle:{ width:'100%', backgroundColor:'rgba(232,232,232,0.9)', borderColor:"rgb(181,181,181)" }, headerBtnView:{ height:50,justifyContent: 'center' }, cancelText:{ fontSize:16,color:'gray' } }) interface ISHeadProps { customHead:any, confirm:Callback, hide:Callback, headOptions:any } const Head:React.FC<ISHeadProps> = React.memo(({customHead,confirm,hide,headOptions})=>{ if(customHead){ return customHead } const { cancelTextStyle={}, cancelBtnView={}, confirmBtnView={}, confirmTextStyle={}, leftText='取消', rightText='确定', headerContainView={}, } = headOptions return <View style={[styles.emptyView,headerContainView]}> <TouchableOpacity style={[styles.headerBtnView,cancelBtnView]} onPress={hide}> <Text style={[styles.cancelText,cancelTextStyle]}>{leftText}</Text> </TouchableOpacity> <TouchableOpacity onPress={confirm} style={[styles.headerBtnView,confirmBtnView]}> <Text style={[{color:getThemeColor(),fontSize:16},confirmTextStyle]}>{rightText}</Text> </TouchableOpacity> </View> }) interface ISEmpty { hide:Callback } const Empty:React.FC<ISEmpty> = React.memo(({hide})=>{ return <TouchableWithoutFeedback onPress={hide}> <View style={{flex:1,width:getScreenWidth()}} /> </TouchableWithoutFeedback> }) interface ISMaskView { maxLength:number, itemHeihght:number, maskOptions:any, } const MaskView:React.FC<ISMaskView> = React.memo(({maxLength,itemHeihght,maskOptions={}})=>{ const { upMaskView, bottomMaskView } = maskOptions const mid = Math.floor(maxLength/2) return <View style={[styles.maskView]}> <View style={[styles.maskStyle,{height:itemHeihght*mid,borderBottomWidth:1,},upMaskView]}/> <View style={[styles.maskStyle,{height:itemHeihght*mid,borderTopWidth:1,},bottomMaskView]}/> </View> }) interface ISourceType { id:string, name:string, sub?:ISourceType[] [propname:string]:any, } interface ISitemOptons { itemHeihght?:number, maxLength?:5 | 7 | 9, ActiveTextStyle?:any, normalTextStyle?:any, } interface ISProps { sourceData:ISourceType[], cancel?:Callback, confirm:Callback, selectData:ISourceType[], pickerType:'scroll' | 'click' | 'all', customHead:any, headOptions:any, maskOptions:any, itemOptons:itemOptons, } interface ISState { visible:boolean, floor:number, } function WithHeadAndMethod(WrapComponent:any){ return class PickerAlertView extends PureComponent<ISProps,ISState>{ public selectedData:ISourceType[] =[] public static defaultProps = { itemOptons:{}, selectData:[], headOptions:{}, pickerType:'all', maskOptions:{} } constructor(props:ISProps) { super(props); this.state = { visible:false, floor:this.getMaxFloor(), } } public getMaxFloor = ()=>{ const { sourceData } = this.props if(_.isEmpty(sourceData)) return 0 let floor:number = 1 function treeData(arr:any){ const sub:any = arr.find((item:any)=>item?.sub?.length > 0) if(sub){ floor = floor 1 let arr2:any = [] arr.forEach((item:any)=>{ if(item?.sub?.length>0){ arr2 = arr2.concat(item.sub) } }) treeData(arr2) } } treeData(sourceData) return floor } public show = ()=>{ this.setState({visible:true}) } public confirm = ()=>{ const { confirm=_.noop } = this.props this.hide() confirm(this.selectedData) } public hide = ()=>{ const { cancel=_.noop } = this.props cancel() this.setState({visible:false}) } public getSelected = ()=>this.selectedData private setSelected = (item:ISourceType,floor:number)=>{ this.selectedData[floor] = item } public componentDidUpdate(preProps,prevState,snapshot){ if(!_.isEqual(this.props.sourceData,preProps.sourceData)){ this.setState({ floor:this.getMaxFloor() }) } } public render(){ const { visible, floor, } = this.state const { selectData, sourceData, pickerType, customHead, headOptions, maskOptions, itemOptons, } = this.props if(!visible) return null if(!_.isEmpty(selectData) && selectData.length !== floor ) return null if(_.isEmpty(sourceData)) return null const { maxLength=9, itemHeihght=50, ActiveTextStyle={}, normalTextStyle={} } = itemOptons return ( <Modal visible={visible} animationType="slide" transparent={true} > <View style={styles.container} > <Empty hide={this.hide}/> <View style={{ width:getScreenWidth()}}> <Head headOptions={headOptions} hide={this.hide} confirm={this.confirm} customHead={customHead} /> <View style={[{height:itemHeihght*maxLength},styles.contentView]}> <View style={styles.stage}> <WrapComponent sourceList={sourceData} setSelect={this.setSelected} floor={floor} currentFloor={0} maxLength={maxLength} defaultSelected = {selectData} itemHeihght={itemHeihght} ActiveTextStyle={ActiveTextStyle} normalTextStyle={normalTextStyle} pickerType={pickerType} /> </View> <MaskView maxLength={maxLength} itemHeihght={itemHeihght} maskOptions={maskOptions} /> </View> </View> </View> </Modal> ) } } } interface ISItemProps { sourceList:ISourceType[], setSelect:Callback, floor:number, currentFloor:number, maxLength: 5 | 7 | 9, defaultSelected:ISourceType[], itemHeihght:number, ActiveTextStyle:any, normalTextStyle:any, pickerType:string } interface ISItemState { nextData:ISourceType[], currentChoose:ISourceType, initialScrollIndex:number, renderList:any[], } class PickerAlertViewItem extends PureComponent<ISItemProps,ISItemState> { public scrollView:any public flatEl:any public currentItem:any public itemEL:any public config:any = { waitForInteraction: false, itemVisiblePercentThreshold:100, } public constructor(props:ISItemProps) { super(props); this.state = { nextData:this.defaultRenderList(), currentChoose:this.getDefaultChoose(), initialScrollIndex:this.getInitialScrollIndex(), renderList:this.flatListData(), } } public getInitialScrollIndex = ()=>{ const {defaultSelected,currentFloor,sourceList } = this.props if(_.isEmpty(defaultSelected)) return 0 const initIndex = sourceList.findIndex(item=>item.id === defaultSelected[currentFloor]?.id) return initIndex<0?0:initIndex } public defaultRenderList = ()=>{ const { currentFloor, floor, defaultSelected, sourceList,} = this.props const getDefaultList = ()=>{ if(currentFloor 1 === floor ) return [] return sourceList.find(value=>value.id === defaultSelected[currentFloor].id)?.sub || [] } const getNoFefault = ()=>this.props.sourceList?.[0]?.sub || [] return _.isEmpty(defaultSelected)?getNoFefault():getDefaultList() } public getDefaultChoose = ()=>{ const { defaultSelected, currentFloor, sourceList } = this.props return _.isEmpty(defaultSelected)?sourceList?.[0]:defaultSelected[currentFloor] } public mid = ()=>{ const { maxLength } = this.props return Math.floor(maxLength/2) } public flatListData = (nextData:any[])=>{ const { sourceList } = this.props const mid = this.mid() const arr = Array(mid).fill({id:'',name:'',sub:[]}) const currentData = nextData?nextData:sourceList return [...arr,...currentData,...arr] } public getItemLayout = (data:any,index:any) =>{ const { itemHeihght } = this.props return {length: itemHeihght, offset: itemHeihght * index, index} } public renderItem = ({item,index,separators}:any)=>{ const { itemHeihght ,ActiveTextStyle,normalTextStyle,pickerType} = this.props const {currentChoose} = this.state const itemPress = (pickerType === 'scroll' || item.id === currentChoose?.id)?_.noop:this.clickItem return <TouchableWithoutFeedback onPress={()=>itemPress(item,index)}> <Text numberOfLines={1} style={[ { height:itemHeihght, textAlign:'center', lineHeight:itemHeihght, opacity:0.8, paddingHorizontal: 10, }, item.id === currentChoose?.id?ActiveTextStyle:normalTextStyle ]} >{item.name}</Text> </TouchableWithoutFeedback> } public clickItem =(item:ISourceType,index:any)=>{ const mid = this.mid() if(index<mid) return if(!item) return this.scrollToIndex(index) this.chooseItem(index,item) } public scrollToIndex = (index:any)=>{ if(this.flatEl){ this.flatEl.scrollToIndex({ index, viewPosition:0.5, animated:true }) } } public chooseItem = (index:number,item:ISourceType)=>{ const { setSelect,currentFloor } = this.props setSelect(item,currentFloor) this.setState({ currentChoose:item || {}, nextData:item?.sub || [] },()=>{ if(this.itemEL) this.itemEL.reduction() }) } public reduction=()=>{ const currentChoose = this.props.sourceList?.[0] || {} const nextData = currentChoose?.sub || [] const mid = this.mid() this.setState({ currentChoose, nextData, renderList:this.flatListData() },()=>{ _.delay(()=>this.clickItem(currentChoose,mid),60) }) } public componentDidMount(){ this.initSelected() } public initSelected = ()=>{ const {setSelect,currentFloor } = this.props const { currentChoose } = this.state setSelect(currentChoose,currentFloor) if(this.flatEl) this.flatEl.recordInteraction() } public render(){ const { floor, currentFloor, setSelect, defaultSelected, maxLength, itemHeihght=50, ActiveTextStyle, normalTextStyle, pickerType } = this.props const { nextData, initialScrollIndex, renderList, } = this.state const nextFloor = currentFloor 1 return ( <> <FlatList style={{flex:1 }} ref={ref=>this.flatEl = ref} showsHorizontalScrollIndicator={false} showsVerticalScrollIndicator={false} keyExtractor={(item: object, index: number) => String(index)} renderItem={this.renderItem} viewabilityConfig={this.config} data={renderList} getItemLayout={this.getItemLayout} initialScrollIndex={initialScrollIndex} onMomentumScrollEnd={this._moveEnd} scrollEnabled={pickerType === 'click'?false:true} /> { floor > nextFloor ? <PickerAlertViewItem ref={ref=>this.itemEL = ref} sourceList={nextData} floor={floor} setSelect={setSelect} currentFloor={nextFloor} defaultSelected={defaultSelected} maxLength={maxLength} itemHeihght={itemHeihght} ActiveTextStyle={ActiveTextStyle} normalTextStyle={normalTextStyle} pickerType={pickerType} />: null } </> ) } private _moveEnd=(e)=>{ const {itemHeihght} = this.props const { renderList } = this.state const contentOffset = e.nativeEvent.contentOffset.y; const mid = this.mid() const index = Math.round(contentOffset/itemHeihght) mid this.clickItem(renderList[index],index) } } export default WithHeadAndMethod(PickerAlertViewItem)

数据结构很简单

[ { id:"1', name:'test1', sub:[] }, { id:"2', name:'test2', sub:[] }, ]

有优化的地方;欢迎大家指正!一起进步!实践出真知

,