React-Native系列Android自定义原生UI组件

来源:互联网 发布:安徽博微智能电气知乎 编辑:程序博客网 时间:2024/06/06 14:15

由于官方的Android原生UI组件解释的并不是很完整,根据个人的不断摸索,终于成功完成原生组件的制作,所以写下这篇文章作为记录,也给让小白们少走些弯路。

我这里通过讲解制作一个绘圆组件的流程,来学习制作android原生UI组件。这个绘圆组件并没有实际的使用价值,只是为了更容易的了解android原生UI组件的制作过程。好了废话不多说,现在开始吧。

  • react-native版本:0.33.0

首先初始化react-native项目,最好能弄个VPN

react-native init AndroidNativeModule

初次build android项目,先打开虚拟机或连接手机。

react-native run-android

项目的创建就讲到这,详细的项目创建和排错请自行百度。现在我们用Android Stuido进入android项目。

  • 创建基本的UI组件框架,这里组件名称严格更具功能进行命名,为了看的更清楚进行了一些打包
    项目目录

    • CircleManager.java CIrcle原生组件管理器,实现JS和JAVA信息传递
    • CircleView.java Circle原生组件
    • MainPackage.java 自定义组件注册包

接下来给创建好的java添加基本结构,然后在后面更具需要添加细节功能

  • CircleView.java 基础结构
package com.androidnativemodule.module.circle;import android.content.Context;import android.view.View;/** * 圆形组件组件基础类 */public class CircleView extends View {    public CircleView(Context context) {        super(context);    }}

UI组件继承于View类,这里没有加什么功能,就创建一个基本的类

  • CircleManager.java
package com.androidnativemodule.module.circle;import com.facebook.react.uimanager.SimpleViewManager;import com.facebook.react.uimanager.ThemedReactContext;/** * 圆形组件基础类管理器 */public class CircleManager extends SimpleViewManager<CircleView> {    /**     * 设置js引用名     * @return String     */    @Override    public String getName() {        return "MCircle";    }    /**     * 创建UI组件实例     * @param reactContext     * @return CircleView     */    @Override    protected CircleView createViewInstance(ThemedReactContext reactContext) {        return new CircleView(reactContext);    }}

所有的组件管理器需要继承SimpleViewManager类,后面加上自己定义的组件基础类,下面2个方法是SimpleViewManager类的必须实现的方法,记住getName()返回的名字要和JS里的应用名进行统一。

  • MainPackge.java
package com.androidnativemodule;import com.androidnativemodule.module.circle.CircleManager;import com.facebook.react.ReactPackage;import com.facebook.react.bridge.JavaScriptModule;import com.facebook.react.bridge.NativeModule;import com.facebook.react.bridge.ReactApplicationContext;import com.facebook.react.uimanager.ViewManager;import java.util.Arrays;import java.util.Collections;import java.util.List;/** * 自定义组件模块注册类 */public class MainPackage implements ReactPackage {    /**     * 创建原生模块     * @param reactContext     * @return     */    @Override    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {        return Collections.emptyList();    }    @Override    public List<Class<? extends JavaScriptModule>> createJSModules() {        return Collections.emptyList();    }    /**     * 创建原生UI组件控制器     * @param reactContext     * @return     */    @Override    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {        return Arrays.<ViewManager>asList(            new CircleManager()        );    }}

这个是注册类,实现ReactPackage接口,可以想自己设计的组件统一写在这个类中,然后一起在Application中注册,注意:返回空值需要返回Collections.emptyList()。它还可以注册原生模块,这个官方有详细的讲解,地址如下:http://facebook.github.io/react-native/docs/native-modules-android.html

注册类写好后,还需要将这个类加载进Application中,我们在通过MainApplication中将其加入进去。

  • MainApplication.java
