如何创建一个android的react-react native 组件库组件

react-native(1)
接着上一篇文章,这一篇我把自己上传到npm上的开发步骤和大家分享。
下载react-native组件命令:
$ npm install react-native-segmented-android --save
这是效果图:
这次要实现的是View组件,所以要通过继承SimpleViewManager 来实现,步骤和(一)基本保持一致。
Step 1 - 新建react-native工程 ReactNativeSegmentedAndroid
$ react-native init ReactNativeSegmentedAndroid
Step 2 - 将新建的工程导入android studio然后新建空library(以react-native-segmented-android为library的名称)
Step 3 - 新建空library(以react-native-segmented-android为library的名称)
在library目录下的build.gradle中添加react-native的依赖
// file: android/react-native-segmented-android/build.gradle
dependencies {
compile 'info.hoang8f:android-segmented:1.0.6'
compile 'com.facebook.react:react-native:0.16.+'
Step 4 - 创建AndroidSegmented类继承SegmentedGroup
public class AndroidSegmented extends SegmentedGroup{
public void setSegmentOrientation(String str){
if(str.equals("horizontal")){
setOrientation(RadioGroup.HORIZONTAL);
}else if(str.equals("vertical")){
setOrientation(RadioGroup.VERTICAL);
public AndroidSegmented(ThemedReactContext context) {
super(context);
setGravity(Gravity.CENTER);
setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
private final Runnable mLayoutRunnable = new Runnable() {
public void run() {
measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY));
layout(getLeft(), getTop(), getRight(), getBottom());
public void requestLayout() {
super.requestLayout();
post(mLayoutRunnable);
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
Step 5 - 继承SimpleViewManager,注意这时就不是继承ReactContextBaseJavaModule了 ,大家可以很明显的发现setChildText()方法上多了一个‘@ReactProp(name = “childText”)’,加上了‘@ReactProp’的,segmented控件多了一个name为childText的属性,值为ReadableArray ( js端代码:childText={[‘One’,’Two’,’Three’,’Four’,”Five”]})。
public class AndroidSegmentedManager extends SimpleViewManager&AndroidSegmented& {
public static final String REACT_CLASS = "AndroidSegmented";
private static final String COLOR_REGEX = "^#([0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$";
public String getName() {
return REACT_CLASS;
protected AndroidSegmented createViewInstance(ThemedReactContext reactContext) {
this.context = reactC
return new AndroidSegmented(reactContext);
protected void addEventEmitters(final ThemedReactContext reactContext, final AndroidSegmented view) {
view.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
public void onCheckedChanged(RadioGroup group, int checkedId) {
reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher()
.dispatchEvent(
new AndroidSegmentedEvent(
view.getId(),
SystemClock.uptimeMillis(),
checkedId));
@ReactProp(name = "childText")
public void setChildText(AndroidSegmented view, ReadableArray data) {
int childCount = data.size();
Log.e("TAG", "___" + childCount);
for (int i = 0; i & childC i++) {
RadioButton child = (RadioButton) LayoutInflater.from(context).inflate(R.layout.radio_button, null);
child.setText(data.getString(i));
view.addView(child);
@ReactProp(name = "selectedPosition")
public void setSelectedChild(AndroidSegmented view, int position) {
RadioButton radioBt= (RadioButton)(view.getChildAt(position));
radioBt.setChecked(true);
@ReactProp(name = "orientation")
public void setOrientation(AndroidSegmented view, String orientation) {
view.setSegmentOrientation(orientation);
@ReactProp(name = "tintColor")
public void setTintColor(AndroidSegmented view, ReadableArray data) {
String type0 = data.getType(0).name();
String type1 = data.getType(1).name();
if ("String".equals(type0) && "String".equals(type1)) {
String color0 = data.getString(0);
String color1 = data.getString(1);
if (color0 != null && color1 != null) {
if (color0.matches(COLOR_REGEX) && color1.matches(COLOR_REGEX)) {
view.setTintColor(Color.parseColor(color0), Color.parseColor(color1));
throw new JSApplicationIllegalArgumentException("Invalid arrowColor property: " + color0);
Step 6 - 创建AndroidSegmentedEvent类继承Event
public class AndroidSegmentedEvent extends Event&AndroidSegmentedEvent& {
public static final String EVENT_NAME = "topChange";
private final int selectedP
public AndroidSegmentedEvent(int viewId, long timestampMs, int selectedPosition) {
super(viewId, timestampMs);
this.selectedPosition = selectedP
public String getEventName() {
return EVENT_NAME;
public void dispatch(RCTEventEmitter rctEventEmitter) {
rctEventEmitter.receiveEvent(getViewTag(), getEventName(), serializeEventData());
public short getCoalescingKey() {
private WritableMap serializeEventData() {
WritableMap eventData = Arguments.createMap();
eventData.putInt("selected", getPosition());
Log.e("AAA","position="+getPosition());
return eventD
private int getPosition() {
return selectedP
Step 7 - 继承ReactPackage,注意createNativeModules()返回的是加入了 AndroidToastModule 的集合,createJSModules()与createViewManagers()返回的都是空集合,如果Step 4 步继承的是BaseViewManager或其子类,那么createViewManagers()中返回的就是加入了BaseViewManager的集合,其他的就是空集合,一般情况createJSModules()的返回值都是空集合。
public class AndroidSegmentedPackage implements ReactPackage {
public List&NativeModule& createNativeModules(ReactApplicationContext reactContext) {
return Collections.emptyList();
public List&Class&? extends JavaScriptModule&& createJSModules() {
return Collections.emptyList();
public List&ViewManager& createViewManagers(ReactApplicationContext reactContext) {
return Arrays.&ViewManager&asList(new AndroidSegmentedManager());
Step 8 - 新建AndroidSegmented.js,文件位置
‘ android/react-native-segmented-android/AndroidSegmented.js ’代码如下,然后在 ‘android/react-native-segmented-android/’下运行如下命令生成package.json文件
$ npm init
'use strict';
var React = require('react-native');
var { requireNativeComponent, PropTypes, View } = R
var NativeAndroidSegmented = requireNativeComponent('AndroidSegmented', AndroidSegmented);
class AndroidSegmented ponent {
constructor() {
this._onChange = this._onChange.bind(this);
_onChange(event) {
if (this.props.onChange) {
this.props.onChange(event.nativeEvent);
render() {
{...this.props}
onChange={this._onChange}/&
var colorType = function (props, propName, componentName) {
var checker = function() {
var color = props[propName];
var regex = /^#([0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/;
if (!regex.test(color)) {
return new Error('Only accept color formats: #RRGGBB and #AARRGGBB');
return PropTypes.string(props, propName, componentName) || checker();
AndroidSegmented.propTypes = {
...View.propTypes,
childText: PropTypes.arrayOf(PropTypes.oneOfType([ PropTypes.string ])),
orientation:PropTypes.string,
tintColor:PropTypes.arrayOf(PropTypes.oneOfType([ PropTypes.string ])),
selectedPosition:PropTypes.number,
onChange: PropTypes.func,
AndroidSegmented.defaultProps = {
module.exports = AndroidS
//package.json内容
"name": "react-native-segmented-android",
"version": "1.0.3",
"description": "a high imitation of iOS segmented Controls",
"main": "AndroidSegmented.js",
"scripts": {
"test": "react-native start"
"repository": {
"type": "git",
"url": "/zzyyppqq/react-native-segmented-android.git"
"keywords": [
"android",
"segmented",
"react-component",
"react-native"
"author": "zzyyppqq",
"license": "ISC",
"peerDependencies": {
"react-native": "^1.0.3"
复制AndroidSegmented.js 文件到‘/ReactNativeSegmentedAndroid/ ’ 目录下,如下是index.android.js代码,然后运行测试
'use strict';
var React = require('react-native');
AppRegistry,
StyleSheet,
Dimensions,
ToastAndroid,
var AndroidSegmented = require('./AndroidSegmented');
var deviceWidth = Dimensions.get('window').
var deviceHeight = Dimensions.get('window').
var ReactNativeSegmentedExample = React.createClass({
onSelectPosition:function(event){
console.log(event);
ToastAndroid.show('segment '+event.selected, ToastAndroid.SHORT)
render: function() {
tintColor={['#ff0000','#ffffff']}
style={{width:deviceWidth,height:60,backgroundColor:'#fff000',
justifyContent: 'center',
alignItems: 'center'}}
childText={['One','Two','Three','Four',"Five"]}
orientation='horizontal'
selectedPosition={0}
onChange={this.onSelectPosition} /&
tintColor={['#009688','#ffffff']}
style={{width:deviceWidth,height:200,backgroundColor:'#fff000',
justifyContent: 'center',
alignItems: 'center'}}
childText={['One','Two','Three','Four',"Five"]}
orientation='vertical'
selectedPosition={0}
onChange={this.onSelectPosition} /&
Step 1 - Install the npm package
$ npm install react-native-degment-android --save
// file: android/settings.gradle
include ':react-native-degment-android', ':app'
project(':react-native-degment-android').projectDir = new File(rootProject.projectDir,'../node_modules/react-native-degment-android')
// file: android/app/build.gradle
dependencies {
compile project(':react-native-degment-android')
Step 4 - Register React Package
import com.higo.zhangyp.segmented.AndroidSegmentedP
public class MainActivity extends FragmentActivity implements DefaultHardwareBackBtnHandler {
private ReactInstanceManager mReactInstanceM
private ReactRootView mReactRootV
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mReactRootView = new ReactRootView(this);
mReactInstanceManager = ReactInstanceManager.builder()
.setApplication(getApplication())
.setBundleAssetName("index.android.bundle")
.setJSMainModuleName("index.android")
.addPackage(new MainReactPackage())
.addPackage(new AndroidSegmentedPackage())
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.RESUMED)
mReactRootView.startReactApplication(mReactInstanceManager, "AwesomeProject", null);
setContentView(mReactRootView);
从react-native的官方文档中我们已经知道facebook的react-native团队已经为我们实现了很多组件,例如 Image、Text、ViewPagerAndroid等,我们在index.android.js中可以直接使用这些组件,这些组件为什么能直接使用呢?
大家会很自然的想到已经封装好了呗。那在哪封装的?如何封装的?其实只要通过命令react-native init ProjectName创建过react-native工程的同学来说,在哪儿封装的一目了然。我们来看react-native工程的结构图:
react-native工程中,在node_modules下有一个很特别的react-native文件夹,android的工程中的build.gradle 文件多了一个依赖,不用想肯定在这两个地方封装的,这也是react-native的关键。
dependencies {
compile 'com.facebook.react:react-native:0.16.+'
首先我们从入口MainActivity开始,看了我的前两篇文章,如何自定义react-native的android组件和,要使用一个自定义组件,必须在MainActivity中加入【.addPackage(new AndroidSegmentedPackage())
】才能使用。
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mReactRootView = new ReactRootView(this);
mReactInstanceManager = ReactInstanceManager.builder()
.setApplication(getApplication())
.setBundleAssetName("index.android.bundle")
.setJSMainModuleName("index.android")
.addPackage(new MainReactPackage())
.addPackage(new AndroidSegmentedPackage())
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.RESUMED)
mReactRootView.startReactApplication(mReactInstanceManager, "ReactNativeSegmented", null);
setContentView(mReactRootView);
那么官方的Android组件是如何实现的呢,我们肯定注意到了【.addPackage(new MainReactPackage())】和自定义的是不是很像,格式也一样,我想肯定在这里面有实现,进入MainReactPackage类中,代码如下:
public class MainReactPackage implements ReactPackage {
public List&NativeModule& createNativeModules(ReactApplicationContext reactContext) {
return Arrays.&NativeModule&asList(
new AsyncStorageModule(reactContext),
new FrescoModule(reactContext),
new IntentModule(reactContext),
new LocationModule(reactContext),
new NetworkingModule(reactContext),
new WebSocketModule(reactContext),
new ToastModule(reactContext));
public List&Class&? extends JavaScriptModule&& createJSModules() {
return Collections.emptyList();
public List&ViewManager& createViewManagers(ReactApplicationContext reactContext) {
return Arrays.&ViewManager&asList(
new ReactDrawerLayoutManager(),
new ReactHorizontalScrollViewManager(),
new ReactImageManager(),
new ReactProgressBarViewManager(),
new ReactRawTextManager(),
new ReactScrollViewManager(),
new ReactSwitchManager(),
new ReactTextInputManager(),
new ReactTextViewManager(),
new ReactToolbarManager(),
new ReactViewManager(),
new ReactViewPagerManager(),
new ReactTextInlineImageViewManager(),
new ReactVirtualTextViewManager(),
new SwipeRefreshLayoutManager());
看了MainReactPackage中的代码,果不其然,首先我们看createViewManagers()方法中的集合,看看集合子集的命名是不是很熟悉,
看看这里一共实现了多少原生控件:DrawerLayout、HorizontalScrollView、HorizontalScrollView、Image等等,还有SwipeRefreshLayout官网上还没有更新这个组件,其实这个版本已经可以使用了。
1.public class ReactDrawerLayoutManager extends ViewGroupManager&ReactDrawerLayout&
2.public class ReactImageManager extends SimpleViewManager&ReactImageView&
3.public class ReactProgressBarViewManager extends BaseViewManager&ProgressBarContainerView, ProgressBarShadowNode&
public class ReactDrawerLayoutManager extends ViewGroupManager&ReactDrawerLayout& {
private static final String REACT_CLASS = "AndroidDrawerLayout";
public static final int OPEN_DRAWER = 1;
public static final int CLOSE_DRAWER = 2;
public String getName() {
return REACT_CLASS;
protected void addEventEmitters(ThemedReactContext reactContext, ReactDrawerLayout view) {
view.setDrawerListener(
new DrawerEventEmitter(
reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher()));
protected ReactDrawerLayout createViewInstance(ThemedReactContext context) {
return new ReactDrawerLayout(context);
@ReactProp(name = "drawerPosition", defaultInt = Gravity.START)
public void setDrawerPosition(ReactDrawerLayout view, int drawerPosition) {
if (Gravity.START == drawerPosition || Gravity.END == drawerPosition) {
view.setDrawerPosition(drawerPosition);
throw new JSApplicationIllegalArgumentException("Unknown drawerPosition " + drawerPosition);
@ReactProp(name = "drawerWidth", defaultFloat = Float.NaN)
public void getDrawerWidth(ReactDrawerLayout view, float width) {
int widthInPx = Float.isNaN(width) ?
ReactDrawerLayout.DEFAULT_DRAWER_WIDTH : Math.round(PixelUtil.toPixelFromDIP(width));
view.setDrawerWidth(widthInPx);
public boolean needsCustomLayoutForChildren() {
return true;
public @Nullable Map&String, Integer& getCommandsMap() {
return MapBuilder.of("openDrawer", OPEN_DRAWER, "closeDrawer", CLOSE_DRAWER);
public void receiveCommand(
ReactDrawerLayout root,
int commandId,
@Nullable ReadableArray args) {
switch (commandId) {
case OPEN_DRAWER:
root.openDrawer();
case CLOSE_DRAWER:
root.closeDrawer();
public @Nullable Map getExportedViewConstants() {
return MapBuilder.of(
"DrawerPosition",
MapBuilder.of("Left", Gravity.START, "Right", Gravity.END));
public @Nullable Map getExportedCustomDirectEventTypeConstants() {
return MapBuilder.of(
DrawerSlideEvent.EVENT_NAME, MapBuilder.of("registrationName", "onDrawerSlide"),
DrawerOpenedEvent.EVENT_NAME, MapBuilder.of("registrationName", "onDrawerOpen"),
DrawerClosedEvent.EVENT_NAME, MapBuilder.of("registrationName", "onDrawerClose"),
DrawerStateChangedEvent.EVENT_NAME, MapBuilder.of(
"registrationName", "onDrawerStateChanged"));
* This method is overridden because of two reasons:
* 1. A drawer must have exactly two children
* 2. The second child that is added, is the navigationView, which gets panned from the side.
public void addView(ReactDrawerLayout parent, View child, int index) {
if (getChildCount(parent) &= 2) {
JSApplicationIllegalArgumentException("The Drawer cannot have more than two children");
if (index != 0 && index != 1) {
throw new JSApplicationIllegalArgumentException(
"The only valid indices for drawer's child are 0 or 1. Got " + index + " instead.");
parent.addView(child, index);
parent.setDrawerProperties();
public static class DrawerEventEmitter implements DrawerLayout.DrawerListener {
private final DrawerLayout mDrawerL
private final EventDispatcher mEventD
public DrawerEventEmitter(DrawerLayout drawerLayout, EventDispatcher eventDispatcher) {
mDrawerLayout = drawerL
mEventDispatcher = eventD
public void onDrawerSlide(View view, float v) {
mEventDispatcher.dispatchEvent(
new DrawerSlideEvent(mDrawerLayout.getId(), SystemClock.uptimeMillis(), v));
public void onDrawerOpened(View view) {
mEventDispatcher.dispatchEvent(
new DrawerOpenedEvent(mDrawerLayout.getId(), SystemClock.uptimeMillis()));
public void onDrawerClosed(View view) {
mEventDispatcher.dispatchEvent(
new DrawerClosedEvent(mDrawerLayout.getId(), SystemClock.uptimeMillis()));
public void onDrawerStateChanged(int i) {
mEventDispatcher.dispatchEvent(
new DrawerStateChangedEvent(mDrawerLayout.getId(), SystemClock.uptimeMillis(), i));
原生控件的实现步骤、方法、例子等其实在源码中都有了,想实现什么组件就照着源码开发,绝不会出错啦。
到此只是完成了android端的java代码,那么组件如何与js代码联系起来,并且供js代码调用呢,我们来看看工程中的react-navie文件夹吧,秘密都在它里面。
react-navie文件结构:
打开react-native文件夹,我一眼就注意到了ReactAndroid目录(因为做Android嘛,对含有Android的词比较敏感&_&), 翻遍了其下所有的目录文件,终于找到一个有用点的文件package.json,在其中找到关键的一句话
【”main”: “Libraries/react-native/react-native.js”】图上用红框标注了。
下一步就该看看Libraries目录了,Libraries目录结构:
图上我用红框标注了几个我们熟悉的控件命名的文件家,我们重点关注两个文件夹
Components与CustomComponents
我们看看里面有什么:
红线标注的控件是不是很熟悉,我们随便找一个控件进去看看,就看DrawerAndroid吧,截图如下:
大家遇到的各种不解之处,其实大部分都可以在源码中得到解答,我也在继续学习中,我只是和大家分享我学习的过程,我也只是顺藤摸瓜了解了如何方便的去自定义组件。其实里面的好多ES6语法我也不是特别理解,只是照猫画虎。欢迎大家来吐槽&_&。
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:11829次
排名:千里之外
转载:11篇
(3)(1)(11)使用React Native一年后的感受【android开发吧】_百度贴吧
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&签到排名:今日本吧第个签到,本吧因你更精彩,明天继续来努力!
本吧签到人数:0成为超级会员,使用一键签到本月漏签0次!成为超级会员,赠送8张补签卡连续签到:天&&累计签到:天超级会员单次开通12个月以上,赠送连续签到卡3张
关注:116,705贴子:
使用React Native一年后的感受收藏
引用英文来自:译文来自:当我在面试Discord的时候,技术主管Stanislav跟我说: 引用React Native代表着未来。等它一发布,我们就会用它从零构建iOS应用。作为一名原生iOS开发者,基于先前使用PhoneGap的经验,我非常怀疑使用Web技术构建移动应用的这种方式。但是当我学习并使用React Native一段时间之后,我非常庆幸我们做了这个决定。 开发效率 虽然iOS“团队”只有我自己一个人,但是iOS应用开发依然可以赶上Web和桌面应用开发闪电般的速度。Apple公司已经允许开发者使用JavaScriptCore进行应用的升级,而无需等待App Store的审核流程。这对于那些缺乏专业的iOS QA(质量保障)团队的小公司来说是非常便利的,因为iOS团队可以在发布新功能之后进行热更新。 使用React Native一年之后,我们的iOS开发周期明显变快了,这得益于很高的开发效率。比如: 基于现有的前端架构,我们在两周之内就发布了V1.0的版本。相比于Auto Layout,基于Flexbox的样式可以节省一半的代码,并且更容易理解。使用Flux设计模式,iOS和Web应用共享了store和action的98%的代码。性能 React Native在后台线程运行JavaScript并发送极小的代码到主线程中。事实证明,React Native相比于Objective-C或Swift编写的原生iOS应用来说有一些性能差异! 引用Reactiflux小组的性能演示,该组有超过1.1万个会员 —— UI和JS线程大多数都是60FPS然而,我们当初开始构建iOS应用时发现聊天滚动视图的性能并不令人满意,尤其是一些活跃的聊天分组。于是,我们决定使用ComponentKit构建聊天视图并编写必要的桥接代码代替原有的方案。当JS线程在完成一些繁重任务的时候,类库也无法提供原生那样流畅的动画(译注:之前动画是在JS线程执行,目前有人提交了一份代码,有望使用原生iOS动画接口),因此我们在抽屉侧滑动画上继续使用PopAnimation。 注: 作者称该应用仅聊天视图和抽屉动画是原生代码实现的,其他均由React Native实现。 当React Native Android版本发布时,我们也尝试在Android设备上运行应用,但遗憾的是,我们遇到了一些性能问题,只好暂时放弃。Android开发主管Miguel是这样说的: 引用很遗憾,不同Android设备的性能差异很大,这点明显落后于iOS。我们可以让应用运行地很快,但是性能——尤其是触摸事件,即使在更高端设备上也不能令人满意。并且在早期,由于React Native Android缺乏完善的功能,我们从产品原型过渡到成品应用比iOS花费了更多时间。可用性
React Native让开发工作更简洁,使得开发者可以专注于每个新版本核心功能的开发。应用内自带的开发者菜单为我节省了大量的时间。 其中我最喜欢的一个功能是Show Inspector(审查工具),它可以即时展现交互视图的层级结构以及被选组件中所有必要的样式信息,这无疑是我用过的最棒的iOS审查工具。 社区 React Native项目每两周会发布一个新版本,其中包含一些新的特性以及修复的bug。这有利有弊,好比iOS几个月的稳定版本的发布,新的代码需要额外的时间进行升级,尤其是生产环境中的应用。因此,这也是到目前为止我们fork的React Native仓库只有四次主要升级的原因。 由于React Native还不太成熟,资源有限,也不完整。但随着它越来越流行,在不久之后一定能赶上其他成熟的技术。下面列出了一些实用的资源,我也经常在它的仓库上提问和获取最新的信息: Reactiflux上的#react-native。js.coach—React Native开源组件列表。awesome-react-native—大量的React Native文章、教程和示例。引用译注:中文资源:React Native学习指南总的来说,React Native很有潜力,它把我们团队的移动应用开发带上了一个新的台阶。像我这样原生的iOS开发者可以平滑地过渡到React Native,这有些出乎我的意料。同时,它也帮助我扩展职业技能,因为我也可以很轻松地向React编写的Web应用贡献代码了。
登录百度帐号推荐应用

我要回帖

更多关于 react native分享组件 的文章

 

随机推荐