可能是最in的React Native使用原生自定义下拉刷新组件
2017-01-25 22:04
791 查看
在 2016 年移动端跨平台开发是几个最热的技术之一,相信在 2017 年这股热潮将持续发酵。为什么这么说呢,因为随着业务的爆发式增长,传统的原生开发模式有点显得跟不上节奏了,这也促使各个公司希望寻找到一个更加高效的开发方案,当下可以被选择的方案中,
闲话不多说,这里的主要目的是跟大家聊聊
听说流行有图有真相,那先来个在
适配
UI Components ,如果网络不方便的话也可以参看翻译版 原生UI组件 。
这里就不讲如何自定义
官方文档中给我们的示例是创建
到这里一个简单的
但我们这是一个下拉刷新控件,有一个问题是我们如何将下拉刷新的监听事件传递给
覆写
我们将事件封装为
细心地你肯定发现了
JavaScript 端映射到
关于组件这部分大家可以参看
这部分内容官方文档的介绍足够使用了,这里不再细说。
如果你熟悉
到这里
接下来我们来聊一聊使用
API 有一定的了解。
还记得吗,在
说到使用就太简单了,虽然简单但仍然要说,我们知道官方提供的组件例如
我就在想既然可以通过
Facebook 的工程师们的妙笔生花。
基于以上的分析以及我们对于属性的封装,我们的写法也相当的原味:
希望你能有所收获,本文完!
React Native及
Weex都是不错的技术方案。在年前团队内部的一场
React Native vs Weex的技术对垒中本来我选择的是
Weex的阵营,但当时在多维的技术指标中新生的
Weex还是不敌
React Native,团队内部最终敲定了采用
React Native跨平台方案。http://www.tuicool.com/articles/ERjyyiU
1、概述
闲话不多说,这里的主要目的是跟大家聊聊 React Native在
Android平台使用原生自定义
View,这里默认大家对
React Native已经有一定的了解,
React Native中的组件都是基于
iOS/Android的官方组件进行封装,所以在一些特别的场景下并不能很好的满足需求。正如标题中的下拉刷新组件,
React Native在
Android平台采用的是
android.support.v4.widget.SwipeRefreshLayout,一些
iOS设计优先的团队(譬如我司)而言对于
Android开发人员简直就是灾难。在众多开源的
React Native项目中大家也不会再这些细节上较真,但是公司的
UED这关可不好过。
听说流行有图有真相,那先来个在
iOS端经典的菊花图的
Android
reac-native版:
2、Android 端的支持实现
适配 Android平台的原生组件可以参看官方文档 Native
UI Components ,如果网络不方便的话也可以参看翻译版 原生UI组件 。
2.1 自定义下拉刷新控件
这里就不讲如何自定义 Android控件,假设你是一位有一定经验的开发人员。
//自定义的下拉刷新控件 public class PullToRefreshView extends ViewGroup { ... public PullToRefreshView(Context context) { ... } public void setRefreshing(boolean refreshing) { ... } public void setOnRefreshListener(OnRefreshListener listener) { ... } }
2.2 创建 ViewManager 的实现类
官方文档中给我们的示例是创建 SimpleViewManager的实现类,但此处的下拉刷新控件是个
ViewGroup,所以此处实现类应继承
ViewManager的另一个子类
ViewGroupManager。
public class SwipeRefreshViewManager extends ViewGroupManager<PullToRefreshView>{ @Override public String getName() { return "PtrLayout"; } @Override protected PullToRefreshView createViewInstance(ThemedReactContext reactContext) { return new PullToRefreshView(reactContext); } ... }
到这里一个简单的
ViewGroupManager就实现了。
2.3 给 ViewManager 添加事件监听
但我们这是一个下拉刷新控件,有一个问题是我们如何将下拉刷新的监听事件传递给 JavaScript呢?官方文档中写的并不清晰,还是翻阅源码吧,果不其然在源码中寻找到了我们想要的答案。
覆写
addEventEmitters函数将事件监听传递给
JavaScript。
public class SwipeRefreshViewManager extends ViewGroupManager<PullToRefreshView>{ ... @Override protected void addEventEmitters(ThemedReactContext reactContext, PullToRefreshView view) { view.setOnRefreshListener(new PullToRefreshView.OnRefreshListener() { @Override public void onRefresh() { reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher() .dispatchEvent(new PtrRefreshEvent(view.getId())); } }); } @Nullable @Override public Map<String, Object> getExportedCustomDirectEventTypeConstants() { return MapBuilder.<String, Object>builder() .put("topRefresh", MapBuilder.of("registrationName", "onRefresh")) .build(); } ... }
我们将事件封装为
PtrRefreshEvent。
public class PtrRefreshEvent extends Event<PtrRefreshEvent>{ protected PtrRefreshEvent(int viewTag) { super(viewTag); } @Override public String getEventName() { return "topRefresh"; } @Override public void dispatch(RCTEventEmitter rctEventEmitter) { rctEventEmitter.receiveEvent(getViewTag(),getEventName(),null); } }
细心地你肯定发现了
getExportedCustomDirectEventTypeConstants这个函数,这里先说明一下,覆写该函数,将
topRefresh这个事件名在
JavaScript 端映射到
onRefresh回调属性上,这部分我们后面会在结合 JavaScript 再解释下用法。
关于组件这部分大家可以参看
React Native的
Android部分的代码。
2.4 使用@ReactProp 注解导出属性的设置方法
这部分内容官方文档的介绍足够使用了,这里不再细说。public class SwipeRefreshViewManager extends ViewGroupManager<PullToRefreshView>{ ... @ReactProp(name = "refreshing") public void setRefreshing(PullToRefreshView view, boolean refreshing) { view.setRefreshing(refreshing); } }
2.5 将 ViewManager 注册到应用
如果你熟悉 Android的
React Native集成的话,你只需要将
SwipeRefreshViewManager添加到
ReactPackage中即可,
public class MainPackage implements ReactPackage { ... @Override public List<ViewManager> createViewManagers(ReactApplicationContext reactApplicationContext) { return Arrays.asList(new SwipeRefreshViewManager()); } ... }
到这里
Android端的实现已经全部完成了。
3、React/JS 端的组件实现及使用
接下来我们来聊一聊使用 React实现下拉刷新的组件,当然在这之前期望你对
jsx/es6的语法及
react/react-native的
API 有一定的了解。
3.1 实现下拉刷新组件
还记得吗,在 Android我们通过
SwipeRefreshViewManager中
getName返回的控件名称,将会在这里用于引用这个原生控件。
'use strict'; import React, {Component, PropTypes} from 'react'; import {View, requireNativeComponent} from 'react-native'; import NativeMethodsMixin from 'react/lib/NativeMethodsMixin'; import mixin from 'react-mixin'; //引用原生下拉刷新控件 const NativePtrView = requireNativeComponent('PtrLayout', PtrView); //封装一个react组件,该组件中引用了原生控件的实现 class PtrView extends Component { static propTypes = { ...View.propTypes, onRefresh: PropTypes.func, refreshing: PropTypes.bool.isRequired }; _nativeRef = (null: ?PtrView); _lastNativeRefreshing = false; constructor(props) { super(props); } componentDidMount() { this._lastNativeRefreshing = this.props.refreshing; } componentDidUpdate(prevProps = {refreshing: false}) { if (this.props.refreshing !== prevProps.refreshing) { this._lastNativeRefreshing = this.props.refreshing; } else if (this.props.refreshing !== this._lastNativeRefreshing) { this._nativeRef.setNativeProps({refreshing: this.props.refreshing}); this._lastNativeRefreshing = this.props.refreshing; } } //渲染原生下拉刷新控件,这里onRefresh就是在ViewManager::getExportedCustomDirectEventTypeConstants //这个函数中 topRefresh 的映射属性。 render() { return ( <NativePtrView {...this.props} ref={ref => this._nativeRef = ref} onRefresh={this._onRefresh.bind(this)}/> ) } _onRefresh() { this._lastNativeRefreshing = true; this.props.onRefresh && this.props.onRefresh(); this.forceUpdate(); } } mixin.onClass(PtrView, NativeMethodsMixin); export {PtrView};
3.2 下拉刷新组件的使用
说到使用就太简单了,虽然简单但仍然要说,我们知道官方提供的组件例如 ListView中通过
refreshControl来指定刷新控制器,用法是这样的:
class Demo1 extends Component { ... render() { return ( <View style={{flex: 1}}> <ListView ... refreshControl={ <RefreshControl refreshing={this.state.refreshing} onRefresh={this._refresh.bind(this)} /> } /> </View> ) } }
我就在想既然可以通过
refreshControl来指定刷新控制器,那我自定义的下拉刷新组件是不是也可以通过
refreshControl来指定呢?带着这样的疑问,我仔细读了读
ListView/ScrollView的源码,发现这个猜想还是蛮靠谱的,也赞叹
Facebook 的工程师们的妙笔生花。
const ScrollView = React.createClass({ let ScrollViewClass; if (Platform.OS === 'ios') { ScrollViewClass = RCTScrollView; } else if (Platform.OS === 'android') { if (this.props.horizontal) { ScrollViewClass = AndroidHorizontalScrollView; } else { ScrollViewClass = AndroidScrollView; } } ... const refreshControl = this.props.refreshControl; if (refreshControl) { if (Platform.OS === 'ios') { ... } else if (Platform.OS === 'android') { // On Android wrap the ScrollView with a AndroidSwipeRefreshLayout. // Since the ScrollView is wrapped add the style props to the // AndroidSwipeRefreshLayout and use flex: 1 for the ScrollView. // 此处就是重点,通过 cloneElement 创建一个新的 ReactElement,而 refreshControl 是通过 props 指定而来并没有写死,Good! return React.cloneElement( refreshControl, {style: props.style}, <ScrollViewClass {...props} ref={this._setScrollViewRef}> {contentContainer} </ScrollViewClass> ); } } return ( ... ); })
基于以上的分析以及我们对于属性的封装,我们的写法也相当的原味:
class Demo2 extends Component { ... render() { return ( <View style={{flex: 1}}> <ListView ... refreshControl={ //这里为了保证只在Android平台上使用该组件,如果iOS端也有原生控件的实现, //那就不必考虑平台了。 Platform.OS === 'android' ? <PtrView refreshing={this.state.refreshing} onRefresh={this._refresh.bind(this)} /> : <RefreshControl refreshing={this.state.refreshing} onRefresh={this._refresh.bind(this)} /> } /> </View> ) } }
希望你能有所收获,本文完!
相关文章推荐
- react native FlatList使用详解以及上拉刷新下拉加载带可运行demo
- react-native-navigation 原生路由组件的使用
- React native 自定义弹窗(android使用原生ios弹窗)
- react-native自定义原生组件
- React组件自定义属性的定义及使用
- Android中使用react-native框架中的View组件如何使其中的文本换行
- React-Native开发:react-native-image-crop-picker图片上传组件的使用(安卓)
- React-Native开发:react-native-file-selector选择文件组件的使用(安卓)
- 底部导航组件组件react-native-tab-navigator的使用
- 使用 react-native-tab-navigator 创建 TabBar 组件
- react native中利用Picker自定义日期组件(只包含年和月)
- React-Native之轮播组件looped-carousel的介绍与使用
- React-Native之截图组件view-shot的介绍与使用
- react-native-swipe-list-view侧滑删除组件使用
- react_native 项目实战 (4) 自定义分类 使用 CheckBox 以及 数据存储asyncStorage
- React Native 原生平台调用React Native组件
- 安卓开发中实现页面越界回弹和上下拉的刷新自定义动画的操作(TwinklingRefreshLayout布局的使用)
- React Native使用Modal自定义分享界面的示例代码
- (Android)react-native-wechat组件的使用总结(重点微信支付)