用法
-
JS端通过ref直接操作组件内方法
import { findNodeHandle } from 'react-native' <ScrollView ref={ref => { this._scrollview = ref; }}> </ScrollView> // 滚动到指定位置 this._scrollview.scrollTo(x, y, animated)
-
直接操作原生模块内的方法
通过findNodeHandle
获取_nativeTag
并赋值给this._handle
import { NativeModules, findNodeHandle } from 'react-native' <ScrollView ref={ref => { this._handle = findNodeHandle(ref) }}> </ScrollView> // 滚动到指定位置 NativeModules.ScrollViewModule.scrollTo(this._handle, x, y, animated)
示例代码
这里以
<ScrollView>
组件封装设计为例,介绍如何一步一步实现原生模块
index.js
import { NativeModules, findNodeHandle } from 'react-native'
const ScrollViewClass = NativeModuls.ScrollViewModule
class ScrollView extends React.Component {
_setScrollViewRef=(ref)=>{
this._scrollViewRef = findNodeHandle(ref)
}
scrollTo=({x, y, animated})=>{
// 这里有两种方式实现🎉🌟🎉🌟🎉🌟
// 详见下面代码
}
render(){
return (
<ScrollViewClass {...props} ref={this._setScrollViewRef}>
)
}
}
方式一:UIManager的Command模式
JavaScript
import { UIManager } from 'react-native'
scrollTo=()=>{
UIManager.dispatchViewManagerCommand(
this._setScrollViewRef, // 告诉原生需要定位到的组件
UIManager.RCTScrollView.Commands.scrollTo, // 原生getCommandsMap提前定义好的命令
[x, y, animated], // 携带的参数ReadableArray
)
}
获取命令常量这里建议使用
UIManager.RCTXXX.Commands.*
RN0.60+版本可以用UIManager.getViewManagerConfig('RCTXXX').Commands
但不兼容老版本
1.1 Android实现过程
1.1.1 ScrollViewManager.class
继承GroupManager.class
/ViewManager.class
,在这里重写getCommandsMap()
和receiveCommand()
方法
class ScrollViewManager extends GroupManager {
public static final String REACT_CLASS = "RCTScrollView";
public static final int COMMAND_SCROLL_TO = 1;
public static final int COMMAND_SCROLL_TO_END = 2;
@Override
public String getName() {
return REACT_CLASS;
}
/**
* 重写getCommandsMap
*
* @return map 返回为receiveCommand注册的命令集合
**/
@Override
public Map<String,Integer> getCommandsMap() {
Map<String, Integer> map = new HashMap<>();
map.put("scrollTo", COMMAND_SCROLL_TO);
map.put("scrollToEnd", COMMAND_SCROLL_TO_END);
return map;
}
/**
* 重写receiveCommand
*
* @param root 当前接收命令的ViewManager实例
* @param commandId 命令id,与getCommandMap内定义的对应
* @param args 可选参数
**/
@Override
public void receiveCommand(
ScrollViewManager root,
int commandId,
@Nullable ReadableArray args) {
switch (commandId) {
case COMMAND_SCROLL_TO:{
root.scrollTo(
(int)args.getDouble(0),
(int)args.getDouble(1),
args.getBoolean(2))
}
case COMMAND_SCROLL_TO_END:{
boolean animated = args.getBoolean(0);
root.scrollToEnd(animated)
}
}
}
}
1.2 iOS实现过程
1.2.1 RCTScrollViewManageer.h
// .h
#import <React/RCTViewManager.h>
@interface RCTScrollViewManager : RCTViewManager
@end
1.2.1 RCTScrollViewManageer.m
也是使用addUIBlock
固定写法。
不过和android不同,iOS可以直接在ViewManager中使用RCT_EXPORT_METHOD()
,并且指定方法也会自动注册到Commands,不像android需要手动重写getCommandsMap
// .m
#import "RCTScrollViewManager.h"
@implementation RCTScrollViewManager
RCT_EXPORT_MODULE()
//注册scrollTo方法
RCT_EXPORT_METHOD(scrollTo:(nonnull NSNumber *)reactTag
offsetX:(CGFloat)x
offsetY:(CGFloat)y
animated:(BOOL)animated)
{
[ self.bridge.uiManager addUIBlock:
^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry)
{
//通过tag获取到当前view实例
UIView *view = viewRegistry[reactTag];
// 调用ios系统组件自带方法
if ([view conformsToProtocol:@protocol(RCTScrollableProtocol)]) { //加保护判断是不是scrollview
[(id<RCTScrollableProtocol>)view scrollToOffset:(CGPoint){x, y} animated:animated];
}
}
]
}
方式二: 原生模块与原生UI组件结合使用
JavaScript
直接调用ReactMethod / RCT_EXPORT_METHOD 原生方法
import { NativeModules } from 'react-native'
scrollTo=()=>{
NativeModules.ScrollViewModule.scrollTo(this._setScrollViewRef, x, y, animated)
}
2.1 Android实现过程
2.1.1 ScrollViewManager.class
和上面1.1.1一样,但不再重写getCommandsMap()
和receiveCommand()
方法
2.1.2 ScrollViewModule.class
需要额外声明ScrollViewModule
类继承ReactContextBaseJavaModule.class
,
在这里创建scrollTo()
ReactMethod方法
public class ScrollViewModule extends ReactContextBaseJavaModule {
@Override
public String getName() {
return "ScrollViewModule";
}
public ScrollViewModule(ReactApplicationContext reactContext) {
super(reactContext);
}
/**
* 创建scrollTo方法
*
* @param viewTag 由findNodeHandle创建
* the view tag of the parent view
*
* @param x
* @param y
* @param animated
**/
@ReactMethod
public void scrollTo(final int viewTag, int x, int y, boolean animated) {
final ReactApplicationContext context = getReactApplicationContext();
//固定写法:通过拓展uiManager实现定位ViewManager
UIManagerModule uiManager = context.getNativeModule(UIManagerModule.class);
uiManager.addUIBlock(new UIBlock() {
@Override
public void execute(NativeViewHierarchyManager nativeViewHierarchyManager) {
final ScrollView scrollview;
try {
scrollview = (ScrollView) nativeViewHierarchyManager.resolveView(viewTag);
// 调用自带的scrollTo方法
scrollview.scrollTo(x, y, animated);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
2.1.2 ScrollViewPackage.class
在ReactPackage
中同时注册createNativeModules
和createViewManagers
public class ScrollViewPackage implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
return Arrays.<NativeModule>asList(
new ScrollViewModule(reactContext) // <- add here
);
}
// Deprecated as of RN 0.47.0
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
List<ViewManager> modules = new ArrayList<>();
modules.add(new ScrollViewManager(reactContext)); // <- add here
return modules;
}
}
2.2 iOS实现过程
同1.2
2.3 iOS的RCT_EXPORT_METHOD
宏和android的@ReactMethod
注释区别
// iOS native
RCT_EXPORT_METHOD(scrollTo, ...)
RCT_EXPORT_METHOD(scrollToEnd, ...)
// android native
@ReactMethod
public void scrollTo(int viewTag ...)
// index.js
console.log(UIManager.RCTScrollView)
console.log(NativeModules.ScrollViewManager)
JavaScript打印结果 | iOS | android |
---|---|---|
UIManager.RCTScrollView.Commands | {scrollTo: 1, scrollToEnd: 2} |
undefined |
NativeModules.ScrollViewManager | {scrollTo: fn(), scrollToEnd: fn() } |
{scrollTo: fn(), scrollToEnd: fn() } |
iOS会同时自动注册到Commands和NativeModule方法集合中,而
android是用@ReactMethod
和getCommandMap()
分开注册的。
其他:measure文档补完
老版本:
import {
...
findNodeHandle,
} from 'react-native';
var RCTUIManager = require('NativeModules').UIManager;
var view = this.refs['yourRef']; // Where view is a ref obtained through <View ref='ref'/>
RCTUIManager.measure(findNodeHandle(view), (fx, fy, width, height, px, py) => {
console.log('Component width is: ' + width)
console.log('Component height is: ' + height)
console.log('X offset to frame: ' + fx)
console.log('Y offset to frame: ' + fy)
console.log('X offset to page: ' + px)
console.log('Y offset to page: ' + py)
})
现可直接使用
this.refs['yourRef'].measure(findNodeHandle(view), (fx, fy, width, height, px, py) => {
console.log('Component width is: ' + width)
console.log('Component height is: ' + height)
console.log('X offset to frame: ' + fx)
console.log('Y offset to frame: ' + fy)
console.log('X offset to page: ' + px)
console.log('Y offset to page: ' + py)
})