⚠️ 这里只介绍 基于React的创建原生APP的框架(react-native)
介绍:
学习:网址:https://reactnative.cn/docs/accessibilityinfo(英文网址:https://reactnative.cn/docs/getting-started/)
搭建项目使用技术
使用技术 | 描述 |
---|---|
1、React(import React, { PropTypes } from ‘react’ | RN中类组件的定义及属性的规定需要用到React中Component,propTypes |
2、React Native(import { View} from…) | RN框架,自己封装了原生组件 |
3、rnx-ui(import Loading from ‘rnx-ui/Loading’) | RN项目中支持的组件库 |
4、rnx(import { SysUtils, NetInfo } from ‘RNX’;) | RN项目中支持的组件库 |
5、Ramda、moment等js库 | 引入js库,方便更简洁、快捷的开发 |
6、Redux 集中数据管理的使用 | 网址:https://www.redux.org.cn/index.html |
7、Router 路由 | 网址:查看官方文档 |
扩展技术 | 自己封装的 |
---|---|
rnplus(import RNPlusfrom ‘rnplus’) | RN的集成框架,主要集成了Redux和Router插件,更方便数据流管理及页面跳转 |
1、PView(import{ PView } from ‘rnplus’) | 封装的类组件,用于声明App的一整页,当进行页面跳转时,路由会找到对应的类组件渲染。 |
2、PComponent(import{ PComponent } from ‘rnplus’) | 封装的类组件,即页面里的一个组件。同PView可以设置bindEvents对象和reduxPlugin静态对象 |
3、bindEvents (在PView和PComponent设置的对象) | 代表激活失活时钩子函数 ready() this.props.param可获得参数 actived(param) 激活 deactived()失活 |
4、reduxPlugin(类中的静态对象,可使其变为容器组件) | 可以使用redux数据,使用redux数据,可以使用官方写法,也可以使用便捷写法reducer中的数据 |
5、RNPlus中路由的使用 |
// 1、打开新页面的方法RNPlus.open('目标页面的名字',{ param:{参数对象}}),触发的生命周期当前页deactived和下一页面的 ready、actived
RNPlus.open('Punch', { // 会新建历史记录
param: { // 传的参数可在页面的bindEvents的路由生命周期中通过 this.props.params获取
staff: otherStaffInfo,
type: MemType.OtherMem,
}
});
// 2、RNPlus.goto('页面名称',{param:{参数对象}}) 历史记录中有则执行回退,没有则心间历史。生命周期新建同open 回退同back
RNPlus.goto('RecognitionFace', {
param: {
staffId: staff.staffNo,
},
});
// 3、RNPlus.back() 回到上一页面的方法 触发生命周期当前页面的 deactived 和下一页面的 actived
RNPlus.back({
param:{// 需要传的参数 }
});
// 4、RNPlus.backTo('目标页面的名字',{ param:{参数对象}}) 回退到指定的页面,生命周期当前页失活,指定页的激活
RNPlus.backTo('Home');
// 5、RNPlus.home() 回到首页,可传参数
// 6、RNPlus.close(‘关闭的页面’) 关闭指定的页面,默认关闭当前页,返回上一页
一、核心组件和API(Text、Button 组件本身支持触摸事件,别的组件不支持时用TouchableOpacity等包,借用了冒泡事件原理)
可以借助组件库ant-mobile: https://mobile.ant.design/components/radio
1、基础组件
(1)View(类似原生html中div,可以嵌套自身及其他组件,用于构建用户界面的组件):支持 Flexbox 布局、样式、触摸响应、和一些无障碍功能的容器
// 项目中一般用于盒子使用,用于嵌套结构,有特定的属性及事件(用时具体查props)
hideHud = () => {
this.setState({ show: false });
}
<View style={styles.footer}
ref={ref}
style={[styles.contain, show ? { top: 0 } : { top: px(6000) }]}
onResponderStart={this.hideHud} // 用于写一些项目逻辑,控制样式呀展示什么的,手指按下时的回掉
onStartShouldSetResponderCapture={() => false} //若父视图想要阻止子视图响应触摸开始事件,设置这个方法并返回 true
>
<Text style={styles.footerTitle}>到底了</Text>
</View>
// 支持的常用属性props(包括事件)
(2)Text :用于显示文本的组件(支持嵌套、样式,以及触摸处理):内部采用的是文本布局,而不是flexbox布局
<Text
style={styles.submitText} // 样式
numberOfLines={1} // 最大行数
onPress={this.close} // 文本被点击后回调函数
onLongPress={()=>this.close} // 文本被长按后的回掉
selectable={} // 布尔值,决定用户是否可以长按选择文本,以便复制和粘贴
>
文案||{// 展示文案}
</Text>
(3)Image:展示图片内容的组件,包括网络图片、静态资源、临时的本地图片、以及本地磁盘上的图片(如相册)、base64格式图片等(类似html标签的img,设属性src alt height width)
<Image
style={styles.tinyLogo} // 正常 定义样式引入即可
source={require('@expo/snack-static/react-native-logo.png')} // 图片源
source={{uri: 'https://reactnative.dev/img/tiny_logo.png'}} // 网络图片
source={{uri: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADMAAAAzCAYAAAA6oTAqAAAAEXRFWHRTb2Z0d2FyZQBwbmdjcnVzaEB1SfMAAABQSURBVGje7dSxCQBACARB+2/ab8BEeQNhFi6WSYzYLYudDQYGBgYGBgYGBgYGBgYGBgZmcvDqYGBgmhivGQYGBgYGBgYGBgYGBgYGBgbmQw+P/eMrC5UTVAAAAABJRU5ErkJggg=='}} //base64格式的图片
resizeMode='contain' // 图片尺寸与容器尺寸不成比例时展示方式,是图片覆盖容器还是包含在容器中还是拉升正好跟容器大小一致,还是直接居中展示
/>
// 常用以上属性,别的属性和方法用时文档查
(4)TextInput:文本输入框(类似html中type=‘text’)
<TextInput
ref={}
placeholder="请补充说明" // 没有输入时的提示文本
style={{ height: 40, borderColor: 'gray', borderWidth: 1 }}
onChangeText={text => this.onOtherProblemDescChange(text)} // 输入文本的回掉(类似onChange事件)
value={this.state.otherProblemDesc} // 绑定值
multiline // 多行文本框,单行和多行文本框对于某些属性的支持不同,用时查,例如右侧的清除按钮(如果为 true,文本框中可以输入多行 文字。默认值为 false。注意安卓上如果设置multiline = {true},文本默认会垂直居中,可设置textAlignVertical: 'top'样式来使其居顶显示)
editable={true||false} // 文本框是否可编辑
maxLength={number} // 文本框最大长度的限制
numberOfLines // 多行文本框时规定行数,即占位置
// 可以指定键盘的颜色,弹出哪种类型的键盘,纯数字还是英文等
/>
// 常用属性事件:onBlur onFocus onChange(注意看回掉函数的参数) onChangeText onScroll 滚动时持续调用
// 支持的方法:clear() 通过ref或dom获取组件实例,从而调用方法
(5)ScrollView:可滚动的容器试图(ScrollView 必须有一个确定的高度才能正常工作,因为它原理就是将一系列不确定高度的子组件装进一个确定高度的容器(通过滚动操作)):简单粗暴地把所有子元素一次性全部渲染出来,如何内容特别多则不适用,可选择FlatList惰性渲染子元素
onScroll = (event) => {
EventBus.scrollOffsetY = event.nativeEvent.contentOffset.y; // 可以写一些自己要的逻辑
//nativeEvent: {
// contentInset: {bottom, left, right, top},
// contentOffset: {x, y},
// contentSize: {height, width},
// layoutMeasurement: {height, width},
// zoomScale
}
}
<ScrollView
ref={scroll => (this.scroll = scroll)}
onScrollBeginDrag={this.onScrollBeginDrag} // 当用户开始拖动此视图时调用此函数
onScrollEndDrag={this.onScrollEndDrag} // 当用户停止拖动此视图时调用此函数
onScroll={this.onScroll} //
>
{this.renderShelf()} // 写子组件,可以直接写,也可以调用封装的函数组件或类组件
</ScrollView>
// 方法:scrollTo({ y: 0,animated: false,})实例方法,滚动到指定的 x, y 偏移处
(6) TouchableOpacity:用于封装视图,使其可以正确响应触摸操作,当被按下时透明度降低(react-native中只有部分组件支持触摸事件 Text Button,所以要其他组件支持触摸事件,此时就需要用 TouchableOpacity包起来)
<TouchableOpacity
onPress={this.onNext} // 当点击里面的内容下一页时触发
activeOpacity={1} // 被触摸激活时透明度为多少 0-1
style={} // 设置样式
>
<View style={[styles.btn]}>
<Text style={styles.btnText}>下一页</Text>
</View>
</TouchableOpacity>
(7) TouchableHighlight:组件用于封装视图,使其可以正确响应触摸操作。当按下的时候,封装的视图的不透明度会降低,同时会有一个底层的颜色透过而被用户看到,使得视图变暗或变亮
// 注意TouchableHighlight只支持一个子节点(不能没有子节点也不能多于一个)。如果你希望包含多个子组件,可以用一个 View 来包装它们
function MyComponent(props) {
return (
<View {...props} style={{ flex: 1, backgroundColor: '#fff' }}>
<Text>My Component</Text>
</View>
);
}
<TouchableHighlight
activeOpacity={0.6} // 规定按下时显示的不透明度
underlayColor="#DDDDDD" // 有触摸操作时显示出来的底层的颜色
onPress={() => alert('Pressed!')}
style={]
>
<MyComponent />
</TouchableHighlight>;
(8) TouchableWithoutFeedback:封装视图,用于响应触摸事件,但没有任何视觉上的效果(底层和透明度都没有变化)=>不建议使用
(9) StyleSheet:用于创建样式表的组件,使样式代码可抽离渲染块
import { StyleSheet, Text, View } from "react-native";
// 可直接在组件中定义也可通过创建js文件的形式引入组件,使用时样式属性采用驼峰命名
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 24,
backgroundColor: "#eaeaea"
},
title: {
marginTop: 16,
paddingVertical: 8,
borderWidth: 4,
borderColor: "#20232a",
borderRadius: 6,
backgroundColor: "#61dafb",
color: "#20232a",
textAlign: "center",
fontSize: 30,
fontWeight: "bold"
}
});
// 使用时直接styles. container
// import styles from './styles'; 创建样式文件引入的形式
// styles文件
import { StyleSheet } from 'react-native';
export default StyleSheet.create({
footer: {
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
},
footerTitle: {
color: 'gray',
},
});
2、交互控件
(1)Button 按钮组件,不过该组件的样式是固定的,可以使用TouchableOpacity或是TouchableNativeFeedback组件来定制自己所需要的按钮,或者你也可以在 github.com 网站上搜索 ‘react native button’ 来看看社区其他人的作品
<Button
onPress={onPressLearnMore} // 按钮点击时的回调
title="Learn More" // 按钮展示文字
color="#841584" // 背景颜色.文本的颜色(iOS),或是按钮的背景色(Android)
disabled // 是否禁用按钮
accessibilityLabel="Learn more about this purple button" // 用于给残障人士显示的文本
/>
(2)Picker:选择器(在 iOS 和 Android 上渲染原生的选择器(Picker),类似Select)0.64版过时,next版还没发布
<Picker
selectValue={currentSpace.id} // 默认值
options={options} // 选项数组
isLoading={false} //
change={this.onSpaceChange} // 选项改变时的回调
/>
(3)Slider 滑块 已过时 (类似ant的Slider)
(4)Switch:开关控件(受控组件,需通过onValueChange回调来更新value属性以响应用户的操作)
<View style={styles.container}>
<Switch
trackColor={{ false: "#767577", true: "#81b0ff" }} // 开启状态时的背景颜色(开启、关闭都可以设置)
thumbColor={isEnabled ? "#f5dd4b" : "#f4f3f4"} // 开关上圆形按钮的背景颜色
ios_backgroundColor="#3e3e3e"
onValueChange={this.onValueChange} // 更改value的绑定值为当前值,参数为新的值
value={isEnabled} // 绑定值
disabled // 是否禁用开关组件
/>
</View>
3、列表视图(只会渲染当前屏幕可见的元素,这样有利于显示大量的数据,实现了懒加载)
(1)FlatList:高性能的滚动列表组件
(2)SectionList:也是列表展示组件,类似FlatList,但是多了分组(分组列表展示)
4、iOS 独有组件(用时查文档属性)
5、Android 独有组件(用时查文档属性)
6、其他(类似ant中警告提示 通知框等Alert,反馈时所使用的组件):一般用于一些特定场景
(1)ActivityIndicator:显示一个圆形的 loading 提示符号
<ActivityIndicator
size="large" // enum('small', 'large')指示器大小,目前只能安卓上设置
color="#00ff00" // 滚轮的前景颜色
/>
(2)Alert:启动一个提示对话框,包含对应的标题和信息,类似ant中的Alert提示对话框
import { Alert } from "react-native";
Alert.alert(
"标题", // 提示标题
`确定删除该${customerName}?`, // 提示内容
[ // 操作按钮
{ text: '取消', onPress: () => console.log('点击取消') },
{
text: '确定', onPress: () => console.log('点击确定')
},
{text: '稍后', onPress: ()=> console.log('点击稍后')},
]
);
(3)Animated:易于使用和维护的动画库,可生成流畅而强大的动画(类似css3 动画 animation)
(4)Modal:弹框层
<Modal
animationType="none" // 弹框出现的动画类型,enum('none', 'slide', 'fade')
transparent // 弹框的背景色是否透明,true时透明背景色,默认是白色
fullScreen={false} // 是否全屏
visible={visible} // modal是否展示
onRequestClose={() => { }}
onShow ={() => { }} // modal显示时触发的回调
>
</Modal>
(5)Dimensions:用于获取设备屏幕的宽、高
import { Dimensions } from 'react-native';
const windowWidth = Dimensions.get('window').width; // 可见应用程序窗口的大小
const windowHeight = Dimensions.get('window').height;
// 注意因为当屏幕方向改变时对应的宽高变化,所以不应该当时常量存储,应该写在函数中,每次render前调用
// 常用方法:.get() addEventListener("change", this.onChange);
(6) RefreshControl:下拉刷新的功能,通常和列表FlatList、ScrollView结合使用
==是一个受控组件,refreshing={this.state.refreshing} 的值需通过 onRefresh={this.onRefresh}动态更新
onRefresh = async () => {
try {
this.setState({ refreshing: true });
await this.props.refreshNewData();
this.setState({ refreshing: false });
} catch (error) {
logAsyncException({ target: 'InventoryPage_New_onRefresh', error });
}
}
<ScrollView
contentContainerStyle={styles.scrollView}
refreshControl={
<RefreshControl
refreshing={this.state.refreshing} // 是否应该在刷新时显示指示器
onRefresh={onRefresh} /> // 视图开始刷新时调用
// 还可以指定title 颜色 大小等
}
>
<Text>Pull down to see RefreshControl indicator</Text>
</ScrollView>
(7)PixelRatio:可以获取设备的像素密度及字体缩放比
如果应用运行在一个高像素密度的设备上,显示的图片也应当分辨率更高。一个取得缩略图的好规则就是将显示尺寸乘以像素密度比:
import { PixelRatio} from "react-native";
// 方法
PixelRatio.get() // 返回设备的像素密度
PixelRatio.getFontScale() // 返回字体的缩放比例
getPixelSizeForLayoutSize() // 将一个布局尺寸(dp)转换为像素尺寸(px)
// 案例:在高像素密度上显示图片,可以显示尺寸(页面的宽高)乘以像素密度比
const image = getImage({
width: PixelRatio.getPixelSizeForLayoutSize(200),
height: PixelRatio.getPixelSizeForLayoutSize(100),
});
<Image source={image} style={{width: 200, height: 100}} />
(8)KeyboardAvoidingView:解决键盘挡住页面的问题,本组件可以自动根据键盘的高度,调整自身的 height 或底部的 padding,以避免被遮挡。用时查文档
(9)Linking:仅适用非沙盒项目,提供了一个通用的接口来与传入和传出的 App 链接进行交互
(10)StatusBar:控制应用状态栏的组件
(11)WebView:创建一个原生的 WebView,可以用于访问一个网页(类似a标签 )
<WebView
source={{uri: 'https://github.com/facebook/react-native'}}
style={{marginTop: 20}}
/>
// 直接写html页面
import React, { Component } from 'react';
import { WebView } from 'react-native';
class MyInlineWeb extends Component {
render() {
return (
<WebView
originWhitelist={['*']}
source={{ html: 'Hello world
' }}
/>
);
}
}
二、事件
(1)点击事件 onPress
(2)基本的触摸事件 看组件支持的属性
(3)手势系统
三、样式(通常采用的是flex弹性盒子布局)
(1)行内样式(可以是对象的形式,此时属性采用驼峰命名。可以直接时字符串的形式,直接是属性名)
默认单位dp(1dp=1px) 样式没有继承的,每个都需要单独设置
<View style={{ backgroundColor: "blue", flex: 0.3 }} />
<View style='margin-bottom:6px;color:red' />
(2)创建样式表(StyleSheet)
import { StyleSheet } from 'react-native';
const styles=StyleSheet.create({ // 方便抽离代码统一管理。写法类似定义变量写样式,与直接在css中写法不同
footer: {
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
height: px(60),
marginBottom: px(100),
},
footerTitle: {
marginLeft: px(20),
fontSize: px(24),
color: 'gray',
},
});
// render中
<View style={styles.footer}>
<Text style={styles.footerTitle}>到底了</Text>
</View>
当有多个样式时可以使用数组 style={[styles.footerTitle,styles. footer]}
// 也可以加判断表示是否加载该样式 style={[style.base,this.state.active&&style.active]} //当this.state.active为真则加style.active
// 你也可以在组件中render样式,然而这种做法不推荐,其实就像一般html页面中行内样式不推荐一样
style={[styles.base,{width:this.state.width, height:this.state.width*this.state.aspectRatio}]}
(3)react中样式可以使用的方式react-native中都可以
四、项目中常用的功能组件的使用方式及封装方法(下章节讲)
(1)弹框
(2)按钮
(3)提示,后端错误提示,前端给的用户提示
与react的区别:不支持原生html标签(会报错找不到该标签),使用自己封装的核心组件来开发(替代html标签及属性)
同react开发,只不过将html 标签替换成react-native的原生mobileApp,同时只支持人家组件暴露的属性设置
补充集中数据管理Redux的学习 https://www.redux.org.cn/index.html
补充RN中可使用的组件库 rnx-ui 的学习(公司可集成的在原生组件上的封装)
例如:警告弹框、页面容器、按钮、选择框、
补充前后端交互(使用的是rnx-fetch方法进行交互)
import fetch from 'rnx-fetch';
// Request中直接请求方法的封装
function post(opts) {
const fetchOpt = {
method: 'POST',
body: JSON.stringify(opts.data),
};
return cycleFetch({
...opts,
fetchOpt,
originUri: opts.uri,
uniqueUri: `${opts.uri}${fetchOpt.body}`,
});
}
function postForm(opts) {
const fetchOpt = {
method: 'POST',
body: serialize(opts.data),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',// 表单默认的格式序列化,一般是文本
},
};
return cycleFetch({
...opts,
fetchOpt,
originUri: opts.uri,
uniqueUri: `${opts.uri}${fetchOpt.body}`,
});
}
function postFormFile(opts) {
const fetchOpt = {
method: 'POST',
body: opts.body,
data: serialize(opts.data),
headers: {
'Accept': 'application/json',
'Content-Type': 'multipart/form-data', // 允许提交表单包含: files,non-ASCII-text,Binary 类型数据
}
};
return newFetch({
...opts,
fetchOpt,
originUri: opts.uri,
uniqueUri: `${opts.uri}${fetchOpt.data}`,
})
}
function get(opts) {
const fetchOpt = {
method: 'GET',
};
let postfix = '';
if (opts.data) {
postfix = `?${serialize(opts.data)}`;
}
const uri = `${opts.uri}${postfix}`;
return cycleFetch({
...opts,
uri,
fetchOpt,
originUri: opts.uri,
uniqueUri: uri,
});
}
// 封装的各个请求方法
// 1、punchGet
const punchGet = (uri, param, opt = {}) => Request.get({
uri: punchApiFactory(uri), // 接口的url,会在前拼接punch 后拼接版本号,默认 v1
data: {
...param, // 调用接口时传的参数
deviceId: DEVICEID, // 设备id(从RNX中固定拿到的,接口超时时间)
udid: UDID.mac, // 获取sn (从RNX中固定拿到的,例如udid=90:17:c8:d0:a6:1f)
shopCode: getShopCode() // 从RNX中获取外壳中的当前的门店
},
...opt // 别的参数
});
// 2、punchGetNoVersion
const punchGetNoVersion = (uri, param, opt = {}) => Request.get({
uri: punchApiFactoryNoVersion(uri), //接口的url,会在前拼接punch,后不会拼接版本v1
data: {
...param,
deviceId: DEVICEID,
udid: UDID.mac,
shopCode: getShopCode()
},
...opt
});
// 3、punchGetNoVersionNoParam
const punchGetNoVersionNoParam = (uri, param, opt = {}) => Request.get({
uri: punchApiFactoryNoVersion(uri),
data: {
...param
},
...opt
});
// 4、punchPost 的post方法,会拼接punch及版本号也添加额外参数,body是json形式
const punchPost = (uri, param, opt = {}) => Request.post({
uri: punchApiFactory(uri), // 接口的url,会在前拼接punch 后拼接版本号,默认 v1
data: {
...param,
udid: UDID.mac
},
...opt
});
// 5、punchPostNoVersion(post方法,接口后不会拼接版本)
const punchPostNoVersion = (uri, param, opt = {}) => Request.post({
uri: punchApiFactoryNoVersion(uri),
data: {
...param,
udid: UDID.mac
},
...opt
});
// 6、punchPostForm
const punchPostForm = (uri, param, opt = {}) => Request.postForm({
uri: punchApiFactory(uri), // 接口的url,会在前拼接punch 后拼接版本号,默认 v1
data: { // 此方法会设置body的参数是序列化的格式,且头部的'Content-Type': 'application/x-www-form-urlencoded',
...param,
udid: UDID.mac
},
...opt
});
// 7、punchPostFormFile(post表单的传参数形式。即序列化)
const punchPostFormFile = (uri, param, opt = {}) => Request.postFormFile({
uri: punchApiFactory(uri),
body: param,
data: {
udid: UDID.mac
},
...opt
})
// 8、attendanceGet(get方法)
const attendanceGet = (uri, param, opt = {}) => Request.get({
uri: attendanceApiFactory(uri), // 前拼接attendance 后拼接版本
data: {
...param,
udid: UDID.mac
},
...opt
});
// 1、punchGet 方法的使用
export const checkRosterType = param => punchGet('shop/queryShopType', param, {
useLoading: true
});
在文件中直接 import { checkRosterType } from 'BizApi';
checkRosterType({// 参数就是传的参数对象params
deviceid: DEVICEID,
shopCode
}).then(res => { // 接口成功后的处理逻辑
if (res && res.status === 0) {
this.setState({
rosterType: res.data
});
}
});
// 2、punchGetNoVersion
export const publicityConfigGetForMobile = (param) => punchGetNoVersion('senseOfWorth/getRandomSenseOfWorth', param, {
useLoading: true
})
//3、punchGetNoVersionNoParam(get方法,没有额外自动添加的门店、超时时间等额外参数)
export const queryUnfinishedList = (param) => punchGetNoVersionNoParam('randomPunch/queryUnfinishedList', param, {
useLoading: false
})
// 4、punchPost的使用
export const examSubmitAll = (param, opt) => punchPost('exam/submitAll', param, opt); // #endregion
// 5、punchPostNoVersion 的使用
export const confirmSOPApi = (param) => punchPostNoVersion('sopExecutionFeedback/save', param, {
useLoading: true
})
// 6、punchPostForm (post序列化body传参数格式)
export const restStart = param => punchPostForm('rest/start', param);
// 7、punchPostFormFile 可上传文件(设置的Content-Type不同,目前项目中没用)
// 8、attendanceGet方法
// ⚠️ :自己使用时可以直接使用Request暴露的方法,传的参数直接写死,不用使用原来的方法拼接获取uri
pad项目接口请求的逻辑:还是拿到最终完整的请求url去调用请求方法(至于开发、测试、线上环境的的域名可自己维护,也可让壳维护,将需要的协议域名返回拿到)
蜂利器项目请求走的逻辑:最后处理请求的接口是完整的:https://域名/roster/api/dimission/staff/app/flow/reject
RN样式指南(样式对照表):https://github.com/doyoe/react-native-stylesheet-guide#user-content-text