当前位置:

RN项目实践-实现诗词墨客App:项目基础构建

访客 2024-04-23 1479 0

我们的目标是使用react-native实现诗词墨客App,诗词墨客App的灵感来自于GitHub热门项目chinese-poetry和它的衍生项目weapp-poem,chinese-poetry项目是最全中华古诗词数据库,唐宋两朝近一万四千古诗人,接近5.5万首唐诗加26万宋诗.两宋时期1564位词人,21050首词。我们的App将使用这些数据实现古诗词的推荐、列表展示、详情展示等功能,现在我们进行第一阶段的工作,项目的基本构建,这一个阶段我们主要会涉及到以下几个方面:

  1. react-native调试
  2. 使用react-navigation实现路由管理
  3. 使用react-native-vector-icons实现图标

实现的功能主要有:

  1. 开机显示欢迎页面,然后自动跳转首页
  2. 首页竖排展示诗词
  3. 诗词信息列表展示,点击跳转详情页面

开始

安装好react-native环境后,我们运行react-nativeinitRNPoetry命令生成项目,进入目录运行yarnruniosorandroid命令后就可以启动项目,工欲善其事必先利其器,首先我们不急着进行项目的开发先来学习一下react-native中的调试技巧。

首先我们先了解一下RN的DeveloperMenuDeveloperMenureact-native给开发者定制的一个开发者菜单,来帮助开发者调试RN应用,在Android模拟器中通过快捷键Command⌘M来打开,在iOS模拟器中通过快捷键Command⌘D快速打开。DeveloperMenu中有很多选项其中我们常用的有:

  1. Reload手动重新加载项目,在Android模拟器中快捷键为R,R,在iOS模拟器中快捷键为Command⌘R,重新加载的内容是js,如果涉及到原生的修改需要重新启动项目。
  2. Debug启动js远程调试功能,会主动打开Chrome创建一个“http://localhost:8081/debugger-ui.”标签页,在该标签页打开开发者功能就可以对RN进行调试。
  3. ShowInspector这个功能和Chrome中的审查元素功能是一样的,主要是查看元素布局和样式,当你的样式和你想要的效果不一致的时候,可以使用该功能查找原因。
  4. EnableLiveReload这个选项提供项目动态加载的功能。当你的js代码发生变化之后,RN会自动的生成bundle然后传输到模拟器或手机上。

了解了RN中的调试技巧后我们就正式进行入到项目的开发中吧!

安装需要的包

yarnaddreact-navigationreact-native-reanimatedreact-native-gesture-handlerreact-native-screensreact-navigation-stackreact-navigation-tabsreact-native-vector-icons

react-navigation相关包的安装和配置

react-navigation是RN的常用的导航组件,我们运行命令yarnaddreact-navigation来安装react-navigation,然后运行yarnaddreact-native-reanimatedreact-native-gesture-handlerreact-native-screens来安装相关的依赖包,如果是iOS需要确保安装了Cocoapods然后运行命令:

cdiospodinstallcd..

在Android中为了完成react-native-screens的安装必须在android/app/build.gradle中dependencies选项中添加下面这两行:

implementation'androidx.appcompat:appcompat:1.1.0-rc01'implementation'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha02'

为了完成react-native-gesture-handler的安装必须在MainActivity.java中做如下修改:

packagecom.reactnavigation.example;importcom.facebook.react.ReactActivity;<!--添加内容-->importcom.facebook.react.ReactActivityDelegate;importcom.facebook.react.ReactRootView;importcom.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView;publicclassMainActivityextendsReactActivity{@OverrideprotectedStringgetMainComponentName(){return"Example";}<!--添加内容-->@OverrideprotectedReactActivityDelegatecreateReactActivityDelegate(){returnnewReactActivityDelegate(this,getMainComponentName()){@OverrideprotectedReactRootViewcreateRootView(){returnnewRNGestureHandlerEnabledRootView(MainActivity.this);}};}}

以上是在RN为0.6以上版本需要进行的操作,如果是0.6一下还需要link操作,具体查看这里。

使用react-navigation实现导航肯定会用到导航器,导航器也可以看成是⼀个普通的React组件,你可以通过导航器来定义你的APP中的导航结构。导航器还可以渲染通⽤用元素,例如可以配置的标题栏和选项卡栏。

