REACT NATIVE September 07, 2018

【React Native】一、原生UI组件

Words count 26k Reading time 24 mins. Read count 0

前言

React Native封装了大部分最常见的组件,但有时我们需要一些特殊的组件是React Native没有提供的。该文章将细说如何在React Naitve应用程序中封装和植入已有的原生UI组件。

步骤

  1. 创建一个ViewManager的子类
  2. 实现createViewInstance方法
  3. 导出视图的属性设置器:使用@ReactProp(或@ReactPropGroup)注解
  4. 把这个视图管理类注册到应用程序包的createViewManagers里
  5. 实现JavaScript模块

具体代码实现

我们将自定义一个可以显示圆形、圆角矩形和椭圆形图片的ImageView组件

  1. 在Android中自定义该控件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    public class RoundImageView extends ImageView {

    private Paint mPaint;
    private int mWidth;
    private int mRadius;
    private RectF mRect;
    private int mRoundRadius;
    private BitmapShader mBitmapShader;
    private Matrix mMatrix;
    private int mType = 1;

    public static final int TYPE_CIRCLE = 1;
    public static final int TYPE_ROUND_RECTANGLE = 2;
    public static final int TYPE_OVAL = 3;
    public static final int DEFAUT_RADIUS = 5;

    public RoundImageView(Context context) {
    this(context, null);
    }

    public RoundImageView(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
    }

    public RoundImageView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    initView();
    }

    private void initView() {
    mPaint = new Paint();
    mPaint.setAntiAlias(true);
    mMatrix = new Matrix();
    mRoundRadius = DEFAUT_RADIUS;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    if (mType == TYPE_CIRCLE) {
    mWidth = Math.min(getMeasuredWidth(), getMeasuredHeight());
    mRadius = mWidth / 2;
    setMeasuredDimension(mWidth, mWidth);
    }

    }

    @Override
    protected void onDraw(Canvas canvas) {
    if (null == getDrawable()) {
    return;
    }
    initBitmapShaderByType();
    switch (mType) {
    case TYPE_CIRCLE:
    canvas.drawCircle(mRadius, mRadius, mRadius, mPaint);
    break;
    case TYPE_ROUND_RECTANGLE:
    mPaint.setColor(Color.RED);
    canvas.drawRoundRect(mRect, mRoundRadius, mRoundRadius, mPaint);
    break;
    case TYPE_OVAL:
    canvas.drawOval(mRect, mPaint);
    break;
    }
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    mRect = new RectF(0, 0, getWidth(), getHeight());
    }

    private void initBitmapShaderByType() {
    Drawable drawable = getDrawable();
    if (null == drawable) {
    return;
    }
    Bitmap bitmap = drawableToBitmap(drawable);
    mBitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
    float scale = 1.0f;
    switch (mType) {
    case TYPE_CIRCLE:
    int bSize = Math.min(bitmap.getWidth(), bitmap.getHeight());
    scale = mWidth * 1.0f / bSize;
    break;
    case TYPE_ROUND_RECTANGLE:
    case TYPE_OVAL:
    scale = Math.max(getWidth() * 1.0f / bitmap.getWidth(), getHeight() * 1.0f / bitmap.getHeight());
    break;
    }
    mMatrix.setScale(scale, scale);
    mBitmapShader.setLocalMatrix(mMatrix);
    mPaint.setShader(mBitmapShader);
    }

    public void setImageName(String name) {
    this.setImageResource(getResourceId(name));
    }

    public void setType(String typeStr) {
    int type = 1;
    switch (typeStr) {
    case "circle":
    type = 1;
    break;
    case "round":
    type = 2;
    break;
    case "oval":
    type = 3;
    break;
    }
    if (this.mType != type) {
    this.mType = type;
    invalidate();
    }
    }

    public void setRoundRadius(int mRoundRadius) {
    if (this.mRoundRadius != mRoundRadius) {
    this.mRoundRadius = mRoundRadius;
    invalidate();
    }
    }

    private Bitmap drawableToBitmap(Drawable drawable) {
    if (drawable instanceof BitmapDrawable) {
    BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
    return bitmapDrawable.getBitmap();
    }
    int w = drawable.getIntrinsicWidth();
    int h = drawable.getIntrinsicHeight();
    Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(bitmap);
    drawable.setBounds(0, 0, w, h);
    drawable.draw(canvas);
    return bitmap;
    }

    private int getResourceId(String imageName) {
    int resId = getResources().getIdentifier(imageName, "mipmap", getContext().getPackageName());
    if (resId == 0) {
    resId = getResources().getIdentifier(imageName, "drawable", getContext().getPackageName());
    }
    return resId;
    }
    }

在上述的RoundImageView中对外提供了三个公共方法,用于后续js设置属性使用:

  • setImageName(String name)
  • setType(String typeStr)
  • setRoundRadius(int mRoundRadius)
  1. 创建ViewManager子类,实现方法createViewInstance,通过@ReactProp(或@ReactPropGroup)注解来导出属性的设置方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    public class RoundImageViewManager extends SimpleViewManager<RoundImageView> {

    public static final String COMPONENT_NAME = "RoundImageView";

    @Override
    public String getName() {
    return COMPONENT_NAME;
    }

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

    @ReactProp(name = "name")
    public void setPath(RoundImageView view, String name) {
    view.setImageName(name);
    }

    /**
    * 设置图片类型:圆形、圆角矩形、椭圆形
    *
    * @param mType
    */
    @ReactProp(name = "type")
    public void setType(RoundImageView view, String mType) {
    view.setType(mType);
    }

    /**
    * 设置圆角大小
    *
    * @parammRoundRadius
    */
    @ReactProp(name = "roundradius")
    public void setRoundRadius(RoundImageView view, int mRoundRadius) {
    view.setRoundRadius(mRoundRadius);
    }
    }
  2. 注册ViewManager,然后在Application的mReactNativeHost中返回RoundImageViewReactPackage

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    public class RoundImageViewReactPackage implements ReactPackage {

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

    @Override
    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
    return Arrays.<ViewManager>asList(new RoundImageViewManager());
    }
    }

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

    @Override
    protected List<ReactPackage> getPackages() {
    return Arrays.<ReactPackage>asList(
    new MainReactPackage(),
    new RoundImageViewReactPackage()
    );
    }

    @Override
    protected String getJSMainModuleName() {
    return "index";
    }
    };
  3. 实现对应的JavaScript模块

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    import { PropTypes } from 'prop-types';
    import { requireNativeComponent, View } from 'react-native';

    const RoundImageView = requireNativeComponent('RoundImageView', {
    propTypes: {
    name: PropTypes.string,
    type: PropTypes.oneOf(['circle', 'round', 'oval']),
    roundradius: PropTypes.number,
    ...View.propTypes // 包含默认的View的属性
    },
    });

    export default RoundImageView;
  4. 在js中使用该组件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    import React, { Component } from 'react';
    import { Text, View, StyleSheet } from 'react-native';
    import RoundImageView from './RoundImageView';

    export default class RoundImageViewTest extends Component {
    render() {
    return (
    <View style={styles.container}>
    <RoundImageView style={{width: 100, height: 100}} name="icon_rectangle" type="circle" />
    <RoundImageView style={{width: 100, height: 100}} name="icon_rectangle" type="round" roundradius={30} />
    <RoundImageView style={{width: 80, height: 100}} name="icon_rectangle" type="oval" roundradius={45} />
    </View>
    );
    }
    }

    const styles = StyleSheet.create({
    container: {
    flex: 1,
    flexDirection: 'row',
    justifyContent: 'space-around',
    },
    });

图片

0%