ReactNative自定义控件之 RefreshLayout
来源:互联网 发布:ubuntu怎么安装wps 编辑:程序博客网 时间:2024/05/18 02:57
ReactNative自定义控件之 RefreshLayout
1 自定义下拉刷新控件
//自定义的下拉刷新控件public class PullToRefreshView extends ViewGroup { ... public PullToRefreshView(Context context) { ... } public void setRefreshing(boolean refreshing) { ... } public void setOnRefreshListener(OnRefreshListener listener) { ... }}
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); } ...}
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 部分的代码。
4 使用@ReactProp 注解导出属性的设置方法
这部分内容官方文档的介绍足够使用了,这里不再细说。
public class SwipeRefreshViewManager extends ViewGroupManager<PullToRefreshView>{ ... @ReactProp(name = "refreshing") public void setRefreshing(PullToRefreshView view, boolean refreshing) { view.setRefreshing(refreshing); }}
5 将 ViewManager 注册到应用
如果你熟悉 Android 的 React Native 集成的话,你只需要将 SwipeRefreshViewManager 添加到 ReactPackage 中即可
public class MainPackage implements ReactPackage { ... @Override public List<ViewManager> createViewManagers(ReactApplicationContext reactApplicationContext) { return Arrays.asList(new SwipeRefreshViewManager()); } ...}
6 实现下拉刷新组件
还记得吗,在 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};
7 下拉刷新组件的使用
说到使用就太简单了,虽然简单但仍然要说,我们知道官方提供的组件例如 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> ) }}
8 我就在想既然可以通过 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 ( ... );})
9 基于以上的分析以及我们对于属性的封装,我们的写法也相当的原味:
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> ) }}
- ReactNative自定义控件之 RefreshLayout
- ReactNative自定义控件。
- RefreshLayout
- ReactNative进阶之评分控件的封装
- ReactNative自定义控件状态更新的正确姿势
- ReactNative TextView控件
- ReactNative Image控件
- [ReactNative]自定义alert对话框
- ReactNative 自定义导航栏
- 自定义控件之翻页控件
- 自定义控件之组合控件
- 自定义控件之组合控件
- 自定义控件之组合控件
- 自定义控件之自定义属性
- 自定义控件之自定义开关
- 自定义控件之自定义xmlns
- Android自定义控件之自定义日历控件
- Android自定义控件之自定义组合控件
- 生产者消费者问题
- Java算法基础之快速排序算法
- ubuntu 14.04 安装虚拟机12.1.0
- 《平凡的世界》读感
- CSS中包含块
- ReactNative自定义控件之 RefreshLayout
- poj 2533 Longest Ordered Subsequence (最长不下降子序列)
- JavaScript的一些基本知识
- 条件语句
- HDU 3849 By Recognizing These Guys, We Find Social Networks Useful 找出所有割边
- Ubantu 14.04 安装 PCL
- 成为优秀Java程序员的10个要点
- 字符串常量作为模板参数的陷阱
- 8. String to Integer (atoi)