为了实现在RN,weex 提供组件库,实现Fragment的嵌套,在集成的过程中,遇到很多问题,尤其是RN界面嵌套Fragment.以下是对问题的一个记录。
一、 ReactNative 对于嵌套Fragment 的crash
- 现象:no View found for id xxxx for fragment
- 原因:
In your code, you try to add fragment to the view which cannot be a fragment container.
Usually, View.generateViewId() will return id 0x1 which is duplicated with ReactRootView by coincidence so that your app will not crash. If you set other id to the container view, you will get exception and app will crash because the id of your view can not be found from current app's layout.
You can check it with Layout Inspector in Android Studio.
To prevent from duplication of ids, the best way is defining them in xml. And that's why I inflate a root view from xml.
But if you set id for the root(LinearLayout in my example), the id will be removed by RN when it is added, so the child is necessary.
After RN get the root view(didComponentMount()), then you need to call create() from RN component, and you can see it.
从上面的理解是无论是通过view.getId()还是View.generateViewId(),还是setId(),RN在拿到RootView后(didComponentMount)会将产生的id 移除,所以会出现找不到view id 的问题,因此建议在componentDidMount 中通过command手动进行createFragment(),这对应的下面的方法(1). 同时,发现通过在onAfterUpdateTransaction 中延迟处理View 也可以实现,对应方法(2).
- 解决办法:
(1)解决办法1:通过command 实现对View的显示,替代onAfterUpdateTransaction 方法。 参考链接:stackoverflow.com/questions/3…
(2)解决办法2: 在onAfterUpdateTransaction 中延迟加载View
protected void onAfterUpdateTransaction(final View view) {
super.onAfterUpdateTransaction(view);
//加延迟to fix crash issue no view found for id for fragment
view.postDelayed(new Runnable() {
@Override
public void run() {
addFragment();//添加自己的Fragment
reLayout();//代码详见第二条,如果还出现白屏,对reLayout单独加个延迟。
}
}, 0);
}
二、React Native 对于postDelayed 的addView 异常
- 现象:对于RN,通过postDelayed 去addView,是不显示的,直接addView 无异常。对于weex, native, 直接addView 和通过postDelayed 去addView 都无异常。
- 分析:通过Android LayoutInspector 查看界面,布局已添加,但是不显示。
- 解决办法:
在添加完View 后重新绘制界面,invalidate 是不起作用的,通过以下代码解决:
public void reLayout() {
Choreographer.getInstance().postFrameCallback(new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
child.measure(MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
child.layout(0, 0, child.getMeasuredWidth(), child.getMeasuredHeight());
}
getViewTreeObserver().dispatchOnGlobalLayout();
}
});
}
三、RN界面 嵌套原生Fragment 白屏问题
- 现象: RN组件嵌套原生Fragment,第一次显示的时候白屏,滑动一下界面界面显示
- 原因:在原生Fragment被 add 到容器后,尚未执行到onViewCreated,尚未将View完全加载,就执行了reLayout
- 解决办法:对上面的reLayout 加延迟。如果嵌套的是WeexFragment,如果依旧白屏不显示,延迟加长一些,两秒以上。