react-navigation中有多种类型的导航器以适应不同的应用场景:

  • createStackNavigator:类似普通的Navigator,导航上⽅方导航栏
  • createTabNavigator:已弃⽤用,使⽤用createBottomTabNavigator、
  • createMaterialTopTabNavigator替代
  • createBottomTabNavigator:相当于IOS⾥里里⾯面的UITabBarController,屏幕下⽅方的标签栏
  • createMaterialTopTabNavigator:屏幕顶部的Material设计主题标签栏
  • createDrawerNavigator:抽屉效果,侧边滑出
  • createSwitchNavigator:SwitchNavigator的⽤用途是⼀一次只显示⼀一个⻚页⾯面,常⽤用于welcome⻚页⾯面或者登陆⻚页⾯面,这种⻚页⾯面没有回退操作。

我们可以通过不同的导航器来创建App,可以选择其中的一个也可以多个组合,这个根据具体的场景选择使用。在我们的项目中我们将用到createStackNavigator来实现基本的页面跳转,createBottomTabNavigator实现底部标签栏导航,createSwitchNavigator来实现启动欢迎页面。其中除了createSwitchNavigator在我们已经安装的react-navigation中,createStackNavigatorreact-navigation-stack中,createBottomTabNavigatorreact-navigation-tabs中,所以我们需要再在项目中添加这两个项目:

yarnaddreact-navigation-stackreact-navigation-tabs

react-native-vector-icons的安装

react-native-vector-icons是一个图标组件库,包含了多个图标库,也支持自定义图标的使用。运行命令yarnaddreact-native-vector-icons来安装它,然后进行link操作react-nativelinkreact-native-vector-icons,在安卓中使用需要在android/app/build.gradle中添加:

applyfrom:"../../node_modules/react-native-vector-icons/fonts.gradle"

在iOS中link操作可能报错,我碰到的错误如下:

Buildsysteminformationerror:Multiplecommandsproduce'/Users/zhangxiangchen/Code/demo/RNPoetry/ios/build/RNPoetry/Build/Products/Debug-iphonesimulator/RNPoetry.app/Entypo.ttf':1)Target'RNPoetry'(project'RNPoetry')hascopycommandfrom'/Users/zhangxiangchen/Code/demo/RNPoetry/node_modules/react-native-vector-icons/Fonts/Entypo.ttf'to'/Users/zhangxiangchen/Code/demo/RNPoetry/ios/build/RNPoetry/Build/Products/Debug-iphonesimulator/RNPoetry.app/Entypo.ttf'2)ThatcommanddependsoncommandinTarget'RNPoetry'(project'RNPoetry'):scriptphase“[CP]CopyPodsResources”

解决方式是使用xcode打开项目,然后在target->Buildphase>CopyPodsResources->OutputFiles,移除${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH},这个问题可能是由于cocoaPod版本导致的,你也可以通过升级cocoaPod来解决

geminstallcocoapods--pre

除了link方法你也可以通过其他方式引入字体文件,具体查看官方文档;link操作引起的问题也可能是其他情况,具体可以查看这里。

构建页面

增加的目录结构:

使用react-navigation构建路由

我们启动App,首先进入一个欢迎页面Welcome,在欢迎页面停留2s后,跳转到主页面Main,Welcome我们只会在启动时打开,也不会需要跳转或返回这个页面,所以我们使用SwitchNavigator来实现的;Main是由Home、Poetry、My通过BottomTabNavigator来实现,他们通过底部导航栏进行跳转;然后每个Tab页面其实都是由StackNavigator来实现的。

我们先了解下createStackNavigator方法,createStackNavigator(RouteConfigs,StackNavigatorConfig)方法接收两个值:

  • RouteConfigs(必选):路路由配置对象是从路路由名称到路路由配置的映射,告诉导航器器该路路由呈现什什么。
  • StackNavigatorConfig(可选):配置导航器器的路路由(如:默认⾸首屏,navigationOptions,paths等)样式(如,转场模式mode、头部模式等)。

RouteConfigs支持三个参数screen、path以及navigationOptions;

  • screen(必选):指定⼀个React组件作为屏幕的主要显示内容,当这个组件被createStackNavigator加载时,它会被分配一个navigationprop。
  • navigationOptions(可选):⽤以配置全局的屏幕导航选项如:title、headerRight、headerLeft等;

StackNavigatorConfig更加复杂,支持配置的参数很多,这里我们不详细列举,把我们会用到的一些参数说一下:

  • initialRouteName:设置默认的页面组件,必须是上面已注册的页面组件。
  • initialRouteParams:初始路由的参数。
  • initialRouteKey-初始路由的可选标识符。
  • defaultNavigationOptions:屏幕导航的默认选项。
  • navigationOptions:导航器本身的导航选项,⽤于配置父导航器
  • mode:⻚面切换模式:左右是card(相当于iOS中的push效果),上下是modal(相当于iOS中的modal效果)
    • card:普通app常⽤用的左右切换。
    • modal:上下切换。

