React Native学习之调用Android自定义view

参考资料:原生UI组件
本篇文章中大部分还是来自参考资料中,还附带了一些我遇到的坑。

Android是一个开源的项目,有许多丰富并且功能强大的第三方自定义控件,那么React Native如何调用Android自定义的控件呢?请听我娓娓道来。
这里就不讲究如何自定义Android控件,假设你是一位Android经验丰富的人。

Android端代码:

先看下待React-Native调用的Android自定义控件:

public class CircleView extends View {

    private final String TAG = "CircleView";
    private Paint mPaint; // 画笔
    private float mRadius;  // 圆的半径

    public CircleView(Context context) {
        super(context);
        mPaint = new Paint();
        mPaint.setColor(0xAA000000);
    }

    /**
     * 设置圆的背景色
     * @param color
     */
    public void setColor(Integer color) {
        mPaint.setColor(color); // 设置画笔颜色
        invalidate();   // 更新画板
    }

    /**
     * 设置圆的半径
     * @param radius
     */
    public void setRadius(Integer radius) {
        /**
         * 由于JS传过的数字是dip单位,需要转换为实际像素
         * 使用com.facebook.react.uimanager包中的PixelUtil,进行转换
         */
        mRadius = PixelUtil.toPixelFromDIP(radius);
        invalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawCircle(mRadius, mRadius, mRadius, mPaint); // 画一个半径为100px的圆
        Log.d(TAG, "绘图");
    }
}

接下来我们需要定义个管理这个CircleView,并将修改CircleView属性的接口暴露给React Native。原生视图需要被一个ViewManager的派生类(或者更常见的,SimpleViewManage的派生类)创建和管理。一个SimpleViewManager可以用于这个场景,是因为它能够包含更多公共的属性,譬如背景颜色、透明度、Flexbox布局等等。这些子类本质上都是单例——React Native只会为每个管理器创建一个实例。它们创建原生的视图并提供给NativeViewHierarchyManager,NativeViewHierarchyManager则会反过来委托它们在需要的时候去设置和更新视图的属性。ViewManager还会代理视图的所有委托,并给JavaScript发回对应的事件。

public class CircleManager extends SimpleViewManager<CircleView> {

    /**
     * 设置js引用名
     */
    @Override
    public String getName() {
        return "MCircle";
    }

    /**
     * 创建UI组件实例
     */
    @Override
    protected CircleView createViewInstance(ThemedReactContext reactContext) {
        return new CircleView(reactContext);
    }

    /**
     * 传输背景色参数
     */
    @ReactProp(name = "color")
    public void setColor(CircleView view, Integer color) {
        view.setColor(color);
    }

    /**
     * 传输半径参数
     */
    @ReactProp(name = "radius")
    public void setRadius(CircleView view, Integer radius) {
        view.setRadius(radius);
    }
}

从这个类中可以看到这个CircleView对外提供两个属性的设置,一个是color和radius。要导出给JavaScript使用的属性,需要申明带有@ReactProp(或@ReactPropGroup)注解的设置方法。方法的第一个参数是要修改属性的视图实例,第二个参数是要设置的属性值。方法的返回值类型必须为void,而且访问控制必须被声明为public。JavaScript所得知的属性类型会由该方法第二个参数的类型来自动决定。支持的类型有:boolean, int, float, double, String, Boolean, Integer, ReadableArray, ReadableMap。

@ReactProp注解必须包含一个字符串类型的参数name。这个参数指定了对应属性在JavaScript端的名字。

除了name,@ReactProp注解还接受这些可选的参数:defaultBoolean, defaultInt, defaultFloat。这些参数必须是对应的基础类型的值(也就是boolean, int, float),这些值会被传递给setter方法,以免JavaScript端某些情况下在组件中移除了对应的属性。注意这个”默认”值只对基本类型生效,对于其他的类型而言,当对应的属性删除时,null会作为默认值提供给方法。例如:

@ReactProp(name = "barWidth", defaultFloat = 1f)