package com.androidnativemodule;import android.app.Application;import android.util.Log;import com.facebook.react.ReactApplication;import com.facebook.react.ReactInstanceManager;import com.facebook.react.ReactNativeHost;import com.facebook.react.ReactPackage;import com.facebook.react.shell.MainReactPackage;import java.util.Arrays;import java.util.List;public class MainApplication extends Application implements ReactApplication {  private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {    @Override    protected boolean getUseDeveloperSupport() {      return BuildConfig.DEBUG;    }    @Override    protected List<ReactPackage> getPackages() {      return Arrays.<ReactPackage>asList(          new MainReactPackage(),          new MainPackage() // 在这里加载我们自己的注册类      );    }  };  @Override  public ReactNativeHost getReactNativeHost() {      return mReactNativeHost;  }}

到这里我们的自定义UI组件框架基本完成,我们可以run一下看看,如果没有效果,可以尝试重新react-native run-android 下。

在Android Monitor中可以看到已经加载了我们的CircleModule,只有有没我们JS并没有实例话他,所以Could not find generated setter for class com.androidnativemodule.module.circle.CircleManager
Android Monitor截图

接下来我们给我们的原生UI组件加上功能。

  • CircleView.java
package com.androidnativemodule.module.circle;import android.content.Context;import android.graphics.Canvas;import android.graphics.Paint;import android.util.Log;import android.view.View;/** * 圆形组件组件基础类 */public class CircleView extends View {    private final String TAG = "CircleView";    private Paint mPaint; // 画笔    public CircleView(Context context) {        super(context);        mPaint = new Paint();    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        canvas.drawCircle(100, 100, 100, mPaint); // 画一个半径为100px的圆        Log.d(TAG, "绘图");    }}

这里我先在CircleView内写死代码,看看这个原生UI到底能不能在JS中使用

现在我们来到react-native项目,我们先定制一个Circle组件的JS接口,方便其他组件调用。

  • 创建Circle.js
import { PropTypes } from 'react';import { requireNativeComponent, View } from 'react-native';const MCircle = requireNativeComponent('MCircle', {  propTypes: {    ...View.propTypes // 包含默认的View的属性  },});export default MCircle;

使用requireNativeComponent根据先前在管理器中定义好的组件名引用原生组件,由于我们还没创建接口,所以这个组件暂时只有父类View的接口。

现在我们在index.android.js中实例化组件看看效果

  • index.android.js
'use strict';import React, { Component } from 'react';import {  AppRegistry,  StyleSheet,  Text,  View} from 'react-native';import Circle from './Circle';class AndroidNativeModule extends Component {  render() {    return (      <View style={styles.container}>        <Circle style={{width: 100, height: 100}} />      </View>    );  }}const styles = StyleSheet.create({  container: {    flex: 1,    justifyContent: 'center',    alignItems: 'center',    backgroundColor: '#F5FCFF',  }});AppRegistry.registerComponent('AndroidNativeModule', () => AndroidNativeModule);

效果图:
模拟器截图

可以看到成功使用Android canvas 画了一个圆

现在我们给他加一些接口,从而实现JS和JAVA的通讯,直接在JS中更改组件样式。

  • CircleView.java
package com.androidnativemodule.module.circle;import android.content.Context;import android.graphics.Canvas;import android.graphics.Paint;import android.util.Log;import android.view.View;import com.facebook.react.uimanager.PixelUtil;/** * 圆形组件组件基础类 */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();    }    /**     * 设置圆的背景色     * @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, "绘图");    }}

这里添加了2个接口,实现对圆的大小和颜色更改,还使用了PixelUtil实现像素的转换

  • CircleManager.java
package com.androidnativemodule.module.circle;import com.facebook.react.uimanager.SimpleViewManager;import com.facebook.react.uimanager.ThemedReactContext;import com.facebook.react.uimanager.annotations.ReactProp;/** * 圆形组件基础类管理器 */public class CircleManager extends SimpleViewManager<CircleView> {    /**     * 设置js引用名     * @return String     */    @Override    public String getName() {        return "MCircle";    }    /**     * 创建UI组件实例     * @param reactContext     * @return CircleView     */    @Override    protected CircleView createViewInstance(ThemedReactContext reactContext) {        return new CircleView(reactContext);    }    /**     * 传输背景色参数     * @param view     * @param color     */    @ReactProp(name = "color")    public void setColor(CircleView view, Integer color) {        view.setColor(color);    }    /**     * 传输半径参数     * @param view     * @param radius     */    @ReactProp(name = "radius")    public void setRadius(CircleView view, Integer radius) {        view.setRadius(radius);    }}

管理器中引用接口,将参数传输过去

现在我们的CircleModule已经完成了,然后我们修改下Circle组件JS接口,实现接口使用。

  • Circle.js
'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 (      <MCircle        style={style}        radius={radius}        color={processColor(color)}      />    );  }}module.exports = Circle;

由于color使用的是String,我们可以用react-native的processColor将其转换为数字,从而可以让java识别出颜色,为了方便使用,所以我这边重写创建了Component,作为中间组件,对color进行转换。

然后我的就可以通过Circle.js使用CircleModule了,例如:

<Circle  style={{width: 100, height: 100}}  color="#25c5f7"  radius={50}/>

效果图:
模拟器截图

到此Android自定义原生UI组件CircleModule的设计就结束了,如果有错误的地方,大家可以指出来反馈给我,也希望大家可以将自己的react-native开发心得分享出来,大家一起来学习。

4 0
原创粉丝点击