ReactNative 桥接原生原子组件

Posted rogerwu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ReactNative 桥接原生原子组件相关的知识,希望对你有一定的参考价值。

♦ 实现一个原生自定义组件 View

 

1、打开 Android Studio 编辑器,在 android > app > src > main > java > com.rndemo (项目包名) 下新建一个 view

 

2、在 view 包下新建一个 InfoView 类,这个就是要提供给 JS 层的原生组件

 

3、这个 InfoView 类需要继承 LinearLayout 类,并选择 Choose Super Class Constructors 弹窗中的 第 1 个构造函数

 

4、在 android > app > src > main > res 下新建一个 layout 目录

 

5、在新建的 layout 目录下新建一个 layout_info_view.xml 布局文件

 

6、在右上角视图模式那里选择 Split 模式,可以同时显示 CodeDesign 窗口

 

7、编写一个简单的布局,用于展示个人信息

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingHorizontal="16dp"
    android:paddingVertical="10dp"
    >
    <ImageView
        android:id="@+id/icon_avatar"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:src="@mipmap/ic_launcher_round"
    />
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:layout_marginLeft="16dp"
        >
        <TextView
            android:id="@+id/txt_name"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="6dp"
            android:text="托尼斯塔克"
            android:textColor="#e91e63"
            android:textSize="20sp"
            android:textStyle="bold"
        />
        <TextView
            android:id="@+id/txt_desc"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="4dp"
            android:text="大家好,我是托尼斯塔克,我已经不是超级英雄了,我只是一个帅气的亿万富翁,但是,我经常忍不住说出那句口头禅 &quot;I am Iron man.&quot;"
            android:textColor="#fff"
            android:background="#3050ff"
            android:textSize="12sp"
            android:textStyle="bold"
            />
    </LinearLayout>

</LinearLayout>

 

8、将布局添加到自定义的 InfoView

 

 

♦ 创建 ViewManager,用于接管原生自定义组件的属性和行为,并把 ViewManager 注册到 ReactPackage 中

 

在上一篇 ReactNative 桥接原生组件 中提到过 原生组件是通过 RnDemoPackagecreateViewManagers 方法注册的,最后通过 MainApplication 注册到整个应用中

从图中可以看到 createViewManagers 现在返回的还是一个空的集合,所以,需要先创建 ViewManager,用于托管前面自定义的那个原生组件 InfoView

1、在 android > app > src > main > java > com.rndemo (项目包名) 下新建一个 viewmanager 包,然后,在这个包下新建一个 InfoViewManager 类,该类需要继承 SimpleViewManager 抽象类并重写其方法

 

2、注册 InfoViewManagerReactPackage

 

 

♦ 在 JS 层导入原生组件,并封装导出 JS 模块

 

1、新建一个 NativeInfoView 文件用于引用 原生组件,将其提供给 JS 层的业务组件使用

 

2、调用上一步封装好的组件

 

重启项目以后,运行效果如下图

 

 

♦ 封装原生组件的属性

1、由于 ViewManager 托管了原生组件的属性和方法,所以,先回到 InfoViewManager.java 文件中添加设置属性的方法

 

2、回到 InfoView.java 文件,获取要修改属性的布局组件实例

 

3、分别添加修改属性的方法,供 ViewManager 调用

 

4、回到 JS 层,添加引用原生组件时需要传入的三个属性:avatar, name, desc

 

5、在业务组件中传入指定的属性值

 

重启项目以后,运行效果如下图

 

6、原生组件添加的属性全部生效以后,就可以删除布局文件 layout_info_view.xml 中的组件默认值

 

7、同时在 InfoViewManager.java 中的 setAvatar 方法中为 Image 组件添加一个默认值

 

8、回到 JS 层,再引用一个原生组件并将其 avatar 属性值设为空字符串

 

重启项目以后,运行效果如下图

 

 

♥ 补充 Android Studio 安装 Glide 的方法

