Open 1uokun opened 4 years ago
JS端通过ref直接操作组件内方法
import { findNodeHandle } from 'react-native' <ScrollView ref={ref => { this._scrollview = ref; }}> </ScrollView> // 滚动到指定位置 this._scrollview.scrollTo(x, y, animated)
直接操作原生模块内的方法 通过findNodeHandle获取_nativeTag并赋值给 this._handle
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>组件封装设计为例,介绍如何一步一步实现原生模块
<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}> ) } }
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但不兼容老版本
UIManager.RCTXXX.Commands.*
UIManager.getViewManagerConfig('RCTXXX').Commands
继承GroupManager.class/ViewManager.class,在这里重写getCommandsMap()和receiveCommand()方法
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) } } } }
// .h #import <React/RCTViewManager.h> @interface RCTScrollViewManager : RCTViewManager @end
也是使用addUIBlock固定写法。 不过和android不同,iOS可以直接在ViewManager中使用RCT_EXPORT_METHOD(),并且指定方法也会自动注册到Commands,不像android需要手动重写getCommandsMap
addUIBlock
RCT_EXPORT_METHOD()
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]; } } ] }
JavaScript直接调用ReactMethod / RCT_EXPORT_METHOD 原生方法
import { NativeModules } from 'react-native' scrollTo=()=>{ NativeModules.ScrollViewModule.scrollTo(this._setScrollViewRef, x, y, animated) }
和上面1.1.1一样,但不再重写getCommandsMap()和receiveCommand()方法
需要额外声明ScrollViewModule类继承ReactContextBaseJavaModule.class, 在这里创建scrollTo()ReactMethod方法
ScrollViewModule
ReactContextBaseJavaModule.class
scrollTo()
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(); } } }); } }
在ReactPackage中同时注册createNativeModules和createViewManagers
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; } }
同1.2
RCT_EXPORT_METHOD
@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)
{scrollTo: 1, scrollToEnd: 2}
undefined
{scrollTo: fn(), scrollToEnd: fn() }
iOS会同时自动注册到Commands和NativeModule方法集合中,而 android是用@ReactMethod和getCommandMap()分开注册的。
getCommandMap()
老版本:
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) })
yourRef.current.measure((fx, fy, width, height, px, py) => {})
How to get the layout changes of yourRef or multiple refs [yourRefs] e.g. when keyboard hide and shows
用法
JS端通过ref直接操作组件内方法
直接操作原生模块内的方法 通过
findNodeHandle
获取_nativeTag
并赋值给this._handle
示例代码
index.js
方式一:UIManager的Command模式
JavaScript
1.1 Android实现过程
1.1.1 ScrollViewManager.class
继承
GroupManager.class
/ViewManager.class
,在这里重写getCommandsMap()
和receiveCommand()
方法1.2 iOS实现过程
1.2.1 RCTScrollViewManageer.h
1.2.1 RCTScrollViewManageer.m
也是使用
addUIBlock
固定写法。 不过和android不同,iOS可以直接在ViewManager中使用RCT_EXPORT_METHOD()
,并且指定方法也会自动注册到Commands,不像android需要手动重写getCommandsMap
方式二: 原生模块与原生UI组件结合使用
JavaScript
直接调用ReactMethod / RCT_EXPORT_METHOD 原生方法2.1 Android实现过程
2.1.1 ScrollViewManager.class
和上面1.1.1一样,但不再重写
getCommandsMap()
和receiveCommand()
方法2.1.2 ScrollViewModule.class
需要额外声明
ScrollViewModule
类继承ReactContextBaseJavaModule.class
, 在这里创建scrollTo()
ReactMethod方法2.1.2 ScrollViewPackage.class
在
ReactPackage
中同时注册createNativeModules
和createViewManagers
2.2 iOS实现过程
同1.2
2.3 iOS的
RCT_EXPORT_METHOD
宏和android的@ReactMethod
注释区别{scrollTo: 1, scrollToEnd: 2}
undefined
{scrollTo: fn(), scrollToEnd: fn() }
{scrollTo: fn(), scrollToEnd: fn() }
其他:measure文档补完
老版本:
现可直接使用
Reference