一篇搞定!React Native - Android Native集成篇

本文概要

大家好我是Joney,在我的职业生涯中 曾经做过RN的开发(从0到1到上架的研发)。深有体会:"如果你不会一些Native的东西,这里的水很深,玩RN建议一定是要懂一些Native的东西(Android/IOS)"。在RN的官方文档中,虽然是说了如何去集成Native的模块,但是!它只说了你要如何如何写,却没有一个完整的示例。本文是一个完整的工程 介绍了 RN 集成 Android Native 的方方面面

重要提示 🚗,本文基于RN 0.70.1, 对于 最新的RN 目前官方还在推他们的新设计,TurboModule 但是还在试验阶段,等它稳定之后我们再去讨论,

文章主要脉络如下

1.工程化工具bod🔧 -->

2.仅仅是集成js部分 --->

3.集成Native的其他模块

4.集成UI组件 --->

重要提醒!:请不要照着文章照抄,建议你先阅读通篇,了解全貌之后再去实践。

本篇是 《一篇搞定!React Native IOS集成 还有人不会制作?》的姊妹篇,所以重复的内容我就不叨叨了 包括 工程脚手架bob 它和ios是一样的,一通百通

我们简单的尝试过后接下来我们将会深入,注意哈我默认你会Java(Android开发)

我会按照下面的知识脉络去介绍

  • 初始化设置
  • 普通的 调用(不需要异步支持)
  • 异步的调用
  • Observer 和广播📢
  • Native组件的生命周期
  • 集成Native UI

初始化设置

  • 对于Native

对于Native 来说,下面的这些步骤是必须的

  1. 创建对应的class文件
  1. 实现对应的class,只有实现了才能把东西暴露给JS
java 复制代码
// CalendarModule.java
package com.your-apps-package-name; // replace your-apps-package-name with your app's package name
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import java.util.Map;
import java.util.HashMap;

public class CalendarModule extends ReactContextBaseJavaModule {
   CalendarModule(ReactApplicationContext context) {
       super(context);
   }

    @Override
    public String getName() {
       return "CalendarModule";
    }
}
  • 对于js
js 复制代码
// 很简单
const {CalendarModuleFoo} = ReactNative.NativeModules;
  • 依赖和注册相关

Android 需要手动的注册class

java 复制代码
package com.your-app-name; // replace your-app-name with your app's name
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class MyAppPackage implements ReactPackage {

   @Override
   public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
       return Collections.emptyList();
   }

   @Override
   public List<NativeModule> createNativeModules(
           ReactApplicationContext reactContext) {
       List<NativeModule> modules = new ArrayList<>();

       modules.add(new CalendarModule(reactContext));

       return modules;
   }

}

注意哈 你写完之后需要按照bob 提供的CLI 进行一定的操作能够关联到 本地RNE项目或者RNP项目中去,具体的方法我在姊妹篇有详细的说明。

以上初始化就完成了

普通的调用

现在我们介绍一下 普通的方法的调用(一般来说我们认为它是同步的,但是RN的通信实际上并不是完全的同步)

  • Native
java 复制代码
@ReactMethod
public void createCalendarEvent(String name, String location) {
    Log.d("CalendarModule", "Create event called with name: " + name  
    + " and location: " + location);
}

关于同步方法,RN官方还提供了一个 可以实现同步的方法,但是RN不推荐使用。因为同步调用方法可能会带来严重的性能损失,并且会在本机模块中引入与线程相关的 bug。

java 复制代码
@ReactMethod(isBlockingSynchronousMethod = true)
@ReactMethod
public void createCalendarEvent(String name, String location) {
    Log.d("CalendarModule", "Create event called with name: " + name  
    + " and location: " + location);
}
  • JS
js 复制代码
import React from 'react';
import {NativeModules, Button} from 'react-native';
const {CalendarModule} = NativeModules;
const NewModuleButton = () => {
    const onPress = () => {
      CalendarModule.createCalendarEvent('testName', 'testLocation');
    };

  return (
    <Button
      title="Click to invoke your native module!"
      color="#841584"
      onPress={onPress}
    />
  );
};

export default NewModuleButton;
  • 关于参数的类型和传递的问题

JS和Native是两种不同的东西,传递参数的时候 就需要注意了这些参数类型是如何转化的