Glide 是一款快速高效的 Android 图像加载库,注重于平滑的滚动。支持拉取、解码和展示视频快照、图片 和 GIF 动画。

官网地址:https://github.com/bumptech/glide

 

1、权限

Glide 一般情况下需要向 AndroidManifest.xml 文件中添加以下四个权限 (如已添加,可跳过)

<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    
    
</manifest>

 

INTERNET:访问网络权限,有了这个权限才能从网络上在线加载图片

ACCESS_NETWORK_STATE:访问网络状态权限,不是必须的权限,但是,加上可以处理片状网络(flaky network)和飞行模式

READ_EXTERNAL_STORAGE:阅读本地 SD 卡权限,从本地文件夹或 DCIM 或图库中加载图片

WRITE_EXTERNAL_STORAGE:将 Glide 缓存存储到 SD 卡上需要这个权限

 

添加的位置在 android > app > src > main > AndroidManifest.xml

 

2、Android Studio 在线依赖

Glide 支持在线依赖,在 android > app > build.gradle 文件中的 dependencies 属性下增加两行代码

implementation \'com.github.bumptech.glide:glide:4.13.0\'
annotationProcessor \'com.github.bumptech.glide:compiler:4.13.0\'

 

保存文件后,点击文件右上角的 Sync Now 按钮安装依赖

 

为啥我的 React Native 桥接 iOS 组件不起作用?

【中文标题】为啥我的 React Native 桥接 iOS 组件不起作用?【英文标题】:Why does my React Native bridged iOS component not work?为什么我的 React Native 桥接 iOS 组件不起作用? 【发布时间】:2015-12-15 17:00:13 【问题描述】:

我想创建一个简单的 UIView 子类,然后通过桥接到 React Native 使其可用作 React Native JavaScript 组件。我遵循了这些指示,并浏览了许多 React 源代码:https://facebook.github.io/react-native/docs/native-components-ios.html

不幸的是,我不知道我的 React Native 组件哪里出了问题。这是我的 Obj-C 管理器类:

// header
#import "RCTViewManager.h"
#import "ColorPicker.h"

@interface ColorPickerManager : RCTViewManager

@end

// implementation
#import "ColorPickerManager.h"

@interface ColorPickerManager()

@property (nonatomic) ColorPicker * colorPicker;

@end

@implementation ColorPickerManager

RCT_EXPORT_MODULE()