然后我们使用Poetry和PoetryDetail来实现最基础的StackNavigator:

import{createStackNavigator}from'react-navigation-stack';constPoetryStackNavigator=createStackNavigator({Poetry:Poetry,PoetryDetail:{screen:PoetryDetail,},},{initialRouteName:'Poetry',},);exportdefaultPoetryStackNavigator;

然后使用同样的方式实现Home、My的导航器,然后再使用createBottomTabNavigator来实现底部导航:

createBottomTabNavigator({Home:{screen:HomeStackNavigator,navigationOptions:{tabBarLabel:'首页',},},Poetry:{screen:PoetryStackNavigator,navigationOptions:{tabBarLabel:'诗词',},},My:{screen:MyStackNavigator,navigationOptions:{tabBarLabel:'我的',},},},{initialRouteName:'Home'},);

此时底部导航栏是没有图标的,我们引入react-native-vector-icons在底部导航栏中添加图片,importIconfrom'react-native-vector-icons/FontAwesome';引入图标,然后在createBottomTabNavigatorStackNavigatorConfig中添加:

defaultNavigationOptions:({navigation})=>({tabBarIcon:({tintColor})=>{const{routeName}=navigation.state;constmapIcon={Home:'home',My:'user-circle',Poetry:'tasks',};return<Iconname={mapIcon[routeName]}size={20}color={tintColor}/>;},}),tabBarOptions:{activeTintColor:'#C20C0C',inactiveTintColor:'gray',},

tabBarIcon的返回值就是展示的图片,我们从navigation.state中获取routeName,然后映射为不同的图标返回,底部导航栏就会根据不同的routeName返回不同的。tabBarOptions设置底部导航栏在激活状态和非激活状态不同的颜色。

最后我们再来实现一下switchNavigator,代码实现相对简单:

constswitchNavigator=createSwitchNavigator({Init:WelcomeStackNavigator,Main:TabNavigator,});

现在我们项目的基本结构已经完成了,启动一下我们的项目吧!启动项目我们会发现我们一直停留在Welcome,这是因为我们的页面并没有做任何处理,接下来我们来完成页面的实现。

页面的实现

Welcome页面是我们启动时看到的第一个页面,这个页面的主要功能是作为承接页面,用户停留一段时间后应该自动跳转至首页,使用StyleSheet创建简单的样式,然后使用定时器跳转到主页面:

conststyles=StyleSheet.create({container:{flex:1,alignItems:'center',justifyContent:'center',backgroundColor:'#C20C0C',},tips:{fontSize:30,color:'#fff',},});classWelcomeextendsComponent{componentDidMount(){setTimeout(()=>{this.props.navigation.navigate('Main');},2000);}render(){return(<Viewstyle={styles.container}><Textstyle={styles.tips}>诗词墨客</Text></View>);}}

从Welcome页面跳转过来我们会进入到我们的首页之中,首页主要是需要实现一个文字竖排的效果;css中可以通过writing-mode进行设置,writing-mode有下面5个值:

  • horizontal-tb内容从左到右水平,从上到下垂直。下一条水平线位于上一条线的下方。
  • vertical-rl内容从上到下垂直流动,从右到左水平流动。下一条垂直线位于上一行的左侧。
  • vertical-lr内容从上到下垂直流动,从左到右水平流动。下一条垂直线位于前一行的右侧。
  • sideways-rl内容从上到下垂直流动,所有字形(即使是垂直脚本中的字形)也都向右侧设置。
  • sideways-lr内容从上到下垂直流动,所有字形(即使是垂直脚本中的字形)也朝左侧设置。

RN中也有一个类似的属性writingDirection,不过只支持三个属性'auto','ltr','rtl',其中并没有能够设置文字竖排的属性,所以我们只能使用其他方式来实现文字竖排效果了;我的解决方案是将Text嵌套到View中,然后给View一个宽度,这个宽度使Text只能每行显示一个字,从视觉效果上来看的确是实现了,不过这个方案对标点符号的支持不是很完美。

