React Native笔记(一):封装android原生列表组件给rn使用

背景

react native官方提供的FlatList与原生RecyclerView相比:

  • 仅创建可见区域的视图,两者是一致的。
  • FlatList没有cell recycling,重用item view。

FlatList中将不可见的视图从内存中移除,但同时也会导致大量的视图重新创建以及垃圾回收。如果不断的创建视图,在列表滚动的过程中,内存占用量会不断增加。而原生的recyclerview因为能复用视图,所以在快速滑动的时候性能上影响不大。并且rn又支持封装原生组件给rn使用,于是产生了封装recyclerview给rn使用的想法。

步骤

  1. 创建列表item布局组件

关键点:列表item的rn布局如何传给RecyclerView的adapter

  • rn的视图布局根节点为ReactRootView,它的底层是FrameLayout,因此可以在adapter的onCreateViewHolder中返回ReactRootView作为item的根视图。
  • isRunning用于复用item的时候,可以直接setAppProperties更新ReactRootView的属性而不用重新startReactApplication加载。
public class RNItemView extends ReactRootView {
    private final String cellModuleName;
    private boolean isRunning = false;

    public RNListItemView(Context context, String cellModuleName) {
        super(context);
        this.cellModuleName = cellModuleName;
    }

    private void startReactApp(Bundle bundle) {
        isRunning = true;
        ReactApplication app = (ReactApplication)getContext().getApplicationContext();
        startReactApplication(app.getReactNativeHost().getReactInstanceManager(), cellModuleName, bundle);
    }

    public boolean isRunning() {
        return isRunning;
    }
}

其中cellModuleName为在rn index.js中注册的组件名字
2. 创建adapter

  • 在onCreateViewHolder中将上一步创建的RNItemView作为item根视图传入
  • 在onBindViewHolder中设置好item的布局参数
public class MyAdapter extends RecyclerView.Adapter<ViewHolder> {
    private List<Bundle> data;
    private Context context;
    private String cellModuleName;

    public MyAdapter(Context context) {
        this.context = context;
        this.data = new ArrayList<>();
    }

    public void setData(List<Bundle> data){
        this.data.clear();
        this.data.addAll(data);
        notifyDataSetChanged();
    }
	
	public void setCellModuleName(String cellModuleName){
        this.cellModuleName = cellModuleName;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    	RNItemView view = new RNItemView(context, cellModuleName);
        return new ViewHolder(context, view);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        Bundle bundle = data.get(position);
        RNItemView rnItemView =  (RNItemView) holder.itemView;
       	LinearLayoutManager.LayoutParams params = (LinearLayoutManager.LayoutParams) rnItemView.getLayoutParams();
        if (params == null){
            params = new LinearLayoutManager.LayoutParams(bundle.getInt("pixelWidth"), bundle.getInt("pixelHeight"));
        }else {
            params.width = bundle.getInt("pixelWidth");
            params.height = bundle.getInt("pixelHeight");
        }
        rnItemView.setLayoutParams(params);
        if(!rnItemView.isRunning()){
            rnItemView.startReactApp(bundle);
        }else{
            rnItemView.setAppProperties(bundle);
        }
    }

    @Override
    public int getItemCount() {
        return data.size();
    }

}
  1. 创建RecyclerView
  • 将rn传过来的数据源解析元素尺寸,转换为pixel后传给adapter
public class RNRecyclerView extends RecyclerView {
	MyAdapter adapter;
    public RNRecyclerView(@NonNull Context context) {
        super(context);
    }

    public RNRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

	private void init(){
		adapter = new MyAdapter();
		LinearLayoutManager lm = new LinearLayoutManager(getContext());
		setLayoutManager(lm);
		setAdapter(adapter);
	}
	
	public void setCellModuleName(String cellModuleName){
		adapter.setCellModuleName(cellModuleName);
	}
	
	public void setDataSource(ReadableArray dataSize){
		List<Bundle> listData = new ArrayList<>();
        for(int i = 0; i < newListSize; i++){
        	ReadableMap readableMap = dataSize.getMap(i);
            Map<String, Object> map = readableMap.toHashMap();
            Bundle b = changeMapToBundle(map);
            b.putInt("pixelWidth", (int) PixelUtil.toPixelFromDIP(readableMap.getInt("width")));
                b.putInt("pixelHeight", (int) PixelUtil.toPixelFromDIP(readableMap.getInt("height")));
            listData.add(b);
        }
        adapter.setData(listData);
	}
}
  1. 创建viewmanager
public class RNRecyclerViewManager extends SimpleViewManager<RNRecyclerView> {
    @NonNull
    @Override
    public String getName() {
        return "RNRecyclerView";
    }

    @NonNull
    @Override
    protected RNRecyclerView createViewInstance(@NonNull ThemedReactContext reactContext) {
        return new RNRecyclerView(reactContext);
    }

    @ReactProp(name = "cellModuleName")
    public void setCellModuleName(RNRecyclerView view, String cellModuleName){
        view.setCellModuleName(cellModuleName);
    }


    @ReactProp(name = "dataSource")
    public void setDataSource(RNRecyclerView view, ReadableArray array){
        view.setDataSource(array);
    }
}
  1. 创建rn组件,数据源元素最好带上item的尺寸
export interface MyData<T> {
	width: number,
	height: number,
    data: T
}

interface Props<T> {
    style?: StyleProp<ViewStyle>
    dataSource?: MyData<T>[]
    cellItemName: string

}
const NativeRNRecyclerView = requireNativeComponent('RNRecyclerView')
export default class RNRecyclerView extends React.Component<Prop<T>> {
    constructor(props: Prop<T>) {
        super(props);
    }


    render(){
        return (
                <NativeRNRecyclerView
                    {...this.props}
                >
                </NativeRNRecyclerView>
            )

    }
}
/*
 * 测试代码
 */
 const {width} = Dimensions.get('window')
export default class TestRNRecyclerView extends React.Component<any>{

    constructor(props: any) {
        super(props);
        this.state = {
        	dataSource: [],
        }
    }

    componentDidMount(){
        //构造数据
        let dataSet: MyData<string>[] = []
        for(let i = 0; i < 30; i++){
        	this.dataSet.push({widht: width, height: 100, data:'row'+i});
        }
        this.setState({
			dataSource: dataSet
		})
    }

    render() {
       return (
            <View style={{
                height: "100%"
            }}>
                <RNRecyclerView 
                	style={{
                    	width: "100%",
                    	height: "100%"
                    }}
                    cellModuleName={"MyItemView"}
                    dataSource={this.state.dataSource}
                />
            </View>
        );
    }
}

export default class MyItemView extends React.Component<MyData<string>>{
    constructor(props: MyData<string>){
        super(props)
    }

    render() {
        if (this.props.data == undefined){
            return null
        }
        //dataSet数据
        let info = this.props.data;
        return (
                <View>
                	<Text style={{
                         color: "#FF0000"
                   			}}>
                    {info}</Text>
                </View>
        )
    }
}

//index.js
AppRegistry.registerComponent('MyItemView', () => MyItemView);
  • MyItemView在index.js中注册好,名称会通过cellModuleName属性传给原生,等待adapter创建加载,而传给RNRecyclerView的dataSource数组元素会通过props回传给MyItemView组件

你可能感兴趣的:(react,native笔记,react,native,android,java)