下面是JS到Java/KOTLIN的类型一览图

JAVA KOTLIN JAVASCRIPT
Boolean Boolean ?boolean
boolean boolean
Double Double ?number
double number
String String string
Callback Callback Function
Promise Promise Promise
ReadableMap ReadableMap Object
ReadableArray ReadableArray Array

JS 我们都很熟悉了 我们来唠一下 java 中的一个例子🌰

js 复制代码
import { NativeModules } from 'react-native';
const {CalendarModule} = NativeModules;
// 调用原生模块的方法,并传递参数
CalendarModule.processData('hello', true, 123, [1, 2, 3], { key: 'value' })
    .then(result => {
        console.log('Received result from native:', result);
    })
    .catch(error => {
        console.error('Error:', error);
    });
java 复制代码
// CalendarModule.java

package com.yourpackage;

import android.util.Log;

import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;

public class CalendarModule extends ReactContextBaseJavaModule {

    public CalendarModule(ReactApplicationContext reactContext) {
        super(reactContext);
    }

    @Override
    public String getName() {
        return "CalendarModule";
    }

    @ReactMethod
    public void processData(
        String str,
        boolean bool,
        int number,
        ReadableArray array,
        ReadableMap map) {
        // 在这里处理传入的参数
        Log.d("CalendarModule", "Received string: " + str);
        Log.d("CalendarModule", "Received boolean: " + bool);
        Log.d("CalendarModule", "Received number: " + number);
        Log.d("CalendarModule", "Received array: " + array.toString());
        Log.d("CalendarModule", "Received map: " + map.toString());
        
        // 模拟处理并返回结果给 React Native
        WritableMap result = Arguments.createMap();
        result.putString("message", "Data processed successfully");
        promise.resolve(result);
    }
    
    @ReactMethod // 返回数据的时候需要使用 Writable 而不是 Readable
    public void processData(Promise promise) {
        // 构建对象 
        WritableMap map = Arguments.createMap(); map.putString("key", "value");
        // 构建数组
        WritableArray array = Arguments.createArray(); 
        array.pushString("item1");
        array.pushString("item2");
        // 将对象和数组传递给 Promise 的 resolve 方法 
        WritableMap result = Arguments.createMap();     
        result.putMap("object", map);
        result.putArray("array", array);
        promise.resolve(result); }
}

如果你希望获取Native定义的常量 可以这样使用

java 复制代码
@Override
public Map<String, Object> getConstants() {
   final Map<String, Object> constants = new HashMap<>();
   constants.put("DEFAULT_EVENT_NAME", "New Event");
   return constants;
}
js 复制代码
const {DEFAULT_EVENT_NAME} = CalendarModule.getConstants();
console.log(DEFAULT_EVENT_NAME);

异步调用

对于 Native 的异步方法, 有两种方式实现,1谁callback 2是promise

  1. callback
java 复制代码
    @ReactMethod
   public void createCalendarEvent(String name, String location, Callback callBack) {
       Integer eventId = ...
       callBack.invoke(null, eventId);
   }
js 复制代码
const onPress = () => {
  CalendarModule.createCalendarEventCallback(
    'testName',
    'testLocation',
    (error, eventId) => {
      if (error) {
        console.error(`Error found! ${error}`);
      }
      console.log(`event id ${eventId} returned`);
    },
  );
};

我们还有另一种形式

java 复制代码
@ReactMethod
public void createCalendarEvent(
    String name,
    String location,
    Callback myFailureCallback,
    Callback mySuccessCallback) {
    // ++++
}
js 复制代码
const onPress = () => {
  CalendarModule.createCalendarEventCallback(
    'testName',
    'testLocation',
    error => {
      console.error(`Error found! ${error}`);
    },
    eventId => {
      console.log(`event id ${eventId} returned`);
    },
  );
};
  1. promise

这个就比较简单了,我一直在用它

java 复制代码
import com.facebook.react.bridge.Promise;

@ReactMethod
public void createCalendarEvent(String name, String location, Promise promise) {
    try {
        Integer eventId = ...
        promise.resolve(eventId);
    } catch(Exception e) {
        promise.reject("Create Event Error", e);
    }
}
js 复制代码
const onSubmit = async () => {
  try {
    const eventId = await CalendarModule.createCalendarEvent(
      'Party',
      'my house',
    );
    console.log(`Created a new event with id ${eventId}`);
  } catch (e) {
    console.error(e);
  }
};

