android 刷新封装,React-native 封装安卓UI原生控件刷新无效的解决办法

最近封装了一个RecyclerView的rn组件,然而在原生下跑demo无比正常的情况下却在rn环境发生了没有刷新的情况,于是乎有了这一篇的发现。

我们了解完安卓下的requestLayout机制和RN下的requestLayout的对应调整,就可以淡定的修复该bug了。n(≧▽≦)n

安卓的 requestLayout 机制

第一步

当一个 View 调用 requestLayout 的时候,会给当前的 View 设置一个FORCE_LAYOUT 标记,由此向 ViewParent 请求布局

便从这个 View 开始向上一直 requestLayout 直到 ViewRootImpl

ViewParent 就是当前的传输链

第二步:

ViewRootImpl 发现请求了布局。那么就会调用 measure 去确认当前 View 是否有FORCE_LAYOUT标记

如果有,那么就会进行重新 measure。并且设置标记 LAYOUT_REQUIRED

第三步

在随后的 layout 方法中,会判断这个标记。如果这个标记为true,就一定会调用onLayout

onLayout调用后会清理LAYOUT_REQUIRED标记

layout调用之后再清理掉FORCE_LAYOUT标记

只要调用了requestLayout, 那么measure、onMeasure、layout、onLayout、draw、onDraw都会被调用。

React-native下 的 requestLayout 对应的调整

react-native 为了提升安卓的性能,将 requestLayout 的执行过程统一通过UIManagerModule 来管理,而原 ReactViewGroup 内的 requestLayout 被重写为空方法,就等同于将该 ViewGroup 包裹下的所有子元素的requestLayout都被拦截,无法通知到ViewRootImpl去进行重绘

6e1f20de8034

ReactViewGroup的requestLayout实现

解决方案:

在自定义 View 中重写 requestLayout 方法如下

@Override

public void requestLayout() {

super.requestLayout();

// The spinner relies on a measure + layout pass happening after it calls requestLayout().

// Without this, the widget never actually changes the selection and doesn't call the

// appropriate listeners. Since we override onLayout in our ViewGroups, a layout pass never

// happens after a call to requestLayout, so we simulate one here.

post(new Runnable(){

@Override

public void run() {

// 就差一个 draw 重绘了

measure(

MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY),

MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY)

);

layout(getLeft(), getTop(), getRight(), getBottom());

// 自定义样式处理

}

});

}

通过post方法异步去主动调用layout来实现刷新

该方案详细见 ----> stackoverflow React Native: Resize custom UI component

假如你封装的组件继承自ViewGroupMananger,在对应的createViewInstance内添加如下代码

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();

}

Choreographer.getInstance().postFrameCallback(this);

});

该方案详细见 ----> react-native的Github issue#17968

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值