React Native 仿ios swtich(双平台)

来源:互联网 发布:显示器品牌知乎 编辑:程序博客网 时间:2024/06/06 05:22

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

0 0