observer和广播

这里描述的是 Native向 JS 发送的广播的具体的实现。比如 通知 消息什么的。

在官方的文档中只有一种,但是实际上有两种。下面我们来看看

  • 模块自己的observer
java 复制代码
...
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.Arguments;
...
private void sendEvent(ReactContext reactContext,
                      String eventName,
                      @Nullable WritableMap params) {
 reactContext
     .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
     .emit(eventName, params);
}

private int listenerCount = 0;

@ReactMethod
public void addListener(String eventName) {
  if (listenerCount == 0) {
    // Set up any upstream listeners or background tasks as necessary
  }

  listenerCount += 1;
}

@ReactMethod
public void removeListeners(Integer count) {
  listenerCount -= count;
  if (listenerCount == 0) {
    // Remove upstream listeners, stop unnecessary background tasks
  }
}
...
// 在java中某个东西 直接触发 sendEvent 就可以发送广播到js了
WritableMap params = Arguments.createMap();
params.putString("eventProperty", "someValue");
sendEvent(reactContext, "EventReminder", params);
js 复制代码
import {NativeEventEmitter, NativeModules} from 'react-native';
...
useEffect(() => {
    const eventEmitter = new NativeEventEmitter(NativeModules.ToastExample);
    let eventListener = eventEmitter.addListener('EventReminder', event => {
      console.log(event.eventProperty) // "someValue"
    });

    // Removes the listener once unmounted
    return () => {
      eventListener.remove();
    };
  }, []);
  • 全局的observer

在上述中我们可以看到这样的代码:NativeEventEmitter(NativeModules.ToastExample);; 我们在创建observer 的时候是具体到 某一个模块的。其实RN有一个全局的DeviceEventEmitter

java 复制代码
// 很简单只需要实现 sendEvent 就可以了 其他的不需要实现
js 复制代码
import { DeviceEventEmitter } from 'react-native';

DeviceEventEmitter.addListener('EventName', event => {
  console.log('Received event:', event);
});
  • 多线程

你可以开业使用 observer 或者 promise 都可以

java 复制代码
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;

public class MyThread extends Thread {
    private ReactApplicationContext reactContext;

    public MyThread(ReactApplicationContext context) {
        this.reactContext = context;
    }

    @Override
    public void run() {
        // 在子线程中处理耗时任务
        try {
            Thread.sleep(2000); // 模拟耗时操作
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 创建一个包含数据的对象
        WritableMap data = Arguments.createMap();
        data.putString("message", "Hello from Android Thread!");

        // 发送消息到 React Native
        reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
                .emit("AndroidThreadEvent", data);
    }
}

StartActivityForResult get res

如果希望从以 startActivityForResult 启动的活动获得结果,则需要侦听 onActivityResult。

java 复制代码
public class ImagePickerModule extends ReactContextBaseJavaModule {

  private static final int IMAGE_PICKER_REQUEST = 1;
  private static final String E_ACTIVITY_DOES_NOT_EXIST = "E_ACTIVITY_DOES_NOT_EXIST";
  private static final String E_PICKER_CANCELLED = "E_PICKER_CANCELLED";
  private static final String E_FAILED_TO_SHOW_PICKER = "E_FAILED_TO_SHOW_PICKER";
  private static final String E_NO_IMAGE_DATA_FOUND = "E_NO_IMAGE_DATA_FOUND";

  private Promise mPickerPromise;

  private final ActivityEventListener mActivityEventListener = new BaseActivityEventListener() {

    @Override
    public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent intent) {
      if (requestCode == IMAGE_PICKER_REQUEST) {
        if (mPickerPromise != null) {
          if (resultCode == Activity.RESULT_CANCELED) {
            mPickerPromise.reject(E_PICKER_CANCELLED, "Image picker was cancelled");
          } else if (resultCode == Activity.RESULT_OK) {
            Uri uri = intent.getData();

            if (uri == null) {
              mPickerPromise.reject(E_NO_IMAGE_DATA_FOUND, "No image data found");
            } else {
              mPickerPromise.resolve(uri.toString());
            }
          }

          mPickerPromise = null;
        }
      }
    }
  };