conststyles=StyleSheet.create({container:{flex:1,alignItems:'center',justifyContent:'center',backgroundColor:'#efefef',},wrapper:{backgroundColor:'#fff',width:'80%',height:'80%',flexDirection:'row-reverse',padding:24,position:'relative',},section:{width:40,},contentText:{fontSize:30,},title:{width:20,position:'absolute',right:24,bottom:24,},titleText:{fontSize:16,},});classHomeextendsComponent{staticnavigationOptions={headerTitle:'首页',};render(){return(<Viewstyle={styles.container}><Viewstyle={styles.wrapper}><Viewstyle={styles.section}><Textstyle={styles.contentText}>欲出未出光辣達,千山萬山如火發。</Text></View><Viewstyle={styles.section}><Textstyle={styles.contentText}>須臾走向天上來,逐却殘星趕却月。</Text></View><Viewstyle={styles.title}><Textstyle={styles.titleText}>「日詩」宋太祖</Text></View></View></View>);}}

Home页面样式完成,并且现在的Home页面Header标题变为首页,这是因为我们在组件中设置了navigationOptions,每个页面的navigationOptions被用来设置页面的标题样式,可以是一个对象,也可以是一个函数,比如我们的PoetryDetail页面,我们的标题名称会根据内容改变,我们通过获取页面传值的信息,更新标题名称:

staticnavigationOptions=({navigation})=>{constitem=navigation.state.params.item;return{headerTitle:item.title,};};

最好我们来实现Poetry和PoetryDetail页面,Poetry是一个列表页面,我们使用RN的FlatList组件来实现,FlatList是一个高效率的列表组件,它通过维护一个有限的渲染窗口(其中包含可见的元素),将渲染窗口之外的元素全部用合适的定长空白空间来代替,改善内存的使用,提高了大量数据情况下的渲染性能问题;这个渲染窗口能响应滚动行为,元素里可视区域越远优先级越低,越近优先级越高,但是当用户滑动过快的时候,会出现短暂空白的情况;它还支持上拉加载,下拉刷新等功能。

列表的元素我们通过TouchableHighlight来实现点击事件和效果,TouchableHighlight会产生一个变暗的效果,在RN中相应用户的点击事件,我们经常使用下面几个组件:

  • TouchableWithoutFeedback:响应用户的点击事件,如果你想在处理点击事件的同时不显示任何视觉反馈,使用它是个不错的选择。
  • TouchableHighlight:在TouchableWithoutFeedback的基础上添加了当按下时背景会变暗的效果。
  • TouchableOpacity:相比TouchableHighlight在按下去会使背景变暗的效果,TouchableOpacity会在用户手指按下时降低按钮的透明度,而不会改变背景的颜色。
  • TouchableNativeFeedback:在Android上还可以使用TouchableNativeFeedback,它会在用户手指按下时形成类似水波纹的视觉效果。注意,此组件只支持Android。

TouchableHighlight上绑定onPress事件,跳转PoetryDetail页面并且把数组元素作为参数传递过去:

classPoetryItemextendsComponent{render(){const{item,handler}=this.props;return(<TouchableHighlightonPress={()=>handler()}><Viewstyle={styles.item}><Viewstyle={styles.itemHeader}><Textstyle={styles.titleText}>{item.title}</Text><Textstyle={styles.authorText}>[宋]{item.author}</Text></View><View><Textstyle={styles.contentText}>{item.paragraphs[0]}</Text></View></View></TouchableHighlight>);}}classPoetryextendsComponent{staticnavigationOptions={headerTitle:'诗词',};render(){const{navigation}=this.props;return(<Viewstyle={styles.contianer}><FlatListdata={list}renderItem={({item})=>(<PoetryItemitem={item}handler={()=>navigation.navigate('PoetryDetail',{item})}/>)}/></View>);}}

PoetryDetail就比较简单了,按照设计完成页面,除了之前提到的navigationOptions动态修改标题只剩下一些基础的样式编写。

完成后的效果如上图所示;这里我们会发现还有一个问题,就是当跳转PoetryDetail页面的时候,底部导航栏依旧存在,我们编写路由的时候,StackNavigator是被包含在BottomTabNavigator中的,即使StackNavigator的路由变化了,依旧是不会影响BottomTabNavigator导航器的底部导航栏。我们要在StackNavigator中对底部导航栏进行处理,在第一个路由时,显示底部导航栏然后在之后的路由不显示:

PoetryStackNavigator.navigationOptions=({navigation})=>{lettabBarVisible=true;if(navigation.state.index>0){tabBarVisible=false;}return{tabBarVisible,};};

其他

本篇为诗词墨客App构建了一个基本项目框架,在下一篇文章中我们会通过网络请求来获取数据,使用Redux结合react-navigation来管理数据,使用FlatList组件的更多功能使我们的App根据完善。

项目仓库地址:github.com/x007xyz/RNP…

发表评论

  • 评论列表
还没有人评论,快来抢沙发吧~