- (instancetype)init 
    self = [super init];
    if ( self ) 
        NSLog(@"color picker manager init");
    self.colorPicker = [[ColorPicker alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
    
    return self;


- (UIView *)view 
    NSLog(@"color picker manager -view method");
    return self.colorPicker;

@结束

这是我通过上述-view 方法出售的简单 UIView 子类:

// header
#import <UIKit/UIKit.h>

@interface ColorPicker : UIView

@end

// implementation
#import "ColorPicker.h"

@interface ColorPicker()

@property (nonatomic) NSArray * colors;

@end

@implementation ColorPicker

- (instancetype)init 
    NSLog(@"init");
    self = [super init];
    if ( self ) 
       [self setUp];
    
    return self;


- (instancetype)initWithFrame:(CGRect)frame 
    NSLog(@"init with frame: %@", NSStringFromCGRect(frame));
    self = [super initWithFrame:frame];
    if ( self ) 
        [self setUp];
    
    return self;


- (instancetype)initWithCoder:(NSCoder *)aDecoder 
    NSLog(@"init with coder: %@", aDecoder);
   self = [super initWithCoder:aDecoder];
   if ( self ) 
       [self setUp];
   
   return self;


- (void)setUp 
    self.colors = @[[UIColor redColor], [UIColor greenColor], [UIColor blueColor]];
    self.backgroundColor = self.colors[0];


- (void)layoutSubviews 
    NSLog(@"layout subviews");


@end

最后,这是我的 react 组件被桥接到 JavaScript:

var  requireNativeComponent  = require('react-native');
var ColorPicker = requireNativeComponent('ColorPicker', null);
module.exports = ColorPicker;
debugger;

它是声明并渲染到屏幕上:

'use strict';
var React = require('react-native');
var ColorPicker = require('./BridgedComponents/ColorPicker.js');


var 
  StyleSheet
 = React;

var iOS = React.createClass(

    render: function() 
        debugger;
        return (
            <ColorPicker style=styles.container />
        );
    
);

var styles = StyleSheet.create(
    container: 
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
    ,
);

AppRegistry.registerComponent('iOS', () => iOS);

当我希望看到 100x100 的红色方块时,这会导致屏幕上没有任何渲染。我尝试过的:

我已向 ColorPicker 添加了一些边框颜色和宽度,以确保它正在渲染某些内容。它的高度/宽度为零,但我在创建视图时指定了 100x100 大小的框架。 我已验证,如果我从项目中删除所有 React Native 代码并将此视图作为不同 rootViewController 的子视图,我的 UIView 将完全呈现我的预期。 我已经在 Xcode 和 Chrome 中逐步检查了调试器,以验证组件的管理器类是否已初始化并出售视图。 (顺便说一下,这发生了两次,可能是因为重新加载,但这似乎仍然效率低下)。 我还单步执行了所有桥接代码,以确保我的模块已注册并将其视图提供给桥接模块列表。 我已经验证我的主 .js 文件中的 ColorPicker 在它呈现到屏幕时以及通过 debugger 创建时不是 undefined。 我已尝试将 height: 100, width: 100 样式应用于渲染的 ColorPicker,但它仍然没有显示红色方块

给 RN 大师的问题 - 我还能做些什么来验证我的视图是否正确桥接? - 在检查这些实例以验证视图是否设置正确时,我应该在 Chrome 调试器中寻找什么吗? - 我已经尝试关注 repo 中的一些源代码,但我对 React 还是很陌生,而且我不是 100% 它是如何工作的。 - 当我创建一个桥接组件时,我希望在 Obj-C/Swift 类中设置外观和布局,还是在 JavaScript 和 CSS 中这样做更好。在我看来,前者是意料之中的。

任何帮助/建议将不胜感激。

【问题讨论】:

【参考方案1】:

除了添加了 ColorPicker 视图之外,您什么也看不到。

问题是 ReactNative 正在管理其 backgroundColor 属性本身并覆盖您最初的选择。

您可以在ColorPicker.m 中添加以下方法并设置断点以查看何时发生:

- (void)setBackgroundColor:(UIColor *)backgroundColor 
    NSLog(@"setBackgroundColor %@", backgroundColor);
    [super setBackgroundColor:backgroundColor];

您应该让 React Native 处理桥接组件的大小和外观。 但完全可以找到完全由本地控制的自定义子视图。

还有你的 ColorPickerManager should return a new instance 视图,当它调用 view 方法时,你正在桥接视图。

即你应该使用这个:

@implementation ColorPickerManager

RCT_EXPORT_MODULE()

- (instancetype)init 
  self = [super init];
  if ( self ) 
    NSLog(@"color picker manager init");
  
  return self;


- (UIView *)view 
  NSLog(@"color picker manager -view method");
  return [[ColorPicker alloc] init];


@end

否则,您将无法显示您的 ColorPicker 组件的 2 个(或更多)实例。

【讨论】:

关于setBackgroundColor: 方法的要点。但是如果我想设置自己的backgroundColorheightwidth 怎么办?我该怎么做?简单地桥接现有组件将需要您仔细检查当前组件是否尚未对这些属性执行特定操作。没有?

以上是关于ReactNative 桥接原生原子组件的主要内容,如果未能解决你的问题,请参考以下文章

ReactNative setNativeProps

react native 接入支付宝支付

React Native 桥接原生模块

H5调用Android和ios原生方法(react)

如何编写重用通用 JavaScript 代码的反应原生“本机模块”(桥)?

为啥Flutter开发APP性能最接近原生,前端程序员请关注