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 givenRN
Provide customizationView
The time to find customizationsView
Inside a lot ofUI
The 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 obviousView
的 requestLayout
No execution.
The answer to that question is hereRN
The root layoutReactRootView
的 measure
Method.
During the measurement of this View, the MeasureSpec is determined to see if there are any updates in MeasureSpec.
whenmeasureSpec
It’s triggered when there’s a change, or when there’s a change in width and heightupdateRootLayoutSpecs
The logic.
Continue to look atupdateRootLayoutSpecs
What are some of the things that are implemented at the end of the source codeUIImplementation
的 dispatchViewUpdates
Methods:
Final Execution:
This is going to update the child from the root node downView
, the implementation ofView
themeasure
和 layout
.
soReactRootView
In the case of the width and height and measurement mode are not changed, it is equivalent to the handleView
Issued by therequestLayout
All 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 theRN
Some customizations come with itView
The implementation we can customize in thisView
When it comes time to rearrange, register oneFrameCallback
To execute your ownmeasure
和 layout
Methods.
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 theRN
updateView
The structure of theUIImplementation#updateViewHierarchy
Method, there are two key points of logic:calculateRootLayout
Call thecssRoot
The 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 calleddispatchUpdates
Method, executeenqueueUpdateLayout
, the callNativeViewHierarchyManager#updateLayout
Logic.updateLayout
The core process of the
- call
resolveView
Method to get the real control object. - Invokes the control
measure
Methods.
- call
updateLayout
To execute the controllayout
methods
Do you see? Here,width
,height
They’re already fixed values that are passed to each othermeausre
和 layout
In other words, theseView
The width and height of is not at allAndroid
Is determined by the drawing process, so thiswidth
和 height
Where did the value of theta come from?
Look back and see the answer:
Width is highleft
,top
,right
,bottom
You get the coordinates by subtracting the coordinates, and the coordinates are bygetLayoutWidth
和 getLayoutHeight
:
And thislayoutWidth
和 layoutHeight
, it isYoga
Calculate it for us. Store itYogoNode
The 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
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:
- createViewInstance
- createShadowNodeInstance
Among them,ReactTextView
It actually implements a commonAndroid TextView
.ReactTextShadowNode
That represents thisTextView
The correspondingYogaNode
The implementation of the.
In its implementation, we can see that a member variable, by name, is responsible for thisYogaNode
的 measure
Work.YogaNodeJNIBase
The JNI method is called, registering such a callback function with the JNI logic.
thisYogaMeasureFunction
Specific implementation of:
I’m going to cut a picture here, and you can see that it’s calledAndroid
中 Text
Draw theAPI
To determine the width of the text. The function returns
Here it is usedYogaMeasureOutput.make
把 Layout
The calculated width and height are converted into a binary callback of a certain formatYoga
Engine, why is thatRN
Their ownText
Tags can be displayed with adaptive width and height.
Here we can also draw a conclusion: ifAndroid
End encapsulation customizationView
You can either determine the width and height or the internal controls are very fixed to be able to pass throughmeasure
和 layout
We can figure out the width and the height. We can register itmeasureFunction
Callbacks are the way to tellYoga
weView
The width of high.
But in real business, many of our business components are encapsulated inConstraintLayout
、RelativeLayout
ViewGroup, so we need another way to handle the component width and height Settings.
The solution
So this problem can be rewrittenView
的 onMeasure
和 layout
The way to solve it? It seems that this is the solutionView
Wide high for0
The problem with rendering does not come out. But if thejsx
When describing a layout like this:
At this timeAndroidView
和 Text
It’s going to be shown at the same time, andAndroidView
被 Text
Cover.
A little thought reveals why: forYoga
For the engine,AndroidView
The node represented by is still of no width or height,YogaNode
The inside of thewidth
,height
Is still0
So when you rewriteonMeasure
和 onLayout
After the logic ofView
The top left vertex shown is(0, 0)
Coordinates.
whileYoga
The engine calculates itselfText
After the width and height of,Text
The top left vertex coordinates of PI must be the same(0, 0)
So this is 2View
It will appear in the same place (overlapped or overlaid).
So the question then becomes, we want to passAndroid
Own layout flow to determine and refresh this custom control, howeverYoga
The engine doesn’t know.
So there are two possible ways to solve this problem:
- Change the UI hierarchy and customizations
View
The granularity of the Native
Measure the actual width and height of the synchronousYoga
engine
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 customizationsView
toRN
The 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 inJS
There 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 themView
In:
To provideJS
End use, no width and height, put the wholeSingHeaderView
Is 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 customizationView
The 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 thatYoga
I don’t knowAndroid
You can call it again yourselfmeasure
To determine the width and height, so if you can pass the latest width and heightYoga
“Can not solve our problems?
How to trigger itYogaNode
What about the refresh? The solution can be found by reading the source code. inUIManage
Inside, there’s one calledupdateNodeSize
的 api
:
thisapi
Will updateView
The correspondingcssNode
The size is then distributed to refreshView
The logic. This logic is guaranteed to be executed in the background message queue, so the refresh message needs to be sent tonativeModulesQueueThread
Inside to execute.
We are inViewManager
I’ll keep this in thereManager
The correspondingView
和 ReactNodeImpl
Instance. For example,Android
The end encapsulates oneLinearLayout
, the correspondingnode
是 MyLinearLayoutNode
.
Overriding customizationView
的 onMeasure
Let yourself bewrap_content
The layout of the:
inrequestLayout
According to their true width and height layout and trigger the following logic:
But the above solution can be solvedView
的 wrap_content
Display problems, but there are some drawbacks:
The refreshYogaNode
Is actually inrequestLayout
That’s the same thing asrequestLayout
This more performance-intensive operation is performed twice as often. For some it may be triggered frequentlyrequestLayout
Need 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!