  ImagePickerModule(ReactApplicationContext reactContext) {
    super(reactContext);

    // Add the listener for `onActivityResult`
    reactContext.addActivityEventListener(mActivityEventListener);
  }

  @Override
  public String getName() {
    return "ImagePickerModule";
  }

  @ReactMethod
  public void pickImage(final Promise promise) {
    Activity currentActivity = getCurrentActivity();

    if (currentActivity == null) {
      promise.reject(E_ACTIVITY_DOES_NOT_EXIST, "Activity doesn't exist");
      return;
    }

    // Store the promise to resolve/reject when picker returns data
    mPickerPromise = promise;

    try {
      final Intent galleryIntent = new Intent(Intent.ACTION_PICK);

      galleryIntent.setType("image/*");

      final Intent chooserIntent = Intent.createChooser(galleryIntent, "Pick an image");

      currentActivity.startActivityForResult(chooserIntent, IMAGE_PICKER_REQUEST);
    } catch (Exception e) {
      mPickerPromise.reject(E_FAILED_TO_SHOW_PICKER, e);
      mPickerPromise = null;
    }
  }
}

集成 Native UI Component

  • 举例说明 实现一个 ImageView
  1. 创建subclass
java 复制代码
public class ReactImageManager extends SimpleViewManager<ReactImageView> {

  public static final String REACT_CLASS = "RCTImageView";

  ReactApplicationContext mCallerContext;

  public ReactImageManager(ReactApplicationContext reactContext) {
    mCallerContext = reactContext;
  }
 

  @Override
  public String getName() {
    return REACT_CLASS;
  }
}
  1. 实现createViewInstance
java 复制代码
  @Override
  public ReactImageView createViewInstance(ThemedReactContext context) {
    return new ReactImageView(context, Fresco.newDraweeControllerBuilder(), null, mCallerContext);
  }
  1. 关于属性的传递

使用@reactprop(或@reactpropgroup)注释来揭示视图属性设置器

java 复制代码
  @ReactProp(name = "src")
  public void setSrc(ReactImageView view, @Nullable ReadableArray sources) {
    view.setSource(sources);
  }

  @ReactProp(name = "borderRadius", defaultFloat = 0f)
  public void setBorderRadius(ReactImageView view, float borderRadius) {
    view.setBorderRadius(borderRadius);
  }

  @ReactProp(name = ViewProps.RESIZE_MODE)
  public void setResizeMode(ReactImageView view, @Nullable String resizeMode) {
    view.setScaleType(ImageResizeMode.toScaleType(resizeMode));
  }
  1. 注册这个 native ui component

注意啊 这个方法要写到 module package 中去也就是这里

java 复制代码
public class MyAppPackage implements ReactPackage {

   @Override
   public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
       return Collections.emptyList();
   }

// +++++ 写到这个class 里 createViewManagers 是 ReactPackage的method

}
java 复制代码
  @Override
  public List<ViewManager> createViewManagers(
                            ReactApplicationContext reactContext) {
    return Arrays.<ViewManager>asList(
      new ReactImageManager(reactContext)
    );
  }
  1. js中的使用
js 复制代码
import {requireNativeComponent} from 'react-native';

/**
 * Composes `View`.
 *
 * - src: string
 * - borderRadius: number
 * - resizeMode: 'cover' | 'contain' | 'stretch'
 */
module.exports = requireNativeComponent('RCTImageView');
  1. 对于事件的处理
java 复制代码
// 我们可以通过 广播的方式 传递 事件出去,但是这效率太低了
class MyCustomView extends View {
   ...
   public void onReceiveNativeEvent() {
      WritableMap event = Arguments.createMap();
      event.putString("message", "MyMessage");
      ReactContext reactContext = (ReactContext)getContext();
      reactContext
          .getJSModule(RCTEventEmitter.class)
          .receiveEvent(getId(), "topChange", event);
    }
}

// 我们选择另一钟方式 把事件 map出去
public class ReactImageManager extends SimpleViewManager<MyCustomView> {
    ...
    public Map getExportedCustomBubblingEventTypeConstants() {
        return MapBuilder.builder().put(
            "topChange",
            MapBuilder.of(
                "phasedRegistrationNames",
                MapBuilder.of("bubbled", "onChange")
            )
        ).build();
    }
}