但是目前我还不是很了解这个defaultFloat值在什么情况下会使用到。如有读者知道,请留言告知。
在这个视图管理器重,最重要的是实现getName和createViewInstance方法。getName返回的是暴露给ReactNative的控件名字。第二个方法则是返回一个自定义View的实例。
现在已经创建了视图管理器了,就要把这个视图管理器注册到应用程序包中。在application中注册:

public class MainApplication extends Application implements ReactApplication {

    private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
        @Override
        protected boolean getUseDeveloperSupport() {
            return BuildConfig.DEBUG;
        }

        @Override
        protected List getPackages() {
            return Arrays.asList(
                    new MainReactPackage(),
                    new AppReactPackage()
            );
        }
    };

    @Override
    public ReactNativeHost getReactNativeHost() {
        return mReactNativeHost;
    }
}
public class AppReactPackage implements ReactPackage {

    @Override
    public List createNativeModules(ReactApplicationContext reactContext) {
        return Collections.emptyList();
    }

    @Override
    public List<Classextends JavaScriptModule>> createJSModules() {
        return Collections.emptyList();
    }

    @Override
    public List createViewManagers(ReactApplicationContext reactContext) {
        return Arrays.asList(
                new CircleManager()
        );
    }
}

把这个视图管理类注册到应用程序包的createViewManagers里。

React Native模块代码:

看下Js的代码:
新建一个circle.js文件,内容如下

import React, { Component, PropTypes } from 'react';
import {
  View,
  requireNativeComponent,
  processColor  // 字符Color转换为数字
} from 'react-native';

//requireNativeComponent函数中的第一个参数就是刚刚CircleManager.getName返回的值。
const RCTCircle = requireNativeComponent('MCircle', {
  propTypes: {
    color: PropTypes.number,
    radius: PropTypes.number,
    ...View.propTypes // 包含默认的View的属性
  },
});
module.exports=RCTCircle;

在调用的js中,如下调用:

import React, { Component,PropTypes } from 'react';
import {
    AppRegistry,
    StyleSheet,
    Text,
    View,
    processColor,
} from 'react-native';
//如果当前的js和circle.js为同级目录的话,就如下调用,后面不用写成from 'circle.js'!!!
import RCTCircle from 'circle';

export default class MainComponent extends Component {
    render() {
            return (
                
                    {{width: 100, height: 100}}
                          color={processColor('#ff0000')}
                          radius={50}
                    />
                
            ); 
    }
}

requireNativeComponent通常接受两个参数,第一个参数是原生视图的名字,而第二个参数是一个描述组件接口的对象。组件接口应当声明一个友好的name,这个name是用来在调试信息中显示,而不是给React-Native调用的名字,这个name可有可无。组件接口还必须声明propTypes字段,PropType是说明该属性的类型,需要和native的保持一致,用来对应到原生视图上。
这里还需要说明的是,通过视图管理器暴露出来的属性,例如该例子中的color和radius,不能写在style={}中,在style={}只支持ReactNative原生的属性。而试图管理器暴露出来的属性,与style平级的,正如例子中展示的那样子。

当然还有一种就是如果试图管理器提供的view,在React-Native这里还需要在封装下,例如传递一些事件或者修改一些属性值什么的,可以在requireNativeComponent函数中第二个参数传component,例如:

'use strict';

import React, { Component, PropTypes } from 'react';
import {
  View,
  requireNativeComponent,
  processColor  // 字符Color转换为数字
} from 'react-native';

const MCircle = requireNativeComponent('MCircle', {
  propTypes: {
    color: PropTypes.number,
    radius: PropTypes.number,
    ...View.propTypes // 包含默认的View的属性
  },
});

class Circle extends Component {

  static propTypes = {
    radius: PropTypes.number,
    color: PropTypes.string, // 这里传过来的是string
    ...View.propTypes // 包含默认的View的属性
  }

  render() {
    const { style, radius, color } = this.props;

    return (
      
    );
  }

}

module.exports = Circle;

你可能感兴趣的:(android)