Using native attempts is an essential part of RN. If someone has done all the native functions, they can use it directly or fine-tune the trial part. There is no need to build another one, a set of wheels.

step

The official document is very detailed. The direct quote is as follows:

1. 新建一个ViewManager子类
2. 实现createViewInstance方法
3. 通过注解@ReactProp或者@ReactPropGroup暴露视图的属性
4. 在createViewManagers方法里注册这个manager
5. 实现JS模块

And the development of native modules is basically the same process.
But first, there must be a native view. Is such that:

public class FillingHoleView extends View {
    // ...

    public float getRadius()
    public void setRadius(float radius)

    public int getStrokeColor()
    public void setStrokeColor(int color)

    onDraw

    onMeasure

    // ...
}

It is an Android view, draw a circle. color and radius of the circle can be controlled by the setter.

Then start adding code in the order above.

ViewManager subclass

public class FillingHoleViewManager extends SimpleViewManager<FillingHoleView> {
    public static final String REACT_CLASS = "FillingHoleView";
    ReactApplicationContext mCallerContext;

    public FillingHoleViewManager(ReactApplicationContext reactContext) {
        this.mCallerContext = reactContext;
    }

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

    @NonNull
    @Override
    protected FillingHoleView createViewInstance(@NonNull ThemedReactContext reactContext) {
        return new FillingHoleView(reactContext);
    }

    @ReactProp(name = "radius", defaultFloat = 50f)
    public void setRadius(FillingHoleView fillingHoleView, int radius) {
        fillingHoleView.setRadius(radius);
    }

    @ReactProp(name = "color", defaultInt = 1)
    public void setStrokeColor(FillingHoleView fillingHoleView, int color) {
        fillingHoleView.setStrokeColor(Color.RED);
    }
}

There is an advantage to using SimpleViewManager . It provides background color, transparency and Flex layout functions by default, as well as some accessbility functions. So if you inherit this view manager, you can use flex layout.

When implementing the view manager, you need to provide the module name. In JS, the native module is also obtained by the module name.

In the createViewInstance method, the native view is returned.

The attributes that need to be exposed to JS are @ReactProp or @ReactGroupProp annotations. The first parameter is your native view, and the second is the attribute you want to modify. name is required. Among these attributes, only value types can provide specific default values, and reference types can only default to null.

Register View Manager

If there is no package class yet, you need to create a new one:

public class MyAppPackage implements ReactPackage {
    @NonNull
    @Override
    public List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactContext) {
        List<NativeModule> modules = new ArrayList<>();
        modules.add(new FillingEventHole(reactContext));

        return modules;
    }

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

Inherit ReactPackage implement the methods. There are two main ones, one for registering native modules and the other for registering native views. Later, this package needs to be added to your application class. Reference here .

Use your native view in JS

The simple version can refer to the official website:

// FillingHoleView.js

import { requireNativeComponent } from 'react-native';

/**
 * Composes `View`.
 *
 * - radius: number
 * - color: number
 * - width: number
 */
module.exports = requireNativeComponent('FillingHoleView');

For a more React implementation method, you can refer to here:

import React from 'react';
import { requireNativeComponent } from 'react-native';

const FillingNativeView = requireNativeComponent('FillingHoleView');

interface FillingHoleViewProps {
  radius: number;
  color: number; // 1, 2, 3
  width?: number;
}

const FillingHoleView: React.FC<FillingHoleViewProps> = props => {
  return <FillingNativeView {...props} />;
};

export { FillingHoleView };

requireNativeComponent returns components that can be used in other React components. But what is missing is the mandatory description of receiving various attributes and methods. Therefore, a full package (in fact, no actual view level is added) can automatically prompt acceptable attributes during use.

There are also events that can be accepted, here you can refer to official website . This part of the content will be added later.

layout

The example in this article is to draw a circle with adjustable background color and radius.

The main discussion here is the drawing logic. The radius is specified by the setter, and then the width and height of the view are obtained (padding is taken into account). In this way, there will be some strange problems when performing flex layout in the view.

First of all, the React unit and the android unit are not the same thing. For example, in this example, set the width to 50 in the Android code, and set the width to 100 in JS to get 275 in Android (the values obtained under different resolutions are also different).

Therefore, in development, it is best to unify the width and height of the entrance. For example, in this article, it is best to set it from JS, and then calculate the radius of the circle from the width. The radius can also be set in JS, but the width has been obtained when measuring, and the radius is not yet, or it is the default value in the example. As long as the value obtained by Android is different from the value obtained by JS (unless the resolution is appropriate).

The above is the issue of the unit. Now let's talk about the width.

The width must be present, otherwise the native view has a width and height of 0 in the flex layout system. As shown in the figure:
image.png

The green circle is considered to exist as a width and height of 0. Specific code:

<View style={styles.fillingNative}>
  <FillingHoleView radius={50} color={2} />
  <Text>2</Text>
</View>

The style of fillingNative

  fillingNative: {
    flex: 1,
    height: 160,
    flexDirection: 'row',
    justifyContent: 'flex-start',
    alignItems: 'center',
    backgroundColor: 'powderblue',
  },

In this case, the green circle has exceeded the boundary, and the text is displayed directly on it. Instead, look at the width of the yellow circle below, but the problem of different units as mentioned above occurs, and the layout is normal.

The problem with the red sphere is that the method of setting the width and height and the width and height must be clearly given. You can see that the red circle and white view have problems in the vertical layout. The problem is solved when the height is clearly given. As shown in the figure:
image.png

Set the width and height, in addition to explicitly passing each one as a prop:

<FillingHoleView width={60} height={60} radius={30} color={3} />

You can also use style, but in this example you need to write the style member in props, otherwise lint will prompt an error.

<FillingHoleView style={{ width: 100, height: 100 }} radius={100} color={1} />

finally

To solve the performance problems of React Native, or the problem of reusing native code, you need to use the content of the native view, and of course there will be no shortage of native modules.

In addition to the steps in the development process, it is also necessary to pay attention to the influence of the measurement unit and width and height of the RN and native views on the flex layout.

The project code is here .


小红星闪啊闪
914 声望1.9k 粉丝

时不我待