RN开发过程中,React Native是将原生控件封装桥接成JS组件来使用的,这保证了其性能的高效性。但是有时候官方封装的常用组件不能满足需求,就需要结合原生UI使用,例如:对原生实现的UI复用;复杂UI仍然需要原生自定义View实现。接下来就简单记录下RN开发过程中调用原生UI的流程。

例如下面这个UI效果,就需要用到Android原生自定义View实现:

以上图实现效果为例,分别从Android端和RN端说明。

Android端

1. 创建自定义控件CircleMenu;

public class CircleMenu extends View {    private Context mContext;    /** 点击外面 */    public static final int DL_TOUCH_OUTSIDE = -2;    /** 点击中间点 */    public static final int DL_TOUCH_CENTER = -1;    /** 中心点的坐标X */    private float mCoreX;    /** 中心点的坐标Y */    private float mCoreY;    /** 是否有中心按钮 */    private boolean mHasCoreMenu;    /** 中心按钮的默认背景 */    private int mCoreMenuNormalBackgroundColor;    /** 中间按钮的描边颜色 */    private int mCoreMenuStrokeColor;    /** 中间按钮的描边边框大小 */    private float mCoreMenuStrokeSize;    /** 中间按钮选中时的背景颜色 */    private int mCoreMenuSelectedBackgroundColor;    /** 中心按钮圆形半径 */    private float mCoreMenuRoundRadius;    /** 菜单数量 */    private int mRoundMenuNumber;    /** 菜单偏移角度 */    private float mRoundMenuDeviationDegree;    /** 菜单图片 */    private ArrayList<Bitmap> mRoundMenuDrawableList = new ArrayList<>();    /** 是否画每个菜单扇形到中心点的直线 */    private boolean mIsDrawLineToCenter;    /** 菜单正常背景颜色 */    private int mRoundMenuNormalBackgroundColor;    /** 菜单点击背景颜色 */    private int mRoundMenuSelectedBackgroundColor;    /** 菜单描边颜色 */    private int mRoundMenuStrokeColor;    /** 菜单描边宽度 */    private float mRoundMenuStrokeSize;    /** 菜单图片与中心点的距离 百分数 */    private float mRoundMenuDistance;    /** 点击状态 -2是无点击,-1是点击中心圆,其他是点击菜单 */    private int onClickState = DL_TOUCH_OUTSIDE;    /** 记录按下时间,超过预设时间算长按按钮 */    private long mTouchTime;    public CircleMenu(Context context) {        super(context);        init(context, null);    }    public CircleMenu(Context context, @Nullable AttributeSet attrs) {        super(context, attrs);        init(context, attrs);    }    public CircleMenu(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init(context, attrs);    }    /**     * 初始化数据     * @param context     * @param attrs     */    private void init(Context context, @Nullable AttributeSet attrs) {        mContext = context;        // 加载默认资源        final Resources res = getResources();        final boolean defaultHasCoreMenu = res.getBoolean(R.bool.default_has_core_menu);        final int defaultCoreMenuNormalBackgroundColor = res.getColor(R.color.default_core_menu_normal_background_color);        final int defaultCoreMenuStrokeColor = res.getColor(R.color.default_core_menu_stroke_color);        final float defaultCoreMenuStrokeSize = res.getDimension(R.dimen.default_core_menu_stroke_size);        final int defaultCoreMenuSelectedBackgroundColor = res.getColor(R.color.default_core_menu_selected_background_color);        final float defaultCoreMenuRoundRadius = res.getDimension(R.dimen.default_core_menu_round_radius);        final int defaultRoundMenuNumber = res.getInteger(R.integer.default_round_menu_number);        final int defaultRoundMenuDeviationDegree = res.getInteger(R.integer.default_round_menu_deviation_degree);        final boolean defaultIsDrawLineToCenter = res.getBoolean(R.bool.default_is_draw_line_to_center);        final int defaultRoundMenuNormalBackgroundColor = res.getColor(R.color.default_round_menu_normal_background_color);        final int defaultRoundMenuSelectedBackgroundColor = res.getColor(R.color.default_round_menu_selected_background_color);        final int defaultRoundMenuStrokeColor = res.getColor(R.color.default_round_menu_stroke_color);        final float defaultRoundMenuStrokeSize = res.getDimension(R.dimen.default_round_menu_stroke_size);        final float defaultRoundMenuDistance = res.getFraction(R.fraction.default_round_menu_distance, 1, 1);        // 读取配置信息        TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.DLRoundMenuView);        mHasCoreMenu = a.getBoolean(R.styleable.DLRoundMenuView_RMHasCoreMenu, defaultHasCoreMenu);        mCoreMenuNormalBackgroundColor = a.getColor(R.styleable.DLRoundMenuView_RMCoreMenuNormalBackgroundColor, defaultCoreMenuNormalBackgroundColor);        mCoreMenuStrokeColor = a.getColor(R.styleable.DLRoundMenuView_RMCoreMenuStrokeColor, defaultCoreMenuStrokeColor);        mCoreMenuStrokeSize = a.getDimension(R.styleable.DLRoundMenuView_RMCoreMenuStrokeSize, defaultCoreMenuStrokeSize);        mCoreMenuSelectedBackgroundColor = a.getColor(R.styleable.DLRoundMenuView_RMCoreMenuSelectedBackgroundColor, defaultCoreMenuSelectedBackgroundColor);        mCoreMenuRoundRadius = a.getDimension(R.styleable.DLRoundMenuView_RMCoreMenuRoundRadius, defaultCoreMenuRoundRadius);        mRoundMenuNumber = a.getInteger(R.styleable.DLRoundMenuView_RMRoundMenuNumber, defaultRoundMenuNumber);        mRoundMenuDeviationDegree = a.getInteger(R.styleable.DLRoundMenuView_RMRoundMenuDeviationDegree, defaultRoundMenuDeviationDegree);        mIsDrawLineToCenter = a.getBoolean(R.styleable.DLRoundMenuView_RMIsDrawLineToCenter, defaultIsDrawLineToCenter);        mRoundMenuNormalBackgroundColor = a.getColor(R.styleable.DLRoundMenuView_RMRoundMenuNormalBackgroundColor, defaultRoundMenuNormalBackgroundColor);        mRoundMenuSelectedBackgroundColor = a.getColor(R.styleable.DLRoundMenuView_RMRoundMenuSelectedBackgroundColor, defaultRoundMenuSelectedBackgroundColor);        mRoundMenuStrokeColor = a.getColor(R.styleable.DLRoundMenuView_RMRoundMenuStrokeColor, defaultRoundMenuStrokeColor);        mRoundMenuStrokeSize = a.getDimension(R.styleable.DLRoundMenuView_RMRoundMenuStrokeSize, defaultRoundMenuStrokeSize);        mRoundMenuDistance = a.getFraction(R.styleable.DLRoundMenuView_RMRoundMenuDistance, 1, 1, defaultRoundMenuDistance);        // 释放内存,回收资源        a.recycle();    }    /**     * 测量     * @param widthMeasureSpec     * @param heightMeasureSpec     */    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);        // 左右减去2,留足空间给算术裁切        setMeasuredDimension(widthSpecSize - 2, heightSpecSize - 2);    }    /**     * 绘制     * @param canvas     */    @SuppressLint("DrawAllocation")    @Override    protected void onDraw(Canvas canvas) {        // 拿到中心位置        mCoreX = (float) getWidth() / 2;        mCoreY = (float) getHeight() / 2;        // 搞到一个正方形画板        RectF rect = new RectF(mRoundMenuStrokeSize, mRoundMenuStrokeSize,                getWidth() - mRoundMenuStrokeSize, getHeight() - mRoundMenuStrokeSize);        // 菜单数量要大于0        if (mRoundMenuNumber > 0) {            // 每个菜单弧形的角度            float sweepAngle = (float) 360 / mRoundMenuNumber;            // 一个重要的点 0度在正X轴上,所以需要让它回到正Y轴上            // 计算真正的偏移角度            // -90度回到正Y轴;-每个菜单占据角度的一半,使得菜单中央回到正Y轴;再加上用户自己想修改的角度偏移            /** 真实菜单偏移角度 */            float mRealRoundMenuDeviationDegree = mRoundMenuDeviationDegree - (sweepAngle / 2) - 90;            for (int i = 0; i < mRoundMenuNumber; i++) {                // 画扇形                Paint paint = new Paint();                paint.setAntiAlias(true);                paint.setColor(onClickState == i?mRoundMenuSelectedBackgroundColor:mRoundMenuNormalBackgroundColor);                canvas.drawArc(rect, mRealRoundMenuDeviationDegree + (i * sweepAngle), sweepAngle, true, paint);                // 画扇形描边                paint = new Paint();                paint.setAntiAlias(true);                paint.setStrokeWidth(mRoundMenuStrokeSize);                paint.setStyle(Paint.Style.STROKE);                paint.setColor(mRoundMenuStrokeColor);                canvas.drawArc(rect, mRealRoundMenuDeviationDegree + (i * sweepAngle), sweepAngle, mIsDrawLineToCenter, paint);                // 画图案                Bitmap roundMenuDrawable = mRoundMenuDrawableList.get(i);                if (null != roundMenuDrawable){                    Matrix matrix = new Matrix();                    matrix.postTranslate((float) ((mCoreX + getWidth() / 2 * mRoundMenuDistance) - (roundMenuDrawable.getWidth() / 2)),                            mCoreY - ((float) roundMenuDrawable.getHeight() / 2));                    matrix.postRotate(mRoundMenuDeviationDegree - 90 + (i * sweepAngle), mCoreX, mCoreY);                    canvas.drawBitmap(roundMenuDrawable, matrix, null);                }            }        }        //画中心圆圈        if (mHasCoreMenu) {            // 画中心圆            RectF rect1 = new RectF(mCoreX - mCoreMenuRoundRadius, mCoreY - mCoreMenuRoundRadius,                    mCoreX + mCoreMenuRoundRadius, mCoreY + mCoreMenuRoundRadius);            Paint paint = new Paint();            paint.setAntiAlias(true);            paint.setStrokeWidth(mCoreMenuStrokeSize);            paint.setColor(onClickState == -1?mCoreMenuSelectedBackgroundColor:mCoreMenuNormalBackgroundColor);            canvas.drawArc(rect1, 0, 360, true, paint);            //画描边            paint = new Paint();            paint.setAntiAlias(true);            paint.setStrokeWidth(mCoreMenuStrokeSize);            paint.setStyle(Paint.Style.STROKE);            paint.setColor(mCoreMenuStrokeColor);            canvas.drawArc(rect1, 0, 360, true, paint);        }    }}

2. 创建ReactCircleMenuManager类继承自SimpleViewManager,供RN调用;

public class ReactCircleMenuManager extends SimpleViewManager {    @Override    public String getName() {        return "ReactCircleMenu";//    }    @Override    protected View createViewInstance(final ThemedReactContext reactContext) {        //final View view = LayoutInflater.from(reactContext).inflate(R.layout.shadow_layout, null);        final CircleMenuView circleMenuView = new CircleMenuView(reactContext);        //        circleMenuView.setOnMenuClickListener(new OnMenuClickListener() {//            @Override//            public void OnMenuClick(int position) {//                Log.e("TAG", "点击了:"+position);//                WritableMap event = Arguments.createMap();//                event.putInt("position", position);//                reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(circleMenuView.getId(), "onMenuClick", event);//            }//        });        return circleMenuView;    }    /**     * 接收传输的颜色参数     */    @ReactProp(name = "color")    public void setColor(View view, String color) {        view.setBackgroundColor(Color.parseColor(color));    }/**     * 回传点击事件到RN     * @return     *///    @Nullable//    @Override//    public Map getExportedCustomDirectEventTypeConstants() {//        return MapBuilder.builder()//                .put("onMenuClick", MapBuilder.of("registrationName", "onMenuClick"))//                .build();//    }}

3. 创建ReactCircleMenuPackage类并实现ReactPackage,在createViewManagers方法中返回CircleManager的实例。

public class ReactCircleMenuPackage implements ReactPackage {    /**     * 用户注册Native Modules     */    @Override    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {        return Collections.emptyList();    }    /**     * 用于注册Native UI Components     * @param reactContext     * @return     */    @Override    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {        return Collections.<ViewManager>singletonList(                new ReactCircleMenuManager()        );    }}

4. 在Application中注册ReactCircleMenuPackage

public class MainApplication extends MultiDexApplication implements ReactApplication {    private static MainApplication instance;    public static MainApplication getInstance() {        return instance;    }    private ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {        @Override        public boolean getUseDeveloperSupport() {            return BuildConfig.DEBUG;        }        @Override        protected List<ReactPackage> getPackages() {            return Arrays.<ReactPackage>asList(                    new MainReactPackage(),                    new ReactCircleMenuPackage()//添加此处            );        }        @Override        protected String getJSMainModuleName() {            return "index";        }    };    public void setReactNativeHost(ReactNativeHost reactNativeHost) {        mReactNativeHost = reactNativeHost;    }    @Override    public ReactNativeHost getReactNativeHost() {        return mReactNativeHost;    }    @Override    public void onCreate() {        super.onCreate();        instance = this;        SoLoader.init(this, /* native exopackage */ false);    }   }

ReactNative端

  1. 创建创建circleMenu.js文件,并通过requireNativeComponent创建变量ReactCircleMenu;
import React, { Component } from "react";import PropTypes from "prop-types";import { View, requireNativeComponent } from "react-native";//requireNativeComponent函数中的第一个参数就是ReactCircleMenuManager.getName返回的值。const RCTCircleMenu = requireNativeComponent("ReactCircleMenu", {    propTypes: {        color: PropTypes.number,//定义传输属性类型        ...View.propTypes // 包含默认的View的属性    }});export default class CircleMenu extends Component {    render() {        return (            <RCTCircleMenu                color={color.primary}//自定义颜色                style={{ width: 175,height: 175}}                onMenuClick={this.onMenuClick.bind(this)}            />        );    }    /**     * 点击事件     */    onMenuClick(e){        console.log(e.nativeEvent.position.toString());    }}
  1. 调用CircleMenu.js文件
import React, {Component} from "react";import {View, requireNativeComponent} from "react-native";import CircleMenu from "../../widget/CircleMenu";export default class DevPhoneIR extends Component {    render() {        return (            <CircleMenu/>        );    }}

这样就实现了RN调用封装的原生组件,上边代码中可以看出,RN可以自定义属性给Android原生View,Android原生组件的点击事件也可以回传给RN。

当然也可以把原生xml布局封装为View形式使用,只需修改ReactCircleMenuManager类里createViewInstance方法的返回View即可。

@Override    protected View createViewInstance(final ThemedReactContext reactContext) {        final Vibrator vibrator = (Vibrator)reactContext.getSystemService(reactContext.VIBRATOR_SERVICE);        final View view = LayoutInflater.from(reactContext).inflate(R.layout.layout, null);        final TextView textView = view.findViewById(R.id.text);        final Button btn = view.findViewById(R.id.btn);        textView.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                Log.e("TAG", "点击了:");            }        });        btn.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                Log.e("TAG", "点击了1:");                ConsumerIrManagerApi.getIrManager(reactContext).transmit(Constants.FREQUENCY, NecPattern.buildPattern(                        Constants.TV_USER_CODE_H,Constants.TV_USER_CODE_L, 0x14));                vibrator.vibrate(80);                WritableMap event = Arguments.createMap();                event.putInt("position", 0);                reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(view.getId(), "onMenuClick", event);            }        });        return view;    }

更多相关文章

  1. Android延时启动效果+轮播图+点击进入+沉浸式状态栏+按钮点击颜
  2. Android自定义GridView之仿支付宝首页可拖动、可删除的九宫格
  3. Android-小巫新闻客户端底部菜单切换实现
  4. Android(安卓)L中水波纹点击效果的实现
  5. android DownloadManager广播事件:下载完成、通知栏点击事件
  6. Android实现加载网页,获取网页上图以及点击图片预览图片
  7. Android使用RadioButton结合ListView显示对话框单选按钮列表
  8. Android的自定义图片按钮ImageButton【第一篇】
  9. android button 背景样式

随机推荐

  1. Android(安卓)ListView 实现 GridView
  2. Android滑动组件----RecyclerView并且实
  3. android 仿iphone主题之主菜单
  4. [转]创建不可见的Activity
  5. android触摸事件传递机制以及onIntercept
  6. android设置屏幕方向与自动感应切换
  7. Android(安卓)RecycleView实现不同样式It
  8. android EditText属性详解
  9. 工欲善其事必先利其器之Android环境搭建
  10. 【Android】一键清理后台实现【附源码】