react-native开发React Native实践

使用React Native封装原生手势解锁组件

2016-11-05  本文已影响2093人  RemisKrlet

(前言)

例子的Demo在这里由于本人工 (不) 作 (会) 忙 (Android开发),因此下文中的原生组件以iOS为例,Android原生组件也会考虑在后面补上。(本组件支持npm

由于篇幅的原因,本文不作React Native的环境搭建和如何创建项目的介绍(我没有前端开发经历哦,相信这点起步也同样难不倒你的),尚未了解的朋友可以参考以下介绍。文中的例子参考了React Native 官网的原文例子,原谅我手笨,尽管有公司前端同事的大力相助,这个小Demo依然做的磕磕碰碰,Anyway,在经过了两个晚上的不懈努力之后,我完成了预定目标,学到了不少有用的东西。对于这些我决定将自己的总结和教训分享出来,方便大家的学习,促进技术交流。

如何搭建React Native环境

React-Native开发的相关知识了解,前端开发的基本概念入门,包括框架介绍,控件布局,样式,网络请求,更多资源参考

为什么要做原生组件的封装呢?React Native为我们提供了很多现成组件,比如ScrollView,Navigator,TextInput;但是在实际开发中,我们许多时候中会遇到React Native没有为我们备好的组件的需求,而原生项目中已经有在用的控件了,后者往往来自于第三方库或者App自带。如果能把这些控件用JS封装好,并应用于React Native项目中,在性能上要优于后者,体验也更流畅;而React Native为我们提供了这样的入口,这是非常好的。

(一些有用的接口)

//RCTGestureViewManager.m
/**

RCT_EXPORT_MODULE(): 将本组件放入模块中方便JS的导入,在原生中它就是class
RCT_EXPORT_METHOD(): 将一些方法导出,方便JS调用。这个宏的参数中包括JS方法名,方法的参数。参数支持 string,
number, bool, function等JS类型
RCT_EXPORT_VIEW_PROPERTY(): 属性导出给JS,参数为属性的名称和类型。代码如下:

*/

//export modules methods and properties
RCT_EXPORT_MODULE() //default name is RNGesutreView

RCT_EXPORT_VIEW_PROPERTY(onComplete, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(nodeScale, CGFloat)
RCT_EXPORT_VIEW_PROPERTY(colCount, int)
RCT_EXPORT_VIEW_PROPERTY(backgroundImgName, NSString)

RCT_EXPORT_METHOD(setupNodeUI:(NSDictionary *)params) {
  NSString *nodeNormalImg = params[@"nodeNormal"];
  NSString *nodeErrorImg = params[@"nodeError"];
  NSString *nodeSelectImg = params[@"nodeSelect"];
  self.innerView.nodeErrorImgName = nodeErrorImg;
  self.innerView.nodeNormalImgName = nodeNormalImg;
  self.innerView.nodeSelectedImgName = nodeSelectImg;
}

为了方便UI组件和JS的交互,我们创建了RCTGestureViewManager,作为原生组件的代理,负责将组件的属性,方法,组件类导出给JS,向JS发送事件,并接收后者的回调,方法调用。RCTGestureViewManager需要继承于RCTViewManager,相应的部分JS代码则是:

//RCTGestureView.ios.js

const GestureManager = NativeModules.RNGestureViewManager
const NativeGestureView = requireNativeComponent('RNGestureView', RNGestureView);
export default class RNGestureView extends Component {

    _onGestureComplete(event) {
        if (!this.props.onGestureComplete) {
            return;
        }
        this.props.onGestureComplete(event.nativeEvent.completion)
    }

    componentDidMount() {
        if (this.props.nodeThemes) {
            GestureManager.setupNodeUI(this.props.nodeThemes);
        }
    }

    static propTypes = {
        nodeScale: PropTypes.number,
        colCount: PropTypes.number,
        onGestureComplete:PropTypes.func,
        backgroundImgName:PropTypes.string,
        nodeThemes: PropTypes.shape({
            nodeNormal: PropTypes.string,
            nodeError: PropTypes.string,
            nodeSelect: PropTypes.string
        })
    }

    render() {
        return (
            <NativeGestureView 
            {...this.props} onComplete = {this._onGestureComplete.bind(this)}/>
            //(event)=>this.props.onGestureComplete(event.nativeEvent.completion)
        );
    }
}

其中,onComplete是原生组件暴露出来的调用 JS 完成特定事件的方法也就是OC中的回调 block,在手势解锁完成后,调用JS中的同名方法,并把手势连接的密码作为参数传递给 JS。

另外,原生暴露出来的属性和方法在JS代码中需要相应的列出。方便JS这边的封装组件和原生进行参照比对。其中的nodeScale等四个字段都是RCTGestureView同名属性propTypesRNGestureView的类成员,负责完成以上的工作。

为了检验封装是否成功,我们进行简单的测试,测试的部分代码如下:

//index.ios.js

class gestureUnlock extends Component {
  constructor() {
    super()
    this.state= {
      textContent: 'test for callback from native module'
    }
  }
  componentDidMount() {
    GestureViewManager.setupNodeUI({
      nodeNormal: 'gesture_node_normal',
      nodeSelect: 'gesture_node_highlighted'
    });
  }
  render() {
    return (
      <View style={styles.container}>
        <GestureView style={styles.gesture} nodeScale={74} colCount={3} 
        backgroundImgName='Home_refresh_bg' onGestureComplete={(result)=>{
          console.log('result', result);
          this.setState({
          textContent:result==null? 'callback failed':result
        })}}/>
        <Text style={styles.passcode}>
          {this.state.textContent}
        </Text>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    backgroundColor: 'blue',
  },
  gesture:{
    flex: 1,
    justifyContent: 'center',
    // backgroundColor: 'yellow',
  },
  passcode: {
    fontSize: 20,
    textAlign: 'center', 
    margin: 10,
    color: 'white'
  },
});

运行中和手势动作结束后的效果:


componentDidMount是声明周期方法,对React Native中的对象运行的生命周期感兴趣的朋友可以参考说明

(结束语)

在本例中,原生向JS发送事件是通过RCTBubblingEventBlock,这是封装好的可以在OC中使用的 block。当调用时,会调用JS中的同名方法。

另外一个发送事件的方法是使用eventDispatcher,并在JS代码中注册监听同名的事件。至于这两个方法在封装原生控件这一操作上哪个更好,我还没有得到适合的答案,希望对此熟悉的朋友可以告诉我。

在这里推荐一个不错的学习React Native的网站,收集了架构,数据流,应用状态的监听等资料介绍。

上一篇 下一篇

猜你喜欢

热点阅读