如此这般 我们就得到了一个 Naive UI Component

tsx 复制代码
class MyCustomView extends React.Component {
  constructor(props) {
    super(props);
    this._onChange = this._onChange.bind(this);
  }
  _onChange(event) {
    if (!this.props.onChangeMessage) {
      return;
    }
    this.props.onChangeMessage(event.nativeEvent.message);
  }
  render() {
    return <RCTMyCustomView {...this.props} onChange={this._onChange} />;
  }
}
MyCustomView.propTypes = {
  /**
   * Callback that is called continuously when the user is dragging the map.
   */
  onChangeMessage: PropTypes.func,
  ...
};

const RCTMyCustomView = requireNativeComponent(`RCTMyCustomView`);
  • 更多的(比如我想自己集成一个view 而不系统组件img什么的如何做?)
  1. 创建 custom view
java 复制代码
// replace with your package
package com.mypackage;

import android.content.Context;
import android.graphics.Color;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.NonNull;

public class CustomView extends FrameLayout {
  public CustomView(@NonNull Context context) {
    super(context);
    // set padding and background color
    this.setPadding(16,16,16,16);
    this.setBackgroundColor(Color.parseColor("#5FD3F3"));

    // add default text view
    TextView text = new TextView(context);
    text.setText("Welcome to Android Fragments with React Native.");
    this.addView(text);
  }
}
  1. 创建 fragemt
java 复制代码
// replace with your package
package com.mypackage;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.fragment.app.Fragment;

// replace with your view's import
import com.mypackage.CustomView;

public class MyFragment extends Fragment {
    CustomView customView;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
        super.onCreateView(inflater, parent, savedInstanceState);
        customView = new CustomView(this.getContext());
        return customView; // this CustomView could be any view that you want to render
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        // do any logic that should happen in an `onCreate` method, e.g:
        // customView.onCreate(savedInstanceState);
    }

    @Override
    public void onPause() {
        super.onPause();
        // do any logic that should happen in an `onPause` method
        // e.g.: customView.onPause();
    }

    @Override
    public void onResume() {
        super.onResume();
       // do any logic that should happen in an `onResume` method
       // e.g.: customView.onResume();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        // do any logic that should happen in an `onDestroy` method
        // e.g.: customView.onDestroy();
    }
}
  1. 创建 viewManager subclass
java 复制代码
// replace with your package
package com.mypackage;

import android.view.Choreographer;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentActivity;

import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.common.MapBuilder;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.uimanager.annotations.ReactPropGroup;
import com.facebook.react.uimanager.ViewGroupManager;
import com.facebook.react.uimanager.ThemedReactContext;

import java.util.Map;

public class MyViewManager extends ViewGroupManager<FrameLayout> {

  public static final String REACT_CLASS = "MyViewManager";
  public final int COMMAND_CREATE = 1;
  private int propWidth;
  private int propHeight;

  ReactApplicationContext reactContext;

  public MyViewManager(ReactApplicationContext reactContext) {
    this.reactContext = reactContext;
  }

  @Override
  public String getName() {
    return REACT_CLASS;
  }

  /**
   * Return a FrameLayout which will later hold the Fragment
   */
  @Override
  public FrameLayout createViewInstance(ThemedReactContext reactContext) {
    return new FrameLayout(reactContext);
  }

  /**
   * Map the "create" command to an integer
   */
  @Nullable
  @Override
  public Map<String, Integer> getCommandsMap() {
    return MapBuilder.of("create", COMMAND_CREATE);
  }

  /**
   * Handle "create" command (called from JS) and call createFragment method
   */
  @Override
  public void receiveCommand(
    @NonNull FrameLayout root,
    String commandId,
    @Nullable ReadableArray args
  ) {
    super.receiveCommand(root, commandId, args);
    int reactNativeViewId = args.getInt(0);
    int commandIdInt = Integer.parseInt(commandId);

    switch (commandIdInt) {
      case COMMAND_CREATE:
        createFragment(root, reactNativeViewId);
        break;
      default: {}
    }
  }

