Skip to content

Commit b6602cd

Browse files
committed
fix(Picker): 保持原有Api, 重新设计Picker. fix #295 #294
1 parent dea3bd4 commit b6602cd

File tree

3 files changed

+84
-93
lines changed

3 files changed

+84
-93
lines changed

example/examples/src/routes/Picker/index.tsx

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,24 @@
11
import React from 'react';
22
import {Text, View} from 'react-native';
3-
import {Picker} from '@uiw/react-native';
3+
import {Picker, Button} from '@uiw/react-native';
44
import {ComProps} from '../../routes';
55
import Layout, {Container} from '../../Layout';
66
const {Header, Body, Footer} = Layout;
77

88
export interface BadgeViewProps extends ComProps {}
99

1010
export default class BadgeView extends React.Component<BadgeViewProps> {
11+
state = {
12+
value: 1,
13+
};
1114
render() {
1215
const {route, navigation} = this.props;
1316
const description = route.params.description;
1417
const title = route.params.title;
18+
const arr = [];
19+
for (let i = 0; i < 100; i++) {
20+
arr.push({label: i});
21+
}
1522
return (
1623
<Container scrollEnabled={false}>
1724
<Layout>
@@ -25,35 +32,24 @@ export default class BadgeView extends React.Component<BadgeViewProps> {
2532
marginTop: 20,
2633
}}>
2734
<View style={{width: '50%'}}>
28-
<Picker
29-
date={[
30-
{label: '1'},
31-
{label: '2'},
32-
{label: '3'},
33-
{label: '4'},
34-
{label: '5'},
35-
{label: '6'},
36-
]}
37-
value={17}
38-
/>
35+
<Picker date={arr as any} value={17} />
3936
</View>
4037
<View style={{width: '50%'}}>
4138
<Picker
4239
onChange={val => {
4340
console.log('val: ', val);
4441
}}
45-
date={[
46-
{label: '1'},
47-
{label: '2'},
48-
{label: '3'},
49-
{label: '4'},
50-
{label: '5'},
51-
{label: '6'},
52-
]}
53-
value={17}
42+
date={arr as any}
43+
value={this.state.value}
5444
/>
5545
</View>
5646
</View>
47+
<Button
48+
onPress={() => {
49+
this.setState({value: this.state.value + 1});
50+
}}>
51+
控制第二列value
52+
</Button>
5753
</Body>
5854
<Footer />
5955
</Layout>

packages/core/src/Picker/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ Picker 选择器
33

44
解决 ios 与 android 和用户交互方式不同问题.
55

6+
> 避免出现样式错乱问题, 请尽量使用统一数字高度。
7+
> 激活状态尽量不要改变高度, 只是修改颜色作为标记。
8+
<!--rehype:style=border-left: 8px solid #ffe564;background-color: #ffe56440;padding: 12px 16px;-->
9+
610
### 基础示例
711

812
```jsx

packages/core/src/Picker/index.tsx

Lines changed: 63 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ import {
66
TextStyle,
77
ViewStyle,
88
LayoutChangeEvent,
9-
TouchableOpacity,
109
Text,
1110
ScrollView,
1211
Animated,
1312
NativeSyntheticEvent,
1413
NativeScrollEvent,
1514
Platform,
15+
Pressable,
1616
} from 'react-native';
1717

1818
export interface PickerDate {
@@ -58,18 +58,17 @@ const Picker = (props: PickerProps) => {
5858
value = 0,
5959
onChange,
6060
} = props;
61+
const Y = useRef(new Animated.Value(0)).current;
6162
const scrollView = useRef<ScrollView>();
6263
const ItemHeights = useRef<Array<number>>([]).current;
63-
const onPressORonScroll = useRef<'onPress' | 'onScroll'>('onScroll');
64-
const timer = useRef<NodeJS.Timeout>();
6564
const saveY = useRef<number>(0);
66-
const Y = useRef(new Animated.Value(0)).current;
65+
const timer = useRef<NodeJS.Timeout>();
66+
const onPressORonScroll = useRef<'onPress' | 'onScroll'>('onScroll');
6767
const currentY = useRef<number>(0);
68-
const isTouchEnd = useRef<boolean>(false);
69-
const isScroll = useRef<boolean>(false);
7068
const [current, setCurrent] = useState(0);
7169
useEffect(() => {
7270
onChange?.(current);
71+
onPressORonScroll.current = 'onScroll';
7372
}, [current]);
7473
useEffect(() => {
7574
if (value !== current) {
@@ -93,93 +92,77 @@ const Picker = (props: PickerProps) => {
9392
containerHeight,
9493
};
9594
}, [containerStyle, textStyle]);
95+
const getItemHeight = (event: LayoutChangeEvent) => {
96+
const { height } = event.nativeEvent.layout;
97+
const round = Math.round(height);
98+
ItemHeights?.push(round * ItemHeights.length + round);
99+
};
100+
96101
const location = (scrollY: number, index: number) => {
97-
scrollView.current?.scrollTo({ x: 0, y: scrollY - (style.containerHeight as number), animated: true });
98102
saveY.current = scrollY - (style.containerHeight as number);
99103
currentY.current = index;
100-
if (Platform.OS === 'android') {
101-
setCurrent(index);
102-
}
103-
};
104-
const scrollYEnd = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
105-
if (onPressORonScroll.current === 'onScroll') {
106-
return;
107-
}
108-
const scrollY = event.nativeEvent.contentOffset.y;
109-
const currentHeight = ItemHeights[currentY.current] - ItemHeights[0];
110-
if (scrollY - 2 <= currentHeight && scrollY + 2 >= currentHeight) {
111-
setCurrent(currentY.current);
112-
}
113-
onPressORonScroll.current = 'onScroll';
104+
let an = Platform.OS === 'android' && { duration: 0 };
105+
let os = Platform.OS === 'ios' && { animated: false };
106+
scrollView.current?.scrollTo({ x: 0, y: scrollY - (style.containerHeight as number), ...an, ...os });
107+
setCurrent(index);
114108
};
115-
const setScrollY = () => {
116-
if (onPressORonScroll.current === 'onPress') {
117-
return false;
118-
}
119-
isTouchEnd.current = true;
120-
if (!isTouchEnd.current || !isScroll.current) {
121-
return false;
122-
}
123-
if (saveY.current <= ItemHeights[0] / 1.1) {
109+
const setScrollHandle = (val: number) => {
110+
const spot = val / ItemHeights[0];
111+
if (spot <= 0.6) {
124112
scrollView.current?.scrollTo({ x: 0, y: 0, animated: true });
125-
saveY.current = 0;
126113
setCurrent(0);
127114
return false;
128115
}
129-
const spot = saveY.current / ItemHeights[0] + '';
130-
const integer = Number(spot.substr(0, spot.indexOf('.')));
131-
const decimal = Number(spot[spot.indexOf('.') + 1]);
132-
const itemIndex = decimal >= 9 ? integer + 1 : integer;
116+
const stringSpot = spot + '';
117+
const integer = Math.floor(spot);
118+
const decimal = Number(stringSpot[stringSpot.indexOf('.') + 1]);
119+
const itemIndex = decimal >= 6 ? integer + 1 : integer;
133120
scrollView.current?.scrollTo({ x: 0, y: ItemHeights[itemIndex] - ItemHeights[0], animated: true });
134-
setCurrent(itemIndex);
135121
saveY.current = ItemHeights[itemIndex] - ItemHeights[0];
136-
isScroll.current = false;
137-
isTouchEnd.current = false;
122+
setCurrent(itemIndex);
123+
clearTimeout(timer.current!);
124+
timer.current = undefined;
138125
};
139-
const getItemHeight = (event: LayoutChangeEvent) => {
140-
const { height } = event.nativeEvent.layout;
141-
ItemHeights?.push(height * ItemHeights.length + height);
126+
const listener = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
127+
if (onPressORonScroll.current === 'onPress') return;
128+
saveY.current = event.nativeEvent.contentOffset.y;
129+
if (timer.current) {
130+
clearTimeout(timer.current!);
131+
timer.current = undefined;
132+
}
133+
timer.current = setTimeout(() => {
134+
setScrollHandle(saveY.current);
135+
}, 160);
136+
};
137+
const onTouchEnd = () => {
138+
if (Platform.OS === 'ios') {
139+
if (onPressORonScroll.current === 'onPress') {
140+
setCurrent(currentY.current);
141+
return;
142+
}
143+
if (timer.current) return;
144+
setScrollHandle(saveY.current);
145+
}
142146
};
143147
return (
144148
<View style={{ paddingVertical: 10, height: (style.containerHeight as number) * lines + 10 }}>
145149
<ScrollView
146150
showsVerticalScrollIndicator={false}
147-
style={{ marginTop: -1 }}
148151
ref={scrollView as React.LegacyRef<ScrollView>}
149-
onMomentumScrollEnd={scrollYEnd}
150152
scrollEventThrottle={16}
151-
onTouchEnd={setScrollY}
153+
onTouchEnd={Platform.OS === 'ios' ? onTouchEnd : undefined}
152154
onScroll={Animated.event([{ nativeEvent: { contentOffset: { y: Y } } }], {
153-
listener: (event: NativeSyntheticEvent<NativeScrollEvent>) => {
154-
const flag = Platform.OS === 'android' ? false : !isTouchEnd.current;
155-
if (onPressORonScroll.current === 'onPress' || flag) {
156-
return false;
157-
}
158-
if (saveY.current === event.nativeEvent.contentOffset.y) {
159-
return false;
160-
}
161-
isScroll.current = false;
162-
saveY.current = event.nativeEvent.contentOffset.y;
163-
if (timer.current) {
164-
clearTimeout(timer.current!);
165-
timer.current = undefined;
166-
}
167-
timer.current = setTimeout(() => {
168-
isScroll.current = true;
169-
setScrollY();
170-
clearTimeout(timer.current!);
171-
timer.current = undefined;
172-
}, 160);
173-
},
155+
listener,
174156
useNativeDriver: false,
175157
})}
176158
>
177159
{date.map((item, index) => (
178-
<TouchableOpacity
160+
<Pressable
179161
onLayout={getItemHeight}
180162
key={index}
181-
activeOpacity={1}
163+
onPressOut={Platform.OS === 'android' ? onTouchEnd : undefined}
182164
onPress={() => {
165+
if (timer.current) return;
183166
onPressORonScroll.current = 'onPress';
184167
location(ItemHeights![index], index);
185168
}}
@@ -191,12 +174,14 @@ const Picker = (props: PickerProps) => {
191174
<Text style={current === index ? style.textAc : style.textUn}>{item[key]}</Text>
192175
</View>
193176
)}
194-
</TouchableOpacity>
195-
))}
196-
{new Array(lines - 1).fill('').map((item, index) => (
197-
<View key={index} style={style.containerUn} />
177+
</Pressable>
198178
))}
199-
{/* style.containerHeight as number * lines + 10 */}
179+
{
180+
<Pressable
181+
style={[style.containerUn, { height: (style.containerHeight as number) * (lines - 1) }]}
182+
onPressOut={Platform.OS === 'android' ? onTouchEnd : undefined}
183+
/>
184+
}
200185
</ScrollView>
201186
<View style={[style.containerAc, { top: (-style.containerHeight as number) * lines + 10 }]} />
202187
<View style={[style.containerAc, { top: (-style.containerHeight as number) * (lines - 1) + 10 }]} />
@@ -209,16 +194,22 @@ const styles = StyleSheet.create({
209194
height: 50,
210195
justifyContent: 'center',
211196
alignItems: 'center',
197+
paddingVertical: 0,
198+
paddingHorizontal: 0,
212199
},
213200
border: {
214201
backgroundColor: '#E6E6E6',
215202
height: 1,
216203
position: 'relative',
217204
zIndex: 999,
205+
paddingVertical: 0,
206+
paddingHorizontal: 0,
218207
},
219208
textStyle: {
220209
fontSize: 20,
221210
color: '#000',
211+
paddingVertical: 0,
212+
paddingHorizontal: 0,
222213
},
223214
acTextStyle: {
224215
color: '#fd8a00',

0 commit comments

Comments
 (0)