移动APP上用的最多的功能之一可能就是下拉刷新了,今天就用 React Native
来封装一个基于android-Ultra-Pull-To-Refresh的下拉刷新控件。
封装流程
- 继承
SimpleViewManager
或ViewGroupManager
创建视图管理器类; - 继承
ReactPackage
创建自定义ReactPackage,重写createNativeModules
、createJSModules
、createViewManagers
这三个方法,返回类型都是 List 类型, 在createViewManagers
中添加我们创建好的视图管理器类到集合中,另外两个方法可以返回空 List 集合; - 在Application 中添加自定义的 ReactPackage;
- JS 层封装自定义 UI 控件。
首先继承ViewGroupManager<T>
创建一个ReactPtrAndroidManager
,ViewGroupManager
所需的泛型类型是一个 ViewGroup
,在这里就是我们的PtrClassicFrameLayout
(我这里直接使用这个库提供的经典的下拉刷新样式),然后重写以下方法:
getName
:返回这个视图管理器的名称,这个名称将被用于在 JavaScript 中创建 RN 组件时所引用。简单地说就是 JS 层创建的刷新组件将通过这个名字来和 Java 层起到映射关系。createViewInstance
:返回一个和泛型类型一致的View
实例。在这里就是返回PtrClassicFrameLayout
类型的实例。getCommandsMap
:返回想要接收的指令的集合。简单地说就是如果你希望 Java 层接受某个指令,就在这个方法里添加进去,比方说我们这个例子,我想在 Java 层处理自动刷新、刷新完成,那就把自动刷新的Key,Value和刷新完成的 Key,Value添加到 Map 集合中。然后在 JS 层就可以通过调用UIManagerModule
类中的dispatchViewManagerCommand
方法,传入对应的指令ID 来调用 Java 层的事件。在 RN 中就是这样实现在 JS 层调用 Java 层的代码。receiveCommand
:这个和getCommandsMap
是对应的,处理接收到对应指令后的逻辑。我们在getCommandsMap
中已经添加了一些指令的集合,并且在 JS 层可以通过dispatchViewManagerCommand
来发送对应的指令,然后在 Java 层处理相应的逻辑,那我们怎么知道 JS 层到底发送了什么指令?其实就是在这个方法中来接收 JS 层发出的指令。addEventEmitters
:重写此方法给给定的视图安装自定义事件发射器。如果这个视图需要发射除了基本的触摸事件以外的事件,那么就需要重写这个方法。这个例子中在开始刷新的回调方法中发送一个自定义的事件ptrRefresh
给 JS 层,getExportedCustomDirectEventTypeConstants
:返回 传递给JS的 定义可以放置在原生视图上的 符合条件的事件 的配置数据的 map 集合(定语比较多(╯‵□′)╯︵┻━┻), 这应该返回非冒泡直接调度的事件类型。反正我是没看懂。。。以下是个人理解,我们注册了onPtrRefresh事件,这个事件是给JS 层使用的,JS 层在onPtrRefresh中执行刷新的逻辑(请求网络数据等),然后回调给 Java 层。正常NativeApp需要在onRefreshBegin里执行刷新逻辑, 使用 RN 就可以这样在 JS 层上执行刷新逻辑了。JS 层 onPtrRefresh –> Java 层ptrRefresh
–>onRefreshBegin
。addView
:为什么要重写addView
呢?如果看过PtrFrameLayout
源码就知道,是在onFinishInflate
中findViewById
并初始化contentView
和headerView
的,可是 RN 根本不走onFinishInflate
方法,因为onFinishInflate
只是在从XML
加载完成布局之后才调用(PS:最初我在createViewInstance
方法中是inflate
XML
布局的,打Log是走onFinishInflate
的。。。都是泪😭)。并且在调用addView
的时候,PtrClassicFrameLayout
中已经存在headerView
了,在PtrClassicFrameLayout
的构造方法中添加的。然后调用父类addView
的时候,是从 Index 为0开始添加的,很显然逻辑就不对了,我们要把onFinishInflate
中的代码稍微修改一下并且抽出来写成公有方法finishInflateRN
,供外部调用。然后我们重写addView 把contentView 添加在 Index 为1的位置,并且调用一下finishInflateRN
就好了。
最后就是暴露属性,让 JS 层可以使用这些属性,方法使用ReactProp
注解,name
就是 JS 的属性,还可以设置一些默认值。
代码片段
1 | public class ReactPtrAndroidManager extends ViewGroupManager<PtrClassicFrameLayout> { |
1 | public class PtrEvent extends Event<PtrEvent> { |
1 | import React, { |
然后我们就可以使用PtrComponent
了:
1 | import React, { |