The author:
ciro

Recently, when I was studying the React Native (RN) side of Android and using custom views to provide Native components, I encountered some practical problems. Here, I want to share the causes and solutions of these problems from the perspective of how RN works.

Custom View content does not work

why

At a givenRNProvide customizationViewThe time to find customizationsViewInside a lot ofUIThe logic didn’t work.

For example, the following figure hides/shows some controls according to logic, but the position of the controls that should be shown does not change. The location of the hidden control is still empty. The whole customization is obviousViewrequestLayoutNo execution.



The answer to that question is hereRNThe root layoutReactRootViewmeasureMethod.



During the measurement of this View, the MeasureSpec is determined to see if there are any updates in MeasureSpec.



whenmeasureSpecIt’s triggered when there’s a change, or when there’s a change in width and heightupdateRootLayoutSpecsThe logic.

Continue to look atupdateRootLayoutSpecsWhat are some of the things that are implemented at the end of the source codeUIImplementationdispatchViewUpdatesMethods:



Final Execution:



This is going to update the child from the root node downView, the implementation ofViewthemeasurelayout.

soReactRootViewIn the case of the width and height and measurement mode are not changed, it is equivalent to the handleViewIssued by therequestLayoutAll requests are blocked.

The solution

If you don’t want me to tell you that my root control needs to be rearranged, then I can do it myself. Refer to theRNSome customizations come with itViewThe implementation we can customize in thisViewWhen it comes time to rearrange, register oneFrameCallbackTo execute your ownmeasurelayoutMethods.

RN custom View must set the width and height on the JS side

After you implement the custom View, you will find that the native component is not displayed after you specify the label in JSX. The width and height of the custom View are both 0. If you set width and height, you can show it. Now I’m wondering why my custom View has WRAP_CONTENT in it, ConstraintLayout, ConstraintLayout, RelativeLayout, ConstraintLayout, ConstraintLayout, etc. To solve this puzzle, you need to understand the RN rendering process.

How does RN determine the width and height of a Native View

We follow theRNupdateViewThe structure of theUIImplementation#updateViewHierarchyMethod, there are two key points of logic:



calculateRootLayoutCall thecssRootThe layout calculation logic of:



And the next thing isapplyUpdatesRecursive, as the name implies, recursively updates all the children of the root node, which in our case is the layout of the entire page.



The node that needs to be updated is calleddispatchUpdatesMethod, executeenqueueUpdateLayout, the callNativeViewHierarchyManager#updateLayoutLogic.



updateLayoutThe core process of the

  • callresolveViewMethod to get the real control object.
  • Invokes the controlmeasureMethods.

  • callupdateLayoutTo execute the controllayoutmethods



Do you see? Here,width,heightThey’re already fixed values that are passed to each othermeausrelayoutIn other words, theseViewThe width and height of is not at allAndroidIs determined by the drawing process, so thiswidthheightWhere did the value of theta come from?

Look back and see the answer:



Width is highleft,top,right,bottomYou get the coordinates by subtracting the coordinates, and the coordinates are by

getLayoutWidthgetLayoutHeight:



And thislayoutWidthlayoutHeight, it isYogaCalculate it for us. Store itYogoNodeThe inside of the.

About Yoga

Yoga is a high-performance, easy-to-use, Flex cross-end layout engine implemented by Facebook. Inside, React Native is laid out using Yoga. For more information, see YOGA's official website: https://yogalayout.com/

This explains why custom views need to be rendered with width and height specified in JSX. Because the original Measure Layout process of these custom views in the Android system has been controlled by RN. It can be summed up in one sentence: The width and height of the final rendered controls in RN are calculated and determined by Yoga engine. The layout process of the system itself cannot directly determine the width and height of these controls. However, there is still a question at this time, why some components of RN itself, such as
, do not specify the width and height can be normally adaptive display?

Why does Rn’s own Text have its own width and height

Let’s look at an RN is renders the TextView, find the corresponding TextView ViewManager, com. Facebook. React. Views. Text. ReactTextViewManager we focus on two methods:

  1. createViewInstance

  1. createShadowNodeInstance



Among them,ReactTextViewIt actually implements a commonAndroid TextView.ReactTextShadowNodeThat represents thisTextViewThe correspondingYogaNodeThe implementation of the.



In its implementation, we can see that a member variable, by name, is responsible for thisYogaNodemeasureWork.



