react-native 在android封装原生listView

前言

react-native中的ListView的性能问题是react-native开发的一个痛点。虽然0.43中推出了FlatList,但是快速滑动的时候的白屏问题仍然是很影响用户体验。
最近在项目中需要使用react-native开发相册,在经过再三的考虑后,决定直接封装原生的listView。虽然这样做使得ios、android的代码不可共用,也放弃了react-native热更新的优势,但却实实在在的拥有了原生的性能。

问题

ios轻易的完成了封装,但是android这边却遇到了坑–listView的更新实时,setAdapter或者adapter.notifyDataSetChanged后页面没有进行相应的更新,必须滚动页面后才会进行相应的刷新。

思考

作为一名程序猿,首先当然是上google去了。遗憾的是没有找到任何有用的信息,甚至没有人提出这个问题。没办法,自己撸源码去。首先看了一下listView中setAdapter和adapter.notifyDataSetChanged的源码,发现其最终都是调用requestLayout来更新页面的。
然后,我就去查了一下requestLayout到底做了什么:

在requestLayout方法中,首先先判断当前View树是否正在布局流程,接着为当前子View设置标记位,该标记位的作用就是标记了当前的View是需要进行重新布局的,接着调用mParent.requestLayout方法,这个十分重要,因为这里是向父容器请求布局,即调用父容器的requestLayout方法,为父容器添加PFLAG_FORCE_LAYOUT标记位,而父容器又会调用它的父容器的requestLayout方法,即requestLayout事件层层向上传递,直到DecorView,即根View,而根View又会传递给ViewRootImpl,也即是说子View的requestLayout事件,最终会被ViewRootImpl接收并得到处理。纵观这个向上传递的流程,其实是采用了责任链模式,即不断向上传递该事件,直到找到能处理该事件的上级,在这里,只有ViewRootImpl能够处理requestLayout事件。– Android View 深度分析requestLayout、invalidate与postInvalidate

解决

既然requestLayout是层层向上传递,那么问题就应该是在父view上了。作为一个控件,在使用中,其父view必然是一个react-native的控件。而react-native的view大部分是继承View,所以我又查看了一下VIew的原生实现ReactVIewGroup的源码,终于找到了原因所在:

  @Override  public void requestLayout() {    // No-op, terminate `requestLayout` here, UIManagerModule handles laying out children and    // `layout` is called on all RN-managed views by `NativeViewHierarchyManager`  }

React-Native重写了ReactViewGroup的requestLayout方法,这使得ListView在使用requestLayout的时候无法事件无法传递到ViewRootImpl。就是说,所有的view在ReactNative都将失效。

知道问题所在,事情就好解决了。在Stack Overflow找到了解决的方法:

@Overridepublic 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(measureAndLayout);}private final Runnable measureAndLayout = new Runnable() {    @Override    public void run() {        measure(                MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY),                MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY));        layout(getLeft(), getTop(), getRight(), getBottom());    }};

http://stackoverflow.com/questions/39836356/react-native-resize-custom-ui-component

更多相关文章

  1. Android自定义属性时TypedArray的使用方法
  2. Android语音识别方法
  3. Parcalable接口使用(android传递结构体数据的方法)
  4. Android 扫码盒子全局接收付款码(全局事件&上层接收&多重下发)
  5. ImageView之android:tint=" "属性方法作用详解
  6. Android中View的事件执行机制
  7. C#/IOS/Android通用加密解密方法
  8. WebView的使用之Android与JS通过WebView互调方法
  9. Android APK文件在电脑上面运行方法

随机推荐

  1. 2011.07.06——— android 安装apiDemos
  2. Android Talker(1)MAC Environment
  3. Android获取OAID设备标识
  4. Android 获取imei号码,获取手机型号和系统
  5. Android消息机制之三---Message
  6. Android 多国语言文件夹
  7. Android 设置让EditText不自动获取焦点
  8. Android Studio添加aar依赖的两种方式
  9. Android PureMVC
  10. Android Gradle Plugin指南(三)——依赖关