前言:终于开始做接触公司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都没有进行封装跟优化,小伙伴们如果需要集成到项目中的话,自己拿到代码去修改下里面的参数。不明白的地方也可以联系我,嘻嘻~!!!!
新闻热点
疑难解答