前言:終于開始做接觸公司rn項目了,不容易啊!!! 在項目需求中需要用到switch組件,但是在rn中,switch是基于各個平臺自己的風格的,所以ios跟android是不一樣的(這就尷尬了!咋辦呢?自己定義唄,哈哈~~),不廢話了,先看看效果:

我們可以看到,rn原生的switch,兩個平臺中還是很大的差別的,為了跟需求一致(個人覺得android的好看),只好委屈自己去做成跟ios一樣的效果,上圖中也都已經看到我們實現好的效果圖了,效果還是不錯滴。

實現起來呢也是非常容易,小伙伴看到代碼就知道了,稍后我會直接貼代碼,先簡單說一下實現方式: 1、定義一個view,然后設置style,里面放置一個圓形的背景是白色的thumb view。
render() { return ( <View style={{marginTop: 20}} {...this._panResponder.panHandlers} > <TouchableWithoutFeedback onPRess={this._onPress.bind(this)} > <View ref={(ref)=>{ this.container=ref; }}onCheckChangeListener style={[styles.container,{backgroundColor:this._getBgColor()}]} > <Thumb style={[{ width:14*2, height:14*2, left:this._animatedThumbLeft, top:0, alignItems:'center' }]} /> </View> </TouchableWithoutFeedback> </View> ); }2、通過監聽手勢的變換(移動,抬起)改變相應的動畫值,跟container的背景。
onPanResponderMove: (evt, gestureState) => { // 最近一次的移動距離為gestureState.move{X,Y} let x=(self._animatedThumbLeft._value+gestureState.dx); console.log('x-->'+x); if(x>17){ x=17 } if(x<0){ x=0 } self._animatedThumbLeft.setValue(x); // 從成為響應者開始時的累計手勢移動距離為gestureState.d{x,y} }, onPanResponderTerminationRequest: (evt, gestureState) => true, onPanResponderRelease: (evt, gestureState) => { // 用戶放開了所有的觸摸點,且此時視圖已經成為了響應者。 // 一般來說這意味著一個手勢操作已經成功完成。 console.log('onPanResponderRelease'); if(gestureState.dx==0){ self._onPress(); }else{ if(self._animatedThumbLeft._value<8.5){ this.state.check=true; }else{ this.state.check=false; } self._onPress(); } },3、執行動畫移動Thumb.
MySwtich的全部代碼:
/** * @author YASIN * @version [React Native PABank V01,17/2/23] * @date 17/2/23 * @description PASwitch2 */import React,{Component}from 'react';import { View, StyleSheet, Animated, TouchableWithoutFeedback, PanResponder}from 'react-native';class ThumbView extends Component { render() { return ( <View style={this.props.style} > <View style={[{ width:14*2, height:14*2, borderRadius:14, borderColor: '#cccccc', borderWidth:1, backgroundColor:'white', }]} /> </View> ); }}const Thumb = Animated.createAnimatedComponent(ThumbView);export default class PASwitch2 extends Component { // 構造 constructor(props) { super(props); let self=this; // 初始狀態 this.state = { check: false, translateX:0 }; this._animatedThumbLeft = new Animated.Value(this.state.check ? 17 : 0); this._animatedThumbLeft.addListener(()=>{ self.container&&self.container.setNativeProps({ style:[ self.props.style, { backgroundColor:self._getBgColor() } ] }); }) this._panResponder = PanResponder.create({ // 要求成為響應者: onStartShouldSetPanResponder: (evt, gestureState) => true, onStartShouldSetPanResponderCapture: (evt, gestureState) => true, onMoveShouldSetPanResponder: (evt, gestureState) => true, onMoveShouldSetPanResponderCapture: (evt, gestureState) => true, onPanResponderGrant: (evt, gestureState) => { // 開始手勢操作。給用戶一些視覺反饋,讓他們知道發生了什么事情! // gestureState.{x,y}0 現在會被設置為0 console.log('onPanResponderGrant'); }, onPanResponderMove: (evt, gestureState) => { // 最近一次的移動距離為gestureState.move{X,Y} let x=(self._animatedThumbLeft._value+gestureState.dx); console.log('x-->'+x); if(x>17){ x=17 } if(x<0){ x=0 } self._animatedThumbLeft.setValue(x); // 從成為響應者開始時的累計手勢移動距離為gestureState.d{x,y} }, onPanResponderTerminationRequest: (evt, gestureState) => true, onPanResponderRelease: (evt, gestureState) => { // 用戶放開了所有的觸摸點,且此時視圖已經成為了響應者。 // 一般來說這意味著一個手勢操作已經成功完成。 console.log('onPanResponderRelease'); if(gestureState.dx==0){ self._onPress(); }else{ if(self._animatedThumbLeft._value<8.5){ this.state.check=true; }else{ this.state.check=false; } self._onPress(); } }, onPanResponderTerminate: (evt, gestureState) => { // 另一個組件已經成為了新的響應者,所以當前手勢將被取消。 }, onShouldBlockNativeResponder: (evt, gestureState) => { // 返回一個布爾值,決定當前組件是否應該阻止原生組件成為JS響應者 // 默認返回true。目前暫時只支持android。 return true; }, }); } render() { return ( <View style={{marginTop: 20}} {...this._panResponder.panHandlers} > <TouchableWithoutFeedback onPress={this._onPress.bind(this)} > <View ref={(ref)=>{ this.container=ref; }}onCheckChangeListener style={[styles.container,{backgroundColor:this._getBgColor()}]} > <Thumb style={[{ width:14*2, height:14*2, left:this._animatedThumbLeft, top:0, alignItems:'center' }]} /> </View> </TouchableWithoutFeedback> </View> ); } _onPress() { this.state.check=!this.state.check; this.props.onCheckChangeListener&&this.props.onCheckChangeListener(this.state.check); this._transplateX() } _getBgColor() { if (this.state.check) { return PASwitch2.defaultProps.checkedColor; } else { return PASwitch2.defaultProps.unCheckColor; } } _transplateX() { Animated.timing(this._animatedThumbLeft, { toValue: this.state.check ? 45 - 28 : 0, duration: 200 }).start(()=> { }); }}PASwitch2.defaultProps = { checkedColor: 'rgba(76,231,99,1)', unCheckColor: 'transparent'}const styles = StyleSheet.create({ container: { width: 45, height: 28, borderRadius: 14, borderWidth: 1, borderColor: '#cccccc', justifyContent: 'center' }});用法:
1、引入view
import Switch from '../Common/PASwitch2';2、render view
<Switch ref={'switch2'} onCheckChangeListener={(isCheck)=>{ if(isCheck){ Store.dispatch(showSnackBar('是')) }else{ Store.dispatch(showSnackBar('否')) } }} />最后附上仿android snackbar雙平臺的代碼: SnackBar.js:
/** * snackbar 4 ios and android */import React,{Component}from 'react';import { View, Text, Touchable, StyleSheet, Dimensions, Animated, TouchableOpacity}from 'react-native';export const DURATION = {LENGTH_LONG: 2000, LENGTH_SHORT: 1000};const {height, width} = Dimensions.get('window');import {getStatusHeight}from '../../Constant/AppBase';export default class SnackBar extends Component { state = { isShowing: false, showAnimated: new Animated.Value(0), textString: 'Default Click!', height: 0, start: -100, end: -100 } render() { let view = null; let containerSyle = {}; if (this.state.isShowing) { containerSyle = { position: 'absolute', left: 0, top: 0, right: 0, transform: [ { translateY: this.state.showAnimated.interpolate({ inputRange: [0, 1], outputRange: [this.state.start, this.state.end] }) } ] }; view = <Animated.View style={[styles.container,containerSyle]} onLayout={(event)=>{ this.setState({ height:event.nativeEvent.layout.height }); }} > <TouchableOpacity> <Text style={styles.text} > {this.state.textString} </Text> </TouchableOpacity> </Animated.View>; } return view; } show(text:string) { this.timer && clearTimeout(this.timer); this.isShow = false; this.state.showAnimated.setValue(0); this.props.text = text; this.setState({ isShowing: true, textString: text, start: -100, end: 0 }); Animated.timing( this.state.showAnimated, { toValue: 1, duration: DURATION.LENGTH_SHORT } ).start(()=> { this.isShow = true; this.close(); }); } close() { if (!this.isShow) return; this.timer && clearTimeout(this.timer); this.timer = setTimeout(() => { this.setState({ start: 0, end: -this.state.height }); this.state.showAnimated.setValue(0); Animated.timing( this.state.showAnimated, { toValue: 1, duration: DURATION.LENGTH_SHORT } ).start(() => { this.setState({ isShowing: false, }); }); }, 2000); }}const styles = StyleSheet.create({ container: { backgroundColor: 'rgba(0,0,0,0.5)', padding: 13, paddingTop:8+getStatusHeight() }, text: { color: 'white' }});使用方式:
1、引入snackbar
import SnackBar from './Common/SnackBar';2、在你布局的最下方添加snackbar
render() { return ( <View style={{flex:1}}> <TabNavigator> {/*--首頁--*/} {this._renderItem('首頁', 'icon_tabbar_homepage', 'icon_tabbar_homepage_selected', 'home', Home)} {/*--商家--*/} {this._renderItem('商家', 'icon_tabbar_merchant_normal', 'icon_tabbar_merchant_selected', 'shop', Store)} {/*--我的--*/} {this._renderItem('我的', 'icon_tabbar_mine', 'icon_tabbar_mine_selected', 'mine', Mine, '10')} {/*--更多--*/} {this._renderItem('更多', 'icon_tabbar_misc', 'icon_tabbar_misc_selected', 'more', More)} </TabNavigator> <LoadingView/> <SnackBar ref={(ref)=>this.snackBar=ref} /> </View> ); }3、獲取snackbar引用,調用show方法
this.snackBar.show(...);兩個view都沒有進行封裝跟優化,小伙伴們如果需要集成到項目中的話,自己拿到代碼去修改下里面的參數。不明白的地方也可以聯系我,嘻嘻~!!!!
新聞熱點
疑難解答