继续react native之旅,其实官方给出的栗子,也是很值得学习的,我们可以在react native github上下载demo,下面说说,我在运行UIexplorer时候遇到的错误。
运行UIexplorer
新创建一个uiexplorer目录
执行如下命令:
git clone https://github.com/facebook/react-native.git
cd react-native && npm install
这里可能需要花费一段时间,看网络的好坏吧,一般4-5分钟。
配置sdk和ndk环境
- 对于sdk有如下要求:
1.Android SDK version 23
2.SDK build tools version 23.0.1
3.Android Support Repository >= 17
2.下载并配置NDK
sdk.dir=F:/android/sdk
ndk.dir=F:/android/android-ndk-r10e
这里sdk.dir和ndk.dir的值分别是其对应的全路径。
编译framework代码
cd react-native
./gradlew :ReactAndroid:assembleDebug
./gradlew :ReactAndroid:installArchives
运行demo
进入react-native目录,执行如下命令:
//编译UIExplorer
./gradlew :Examples:UIExplorer:android:app:installDebug
./packager/packager.sh
需要注意的是./packager/packager.sh命令会将UIExplorer安装到手机上,这个需要一段时间。注意,此时可能会遇到如下错误:
我们按照提示打开Watchman took too long to load ,按照提示,windows下执行如下命令,即可:
npm install --HEAD watchman
运行效果如下:
另外,官方还提供了一个Movie的demo,可以用来学习,运行该demo同样的方法,进入react-native目录:
./gradlew :Examples:Movies:android:app:installDebug
./packager/packager.sh
效果如下:
继续学习react中的组件,下面先学习TextInput组件。
TextInput
TextInput是一个允许用户在应用中通过键盘输入文本的基本组件,类似于android中的EditText组件。
TextInput 属性
先来看下TextInput 的属性:
autoCapitalize enum('none','sentences','words','characters')
控制TextInput是否要自动将特定字符切换为大写:
characters: 所有的字符。
words: 每个单词的第一个字符。
sentences: 每句话的第一个字符(默认)。
none: 不自动切换任何字符为大写。
autoCorrect bool
如果为false,会关闭拼写自动修正。默认值是true。
autoFocus bool
如果为true,在componentDidMount后会获得焦点。默认值为false。
defaultValue string
提供一个文本框中的初始值。当用户开始输入的时候,值就可以改变。
在一些简单的使用情形下,如果你不想用监听消息然后更新value属性的方法来保持属性和状态同步的时候,就可以用defaultValue来代替。
editable bool
如果为false,文本框是不可编辑的。默认值为true。
keyboardType enum("default",'numeric','email-address',"ascii-capable",'numbers-and-punctuation','url','number-pad','phone-pad','name-phone-pad','decimal-pad','twitter','web-search')
决定弹出的何种软键盘的,譬如numeric(纯数字键盘)。
这些值在所有平台都可用:
default
numeric
email-address
maxLength number
限制文本框中最多的字符数。使用这个属性而不用JS逻辑去实现,可以避免闪烁的现象。
multiline bool
如果为true,文本框中可以输入多行文字。默认值为false。
onBlur function
当文本框失去焦点的时候调用此回调函数。
onChange function
当文本框内容变化时调用此回调函数。
onChangeText function
当文本框内容变化时调用此回调函数。改变后的文字内容会作为参数传递。
onEndEditing function
当文本输入结束后调用此回调函数。
onFocus function
当文本框获得焦点的时候调用此回调函数。
onLayout function
当组件挂载或者布局变化的时候调用,参数为{x,y,width,height}。
onSubmitEditing function
此回调函数当软键盘的确定/提交按钮被按下的时候调用此函数。如果multiline={true},此属性不可用。
placeholder string
如果没有任何文字输入,会显示此字符串。
placeholderTextColor string
占位字符串显示的文字颜色。
secureTextEntry bool
如果为true,文本框会遮住之前输入的文字,这样类似密码之类的敏感文字可以更加安全。默认值为false。
style Text#style
Styles
testID string
用来在端到端测试中定位这个视图。
value string
文本框中的文字内容。
TextInput是一个受约束的(Controlled)的组件,意味着如果提供了value属性,原生值会被强制与value属性保持一致。在大部分情况下这都工作的很好,不过有些情况下会导致一些闪烁现象——一个常见的原因就是通过不改变value来阻止用户进行编辑。如果你希望阻止用户输入,可以考虑设置editable={false};如果你是希望限制输入的长度,可以考虑设置maxLength属性,这两个属性都不会导致闪烁。
numberOfLines number
设置输入框的行数。当multiline设置为true时使用它,可以占据对应的行数。
textAlign enum('start','center','end')
设置开始输入的时候光标会处在什么位置。
androidtextAlignVertical enum('top','bottom')
控制文本框中的文字垂直方向上的对齐方式。
androidunderlineColorAndroid string
文本框的下划线颜色(译注:如果要去掉文本框的边框,请将此属性设为透明transparent)。
一个简单的demo
下面看一个关于TextInput的一个简单的demo,先看下效果吧:
代码也比较简单,关键代码如下:
var TextInput = require('TextInput');
var secondProject = React.createClass({
getInitialState: function() {
return {
text: "your name",};
},render: function() {
return (
<View>
<Text style={{color: "red",fontSize: 20}}>姓名:</Text>
<TextInput
style={{height: 40,borderColor: 'gray',borderWidth: 1}}
onChangeText={(text) => this.setState({text})}
value={this.state.text}
/>
</View>
);
},});
这里设定text属性的初始内容,并且监听onChangeText方法,在该方法的回调中,根据当前的text设置新的text值。
ScrollView初学
ScrollView类似于android中的ScrollView控件,有如下属性:
contentContainerStyle StyleSheetPropType(ViewStylePropTypes)
这些样式会应用到一个内层的内容容器上,所有的子视图都会包裹在内容容器内
horizontal bool
当此属性为true的时候,所有的的子视图会在水平方向上排成一行,而不是默认的在垂直方向上排成一列。默认值为false。
keyboardDismissMode enum('none',"interactive",'on-drag')
用户拖拽滚动视图的时候,是否要隐藏软键盘。
none(默认值),拖拽时不隐藏软键盘。
on-drag 当拖拽开始的时候隐藏软键盘。
interactive 软键盘伴随拖拽操作同步地消失,并且如果往上滑动会恢复键盘。安卓设备上不支持这个选项,会表现的和none一样。
keyboardShouldPersistTaps bool
当此属性为false的时候,在软键盘激活之后,点击焦点文本输入框以外的地方,键盘就会隐藏。如果为true,滚动视图不会响应点击操作,并且键盘不会自动消失。默认值为false。
onScroll function
在滚动的过程中,每帧最多调用一次此回调函数。调用的频率可以用scrollEventThrottle属性来控制。
removeClippedSubviews bool
(实验特性):当此属性为true时,屏幕之外的子视图(子视图的overflow样式需要设为hidden)会被移除。这个可以提升大列表的滚动性能。默认值为true。
showsHorizontalScrollIndicator bool
当此属性为true的时候,显示一个垂直方向的滚动条。
showsVerticalScrollIndicator bool
当此属性为true的时候,显示一个水平方向的滚动条。
style style
FlexBox...
Transforms...
backfaceVisibility enum('visible','hidden')
backgroundColor string
borderColor string
borderTopColor string
borderRightColor string
borderBottomColor string
borderLeftColor string
borderRadius number
borderTopLeftRadius number
borderTopRightRadius number
borderBottomLeftRadius number
borderBottomRightRadius number
borderStyle enum('solid','dotted','dashed')
borderWidth number
borderTopWidth number
borderRightWidth number
borderBottomWidth number
borderLeftWidth number
opacity number
overflow enum('visible','hidden')
shadowColor string
shadowOffset {width: number,height: number}
shadowOpacity number
shadowRadius number
====================================================
下面这些事IOS上才有的属性
iosalwaysBounceHorizontal bool
当此属性为true时,水平方向即使内容比滚动视图本身还要小,也可以弹性地拉动一截。当horizontal={true}时默认值为true,否则为false。
iosalwaysBounceVertical bool
当此属性为true时,垂直方向即使内容比滚动视图本身还要小,也可以弹性地拉动一截。当horizontal={true}时默认值为false,否则为true。
iosautomaticallyAdjustContentInsets bool
如果滚动视图放在一个导航条或者工具条后面的时候,iOS系统是否要自动调整内容的范围。默认值为true。(译注:如果你的ScrollView或ListView的头部出现莫名其妙的空白,尝试将此属性置为false)
iosbounces bool
当值为true时,如果内容范围比滚动视图本身大,在到达内容末尾的时候,可以弹性地拉动一截。如果为false,尾部的所有弹性都会被禁用,即使alwaysBounce*属性为true。默认值为true。
iosbouncesZoom bool
当值为true时,使用手势缩放内容可以超过min/max的限制,然后在手指抬起之后弹回min/max的缩放比例。否则的话,缩放不能超过限制。
ioscanCancelContentTouches bool
当值为false时,一旦有子节点响应触摸操作,即使手指开始移动也不会拖动滚动视图。默认值为true(在以上情况下可以拖动滚动视图。)
ioscenterContent bool
当值为true时,如果滚动视图的内容比视图本身小,则会自动把内容居中放置。当内容比滚动视图大的时候,此属性没有作用。默认值为false。
ioscontentInset {top: number,left: number,bottom: number,right: number}
内容范围相对滚动视图边缘的坐标。默认为{0,0,0}。
ioscontentOffset PointPropType
用来手动设置初始的滚动坐标。默认值为{x: 0,y: 0}。
iosdecelerationRate number
一个浮点数,用于决定当用户抬起手指之后,滚动视图减速停下的速度。常见的选项有:
Normal: 0.998 (默认值)
Fast: 0.9
iosdirectionalLockEnabled bool
当值为真时,滚动视图在拖拽的时候会锁定只有垂直或水平方向可以滚动。默认值为false。
iosmaximumZoomScale number
允许的最大缩放比例。默认值为1.0。
iosminimumZoomScale number
允许的最小缩放比例。默认值为1.0。
iosonRefreshStart function
如设置了此属性,则会显示一个UIRefreshControl。参数为一个函数,用来在合适的时候执行,以停止UIRefreshControl的动画
(endRefreshing) => {
endRefreshing();
}
iosonScrollAnimationEnd function
当滚动动画结束之后调用此回调。
iospagingEnabled bool
当值为true时,滚动条会停在滚动视图的尺寸的整数倍位置。这个可以用在水平分页上。默认值为false。
iosscrollEnabled bool
当值为false的时候,内容不能滚动,默认值为true。
iosscrollEventThrottle number
这个属性控制在滚动过程中,scroll事件被调用的频率(单位是每秒事件数量)。更大的数值能够更及时的跟踪滚动位置,不过可能会带来性能问题,因为更多的信息会通过bridge传递。默认值为0,意味着每次视图被滚动,scroll事件只会被调用一次。
iosscrollIndicatorInsets {top: number,right: number}
决定滚动条距离视图边缘的坐标。这个值应该和contentInset一样。默认值为{0,0}。
iosscrollsToTop bool
当此值为true时,点击状态栏的时候视图会滚动到顶部。默认值为true。
iossnapToAlignment enum('start',"center",'end')
当设置了snapToInterval,snapToAlignment会定义停驻点与滚动视图之间的关系。
start (默认) 会将停驻点对齐在左侧(水平)或顶部(垂直)
center 会将停驻点对齐到中间
end 会将停驻点对齐到右侧(水平)或底部(垂直)
iossnapToInterval number
当设置了此属性时,会让滚动视图滚动停止后,停止在snapToInterval的倍数的位置。这可以在一些子视图比滚动视图本身小的时候用于实现分页显示。与snapToAlignment组合使用。
iosstickyHeaderIndices [number]
一个子视图下标的数组,用于决定哪些成员会在滚动之后固定在屏幕顶端。举个例子,传递stickyHeaderIndices={[0]}会让第一个成员固定在滚动视图顶端。这个属性不能和horizontal={true}一起使用。
ioszoomScale number
滚动视图内容初始的缩放比例。默认值为1.0。
makeItems: function(nItems: number,styles): Array<any> {
var items = [];
for (var i = 0; i < nItems; i++) { items[i] = ( <TouchableOpacity key={i} style={styles}>
<Text>{'Item ' + i}</Text>
</TouchableOpacity>
);
}
return items;
},
这里创建一个方法,接收两个参数,创建item,并返回item数组。
//render方法渲染需要显示的视图,这里返回了verticalScrollView
render: function() {
//创建需要显示的items
var items = this.makeItems(NUM_ITEMS,styles.itemWrapper);
var verticalScrollView = (
<ScrollView style={styles.verticalScrollView}>
{items}
</ScrollView>
);
return verticalScrollView;
}
实现水平的ScrollView
实现水平的ScrollView也很简单,需要将竖直上显示数组的的某一个item上重新赋值为一个ScrollView,并且标注其horizontal={true}即可。代码如下:
render: function() {
//创建需要显示的items
var items = this.makeItems(NUM_ITEMS,styles.itemWrapper);
items[4] = (
<ScrollView key={'scrollView'} horizontal={true}>
{this.makeItems(NUM_ITEMS,[styles.itemWrapper,styles.horizontalItemWrapper])}
</ScrollView>
);
var verticalScrollView = (
<ScrollView style={styles.verticalScrollView}>
{items}
</ScrollView>
);
return verticalScrollView;
}
'use strict';
var React = require('react-native');
var {
AppRegistry,ScrollView,StyleSheet,Text,TouchableOpacity
} = React;
var NUM_ITEMS = 20;
var secondProject = React.createClass({
makeItems: function(nItems: number,styles): Array<any> {
var items = [];
for (var i = 0; i < nItems; i++) {
items[i] = (
<TouchableOpacity key={i} style={styles}>
<Text>{'Item ' + i}</Text>
</TouchableOpacity>
);
}
return items;
},render: function() {
//创建需要显示的items
var items = this.makeItems(NUM_ITEMS,styles.itemWrapper);
items[4] = (
<ScrollView key={'scrollView'} horizontal={true}>
{this.makeItems(NUM_ITEMS,styles.horizontalItemWrapper])}
</ScrollView>
);
var verticalScrollView = (
<ScrollView style={styles.verticalScrollView}>
{items}
</ScrollView>
);
return verticalScrollView;
}
});
var styles = StyleSheet.create({
verticalScrollView: {
margin: 10,},itemWrapper: {
backgroundColor: '#dddddd',alignItems: 'center',borderRadius: 5,borderWidth: 5,borderColor: '#008764',padding: 30,margin: 5,horizontalItemWrapper: {
padding: 50
}
});
AppRegistry.registerComponent('secondProject',() => secondProject);
第二个ScrollView
构建数据
由于RefreshControl组件用在ScrollView内部,所以这里,我先构建一个ScrollView,并且为其指定一定的初始值。
设置每一行的模板
var Row = React.createClass({
render: function() {
return (
<Text>
{this.props.data.text}
</Text>
);
},});
上面的代码中,定义了每一行显示的模板,类似于android中为adapter设置的显示模板,这里Text组件中,显示的内容是当前模板的data属性的值。
显示ScrollView数据
var secondProject = React.createClass({
getInitialState() {
return {
//初始化数据
rowData: Array.from(new Array(20)).map(
(val,i) => ({text: 'Initial row' + i})),render: function() {
// 遍历当前rowData的数据,并将每一条数据设置到模板中,返回给新的数组
var rows = this.state.rowData.map((row) => {
return <Row data={row}/>;
});
return (
<ScrollView>
{rows}
</ScrollView>
);
}
});
上面动态创建了一个大小为20的数组,并且遍历,将最终的数组赋值给rowData。
此时,运行该应用程序,效果如下:
可是发现,react给了一个警告,点击下面的黄色部分:
告诉我们需要给数组中的每个元素添加一个key。更改封装数据的方法如下:
var rows = this.state.rowData.map((row,ii) => { console.log("iiii is :"+ii+" the row is :"+row); return <Row key={ii} data={row}/>; });
这样就保证了,数组中每一个元素的key不同了,下面是通过google浏览器的打印结果:
设置样式
可以看到,虽然数据是有了,可是么有应用任何样式,,这也就导致了,界面看起来实在是太挫了,下面为其设置样式:
设置text显示样式
var styles = StyleSheet.create({
text: {
alignSelf: 'center',//居中
color: '#f00',//字体颜色
},});
这里创建了一个text的样式,并且将其应用到Text组件中:
<Text style={styles.text}>
{this.props.data.text}
</Text>
此时效果如下:
设置每一行显示的样式
row: {
borderColor: 'grey',//边框颜色
borderWidth: 1,//边框宽度
padding: 20,//内边距
backgroundColor: '#3a5795',//背景色
margin: 5,//外边距
},
此时效果如下:
添加点击效果
下面给每一行添加一个点击反馈的效果,这里我使用的是TouchableOpacity组件,即点击的时候,半透明
- 引入TouchableOpacity组件
var { AppRegistry,TouchableOpacity,RefreshControl,View,} = React;
- 应用TouchableOpacity
<TouchableOpacity>
<View style={styles.row}>
<Text style={styles.text}>
{this.props.data.text}
</Text>
</View>
</TouchableOpacity>
'use strict';
var React = require('react-native');
var {
AppRegistry,} = React;
var Row = React.createClass({
render: function() {
return (
<TouchableOpacity>
<View style={styles.row}>
<Text style={styles.text}>
{this.props.data.text}
</Text>
</View>
</TouchableOpacity>
);
},});
var secondProject = React.createClass({
getInitialState() {
return {
isRefreshing: false,loaded: 0,rowData: Array.from(new Array(20)).map(
(val,i) => ({text: 'Initial row' + i})),render: function() {
const rows = this.state.rowData.map((row,ii) => { console.log("iiii is :"+ii+" the row is :"+row); return <Row key={ii} data={row}/>; }); return ( <ScrollView style={styles.scrollview} > {rows} </ScrollView> ); },}); var styles = StyleSheet.create({ text: { alignSelf: 'center',color: '#f00',row: { borderColor: 'grey',//边框颜色 borderWidth: 1,//边框宽度 padding: 20,//内边距 backgroundColor: '#3a5795',//背景色 margin: 5,//外边距 },scrollview: { flex: 1,}); AppRegistry.registerComponent('secondProject',() => secondProject);
ToolbarAndroid学习
ToolbarAndroid类似于android平台的Toolbar,比较简单,有如下属性:
actions [{title: string,icon: optionalImageSource,show: enum('always','ifRoom','never'),showWithText: bool}]
设置功能菜单中的可用功能。他们会显示为部件右侧的图标或文字。如果放不下,则会被放进一个弹出菜单里。
这个属性接受一个对象数组,每个对象可以有如下的字段:
title: 必须的,功能的标题
icon: 这个功能的图标,例如require('./some_icon')
show: 是直接作为icon显示还是先隐藏,而在弹出菜单里显示:always总是显示,ifRoom如果放的下则显示,或者never从不显示。
showWithText: 值为布尔类型,指定是否在图标旁边同时还显示文字
contentInsetEnd number
设置Toolbar的右边缘和屏幕右边缘的距离。
除了导航按钮和菜单以外,设置这一属性也会影响Toolbar的内容区域。它定义了Toolbar与屏幕边沿的最小边距,可以用来使Toolbar的内容和一些设计上的网格线对齐。
contentInsetStart number
设置Toolbar的左边缘和屏幕左边缘的距离。
作用同上。
logo optionalImageSource
设置整个工具条的徽标。
navIcon optionalImageSource
设置导航器的icon。
onActionSelected function
当一个功能被选中的时候调用此回调。传递给此回调的唯一参数是该功能在actions数组中的位置。
onIconClicked function
当图标被选中的时候调用此回调。
overflowIcon optionalImageSource
设置功能列表的弹出菜单的图标。
rtl bool
设置toolbar的排列顺序为从右到左。除了将这一属性设为true以外,你还需要在AndroidManifest.xml中添加:
android:supportsRtl="true"
以及在Main.Activity的onCreate方法中调用 setLayoutDirection(LayoutDirection.RTL)
subtitle string
设置工具条的子标题
subtitleColor string
设置工具条子标题的颜色。
testID string
用来在端到端测试中定位这个视图。
title string
设置工具栏的标题。
titleColor string
设置工具栏的标题颜色。
ToolbarAndroid简单栗子
先看下效果吧:
拷贝图片资源到res/drawable目录下
如下:
创建toolbar需要显示的内容数组
var toolbarActions = [
{title: 'Create',icon: require('image!ic_create_black_48dp'),show: 'always'},{title: 'Filter',show:'always'},{title: 'Settings',icon: require('image!ic_settings_black_48dp'),];
render方法的实现
// 为actionText state定义一个初始显示的字符串
getInitialState: function() {
return {
actionText: 'toolbar example',//定义一个方法,当onActionSelected方法调用的时候,回调该方法
_onActionSelected: function(position) {
this.setState({
//改变actionText的值
actionText: 'Selected ' + toolbarActions[position].title,});
},render: function() {
return(
<ToolbarAndroid
actions={toolbarActions}
navIcon={require('image!ic_menu_black_24dp')}
onActionSelected={this._onActionSelected}
onIconClicked={() => this.setState({actionText: 'Icon clicked'})}
style={styles.toolbar}
subtitle={this.state.actionText}
title="Toolbar"
titleColor="red"
subtitleColor="green"
/>
</ScrollView>
);
}
完整代码如下:
'use strict';
var React = require('react-native');
var ToolbarAndroid = require('ToolbarAndroid');
var {
AppRegistry,} = React;
var secondProject = React.createClass({
getInitialState: function() {
return {
actionText: 'toolbar example',toolbarSwitch: false,_onActionSelected: function(position) {
this.setState({
actionText: 'Selected ' + toolbarActions[position].title,render: function() {
return(
<ToolbarAndroid
logo={require('image!launcher_icon')}
actions={toolbarActions}
navIcon={require('image!ic_menu_black_24dp')}
onActionSelected={this._onActionSelected}
onIconClicked={() => this.setState({actionText: 'Icon clicked'})}
style={styles.toolbar}
subtitle={this.state.actionText}
title="Toolbar" />
);
}
});
var toolbarActions = [
{title: 'Create',icon: require('image!ic_create_black_48dp'),show: 'always'},{title: 'Filter',show:'always'},{title: 'Settings',icon: require('image!ic_settings_black_48dp'),];
var styles = StyleSheet.create({
toolbar: {
backgroundColor: '#e9eaed',height: 56,});
AppRegistry.registerComponent('secondProject',() => secondProject);
ok,今天就到这里了,未完待续。