YogaNodeJNIBaseThe JNI method is called, registering such a callback function with the JNI logic.



thisYogaMeasureFunctionSpecific implementation of:



I’m going to cut a picture here, and you can see that it’s calledAndroidTextDraw theAPITo determine the width of the text. The function returns



Here it is usedYogaMeasureOutput.makeLayoutThe calculated width and height are converted into a binary callback of a certain formatYogaEngine, why is thatRNTheir ownTextTags can be displayed with adaptive width and height.

Here we can also draw a conclusion: ifAndroidEnd encapsulation customizationViewYou can either determine the width and height or the internal controls are very fixed to be able to pass throughmeasurelayoutWe can figure out the width and the height. We can register itmeasureFunctionCallbacks are the way to tellYogaweViewThe width of high.

But in real business, many of our business components are encapsulated inConstraintLayoutRelativeLayoutViewGroup, so we need another way to handle the component width and height Settings.

The solution

So this problem can be rewrittenViewonMeasurelayoutThe way to solve it? It seems that this is the solutionViewWide high for0The problem with rendering does not come out. But if thejsxWhen describing a layout like this:



At this timeAndroidViewTextIt’s going to be shown at the same time, andAndroidViewTextCover.

A little thought reveals why: forYogaFor the engine,AndroidViewThe node represented by is still of no width or height,YogaNodeThe inside of thewidth,heightIs still0So when you rewriteonMeasureonLayoutAfter the logic ofViewThe top left vertex shown is(0, 0)Coordinates.

whileYogaThe engine calculates itselfTextAfter the width and height of,TextThe top left vertex coordinates of PI must be the same(0, 0)So this is 2ViewIt will appear in the same place (overlapped or overlaid).

So the question then becomes, we want to passAndroidOwn layout flow to determine and refresh this custom control, howeverYogaThe engine doesn’t know.

So there are two possible ways to solve this problem:

  • Change the UI hierarchy and customizationsViewThe granularity of the
  • NativeMeasure the actual width and height of the synchronousYogaengine
Increase granularity of custom controls

Here is an example of a custom control:



We want to split the controls in the first row of the diagram into lower-grained customizationsViewtoRNThe ability to implement dynamic configuration of the layout. However, the controls on the left and right sides of this scenario are adaptive widths. At this time inJSThere is no way for the end to provide a proper width. Considering that the adaptive width controls on the same directional axis in more scenarios are positional dependent, it is possible to define both parts in the same custom without splitting themViewIn:



To provideJSEnd use, no width and height, put the wholeSingHeaderViewIs set to



At this point, the two internal controls will do their own layout. And what you end up showing is both left and rightWrap_Content.

Native measures the actual required width and height and synchronizes it to the Yoga engine

But control customizationViewThe granularity of the approach is always not flexible enough, development time also often makes people hesitate to split. Now, since the paradox of this question is thatYogaI don’t knowAndroidYou can call it again yourselfmeasureTo determine the width and height, so if you can pass the latest width and heightYoga“Can not solve our problems?

How to trigger itYogaNodeWhat about the refresh? The solution can be found by reading the source code. inUIManageInside, there’s one calledupdateNodeSizeapi:



thisapiWill updateViewThe correspondingcssNodeThe size is then distributed to refreshViewThe logic. This logic is guaranteed to be executed in the background message queue, so the refresh message needs to be sent tonativeModulesQueueThreadInside to execute.

We are inViewManagerI’ll keep this in thereManagerThe correspondingViewReactNodeImplInstance. For example,AndroidThe end encapsulates oneLinearLayout, the correspondingnodeMyLinearLayoutNode.



Overriding customizationViewonMeasureLet yourself bewrap_contentThe layout of the:



inrequestLayoutAccording to their true width and height layout and trigger the following logic:





But the above solution can be solvedViewwrap_contentDisplay problems, but there are some drawbacks:

The refreshYogaNodeIs actually inrequestLayoutThat’s the same thing asrequestLayoutThis more performance-intensive operation is performed twice as often. For some it may be triggered frequentlyrequestLayoutNeed to be carefully considered for the business scenario. If you encounter this kind of scenario, you still need to flexibly choose the solution according to your own needs.

This article is posted from
Netease cloud music big front end teamThis article is not allowed to be reproduced in any form without authorization. We recruit front-end, iOS, and Android all year round. If you’re ready for a career change and you happen to like cloud music, then join us. Grp.music-fe (at)corp.netease.com!