  @ReactPropGroup(names = {"width", "height"}, customType = "Style")
  public void setStyle(FrameLayout view, int index, Integer value) {
    if (index == 0) {
      propWidth = value;
    }

    if (index == 1) {
      propHeight = value;
    }
  }

  /**
   * Replace your React Native view with a custom fragment
   */
  public void createFragment(FrameLayout root, int reactNativeViewId) {
    ViewGroup parentView = (ViewGroup) root.findViewById(reactNativeViewId);
    setupLayout(parentView);

    final MyFragment myFragment = new MyFragment();
    FragmentActivity activity = (FragmentActivity) reactContext.getCurrentActivity();
    activity.getSupportFragmentManager()
            .beginTransaction()
            .replace(reactNativeViewId, myFragment, String.valueOf(reactNativeViewId))
            .commit();
  }

  public void setupLayout(View view) {
    Choreographer.getInstance().postFrameCallback(new Choreographer.FrameCallback() {
      @Override
      public void doFrame(long frameTimeNanos) {
        manuallyLayoutChildren(view);
        view.getViewTreeObserver().dispatchOnGlobalLayout();
        Choreographer.getInstance().postFrameCallback(this);
      }
    });
  }

  /**
   * Layout all children properly
   */
  public void manuallyLayoutChildren(View view) {
      // propWidth and propHeight coming from react-native props
      int width = propWidth;
      int height = propHeight;

      view.measure(
              View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY),
              View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY));

      view.layout(0, 0, width, height);
  }
}
  1. 注册 viewManager
java 复制代码
// replace with your package
package com.mypackage;

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

import java.util.Arrays;
import java.util.List;

public class MyPackage implements ReactPackage {

   @Override
   public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
       return Arrays.<ViewManager>asList(
            new MyViewManager(reactContext)
       );
   }

}
  1. 组册到 packgae中去
java 复制代码
// MainApplication.java
    @Override
    protected List<ReactPackage> getPackages() {
      List<ReactPackage> packages = new PackageList(this).getPackages();
      ...
      packages.add(new MyPackage());
      return packages;
    }
  1. js参考
tsx 复制代码
import {requireNativeComponent} from 'react-native';

export const MyViewManager =
  requireNativeComponent('MyViewManager');

import React, {useEffect, useRef} from 'react';
import {
  PixelRatio,
  UIManager,
  findNodeHandle,
} from 'react-native';

import {MyViewManager} from './my-view-manager';

const createFragment = viewId =>
  UIManager.dispatchViewManagerCommand(
    viewId,
    // we are calling the 'create' command
    UIManager.MyViewManager.Commands.create.toString(),
    [viewId],
  );

export const MyView = () => {
  const ref = useRef(null);

  useEffect(() => {
    const viewId = findNodeHandle(ref.current);
    createFragment(viewId);
  }, []);

  return (
    <MyViewManager
      style={{
        // converts dpi to px, provide desired height
        height: PixelRatio.getPixelSizeForLayoutSize(200),
        // converts dpi to px, provide desired width
        width: PixelRatio.getPixelSizeForLayoutSize(200),
      }}
      ref={ref}
    />
  );
};

参考

react-native-builder-bob仓库

RN官方文档

OC文档

相关推荐
web行路人4 分钟前
React中类组件和函数组件的理解和区别
前端·javascript·react.js·前端框架
番茄小酱0016 分钟前
Expo|ReactNative 中实现扫描二维码功能
javascript·react native·react.js
晨曦_子画6 分钟前
编程语言之战:AI 之后的 Kotlin 与 Java
android·java·开发语言·人工智能·kotlin
超雄代码狂26 分钟前
ajax关于axios库的运用小案例
前端·javascript·ajax
孤客网络科技工作室28 分钟前
AJAX 全面教程:从基础到高级
android·ajax·okhttp
长弓三石35 分钟前
鸿蒙网络编程系列44-仓颉版HttpRequest上传文件示例
前端·网络·华为·harmonyos·鸿蒙
小马哥编程36 分钟前
【前端基础】CSS基础
前端·css
嚣张农民1 小时前
推荐3个实用的760°全景框架
前端·vue.js·程序员
周亚鑫1 小时前
vue3 pdf base64转成文件流打开
前端·javascript·pdf
Justinc.1 小时前
CSS3新增边框属性(五)
前端·css·css3