内容简介:需要注意的是为了看起来稍微好看点,加了一点样式,不加也可以。style.js别忘了在route里配置新的路由,完整代码如下:
还是摘自L小庸的文章,加入了一点个人的实践和理解
经过第一部分 开发 React Native APP —— 从改造官方 Demo 开始(1) 介绍,App 框架基本构建完成,这部分主要关注 UI/交互、App 发布前的准备工作及如何发布,具体内容包括:
- 在使用 react navigation 的前提下, iOS 实现单个页面从下往上(modal)的进入动画
- 尺寸自适应
- 设置启动页,更换桌面图标、app 展示名称、appID
- 打包发布
一 扩展 react navigation
这部分在android下无效…… ,个人是win,所以后面没有展示效果图,有mac的可以尝试下效果复制代码
官方标注:modal - Make the screens slide in from the bottom which is a common iOS pattern. Only works on iOS, has no effect on Android.
这里的扩展指的是实现 可单独配置页面的进入方式 (react navigation 默认只支持全局配置,要么 card ,要么 modal ,配置后所有页面进入动画相同)。
关于card和modal,个人还并不是很理解,后续理解了再补上
实现上述效果需要做两方面修改: createStackNavigator API(在 route.js 中使用)和进入某个页面是的调用方式。
1.1 修改 StackNavigator API
..navigate('ScreenSome1') ;如果要使某个页面进入方式为 modal 只需要在路径上加上 Modal 比如:
..navigate('ScreenSome2Modal') 。
需要注意的是 如果页面进入方式为 modal,需要自定义 header,因为默认 header 样式失效,都叠在一块了 。(还没有实际遇到,先mark着)
1.2 页面中调用
首先我们新建页面 ScreenSome2 ,接下来就让它以 modal 的形式进入(从屏幕下面进入),作为对比 ScreenSome1 以 card 的形式进入(默认进入方式,从屏幕右侧进入)。
因为以 modal 形式进入的页面需要自定义 header,一般只是一个关闭按钮,以 ScreenSome2 为例:
/**
* ScreenSome2/view.js
* 自定义 header(关闭按钮)
*/
import React from 'react';
import { TouchableHighlight,Text,View } from 'react-native'
import pxToDp from '../../config/pxToDp';
<View>
<TouchableHighlight
onPress={() => self.navigation.goBack()}
underlayColor="transparent"
style={{
display: "flex",
justifyContent: "center",
marginTop: pxToDp(30),
width: pxToDp(150),
height: pxToDp(90),
backgroundColor: "yellow"
}}
>
<Text style={{ marginLeft: pxToDp(24) }}>关闭</Text>
</TouchableHighlight>
<Text style={{ fontSize: pxToDp(36) }}>some2,以 modal 的形式进入</Text>
</View>复制代码
ScreenSome2 代码:
/**
* ScreenSome2/view.js
* 自定义 header(关闭按钮)
*/
import React from 'react';
import { TouchableHighlight,Text,View } from 'react-native'
import pxToDp from '../../config/pxToDp';
export default self => (
<View>
<TouchableHighlight
onPress={() => self.navigation.goBack()}
underlayColor="transparent"
style={{
display: "flex",
justifyContent: "center",
marginTop: pxToDp(30),
width: pxToDp(150),
height: pxToDp(90),
backgroundColor: "yellow"
}}
>
<Text style={{ marginLeft: pxToDp(24) }}>关闭</Text>
</TouchableHighlight>
<Text style={{ fontSize: pxToDp(36) }}>some2,以 modal 的形式进入</Text>
</View>
);复制代码
为了看起来稍微好看点,加了一点样式,不加也可以。style.js
import { StyleSheet } from 'react-native';
export default StyleSheet.create({
buttonContainer: {
margin: 20
},
});复制代码
别忘了在route里配置新的路由,完整代码如下:
// 引入依赖
import React from 'react';
import { createStackNavigator, createAppContainer } from 'react-navigation'
// 引入页面组件
import ScreenBottomTab from '../screens/ScreenBottomTab';
import ScreenHome from '../screens/ScreenHome';
import ScreenSome1 from '../screens/ScreenSome1';
import ScreenSome2 from '../screens/ScreenSome2';
import ScreenTab1 from '../screens/ScreenTab1';
import ScreenTab2 from '../screens/ScreenTab2';
import ScreenTab3 from '../screens/ScreenTab3';
/**
* 自定义 StackNavigator,可以选择 screen 进入方式
* 默认状态为 card,只需要输入对应页面,比如 ..navigate('ScreenSome1')
* 如果要使某个页面进入方式为 modal 只需要在路径上加上 Modal
* 比如:..navigate('ScreenSome2Modal')
*/
const StackModalNavigator = (routeConfigs, navigatorConfig) => {
const CardStackNavigator = createStackNavigator(routeConfigs, navigatorConfig);
const modalRouteConfig = {};
const routeNames = Object.keys(routeConfigs);
for (let i = 0; i < routeNames.length; i++) {
modalRouteConfig[`${routeNames[i]}Modal`] = routeConfigs[routeNames[i]];
}
const ModalStackNavigator = createStackNavigator(
{
CardStackNavigator: { screen: CardStackNavigator },
...modalRouteConfig,
},
{
// 如果页面进入方式为 modal,需要自定义 header(默认 header 样式失效,都叠在一块了)
mode: 'modal',
headerMode: 'none',
},
);
return ModalStackNavigator;
};
// 配置路由
const AppNavigator = StackModalNavigator({
ScreenBottomTab: ScreenBottomTab,
//下面几个配置的是测试Navigator不同使用场景用,只需要tab的话,只要ScreenBottomTab: ScreenBottomTab即可
ScreenHome: ScreenHome,
ScreenSome1: ScreenSome1,
ScreenSome2: ScreenSome2,
ScreenTab1: ScreenTab1,
ScreenTab2: ScreenTab2,
ScreenTab3: ScreenTab3,
});
const App = createAppContainer(AppNavigator)
export default App
复制代码
this.props.navigation有很多方法,demo中(ScreenHome,ScreenSome1)只用到.navigate,.push,.goBack,具体可参考https://reactnavigation.org/docs/zh-Hans/navigation-prop.html
二 自适应
自适应主要包括两方面:尺寸根据屏幕大小自适应,包括 fontSize , width 等;图片分辨率根据屏幕分辨率自适应,也就常说的二倍图、三倍图等。
2.1 尺寸自适应
copy自@L小庸的代码片段
尺寸自适应的原理是通过获取手机屏幕的宽度,尺寸做相应比例的调整,为此封装了一个 工具 函数,放在了 config/pxToDp.js 中。
-
config/pxToDp.js尺寸转换的工具函数
1)编写自适应尺寸工具函数
因为所有涉及尺寸的数据都要转换( fontSize , width 等),所以对转换后的数据要做处理,保证:1.大于等于 1 的数字向上取整;2.小于 1 的数字,如果是 ios 平台统一设为 0.5;如果是安卓平台统一设为 1(因为安卓平台分辨率千差万别万别,低分辨率的屏幕显示 0.5 的尺寸会有锯齿状)。工具函数完整代码如下:
/**
* 自适应布局
* @param uiElementPx: ui给的原始尺寸
*/
import { Dimensions, Platform } from 'react-native';
// app 只有竖屏模式,所以可以只获取一次 width
const deviceWidthDp = Dimensions.get('window').width;
// UI 默认给图是 750
const uiWidthPx = 750;
function pxToDp(uiElementPx) {
const transferNumb = uiElementPx * deviceWidthDp / uiWidthPx;
if (transferNumb >= 1) {
// 避免出现循环小数
return Math.ceil(transferNumb);
} else if (Platform.OS === 'android') {
// 如果是安卓,最小为1,避免边框出现锯齿
return 1;
}
return 0.5;
}
export default pxToDp;
复制代码
Dimensions.get('window').width 获取的屏幕宽度和自己想象的可能有出入,比如,iphone7 屏幕 4.7'',获取到的宽度是
375 ,华为 P9 是 5.2',但获取到的宽度却是是
360 !有点坑,这个工具函数还有待优化。个人还没有遇到这么多场景,也还没有更好的方案,如果踩坑,后续更新。
2.3 图片分辨率自适应
手机分辨率越来越多,尤其安卓,React Native 可以根据不同分辨率加载不同尺寸的图片,只需在图片命名上面加以区分。
- 提供不同分辨率的图片
比如我们有张图片叫 test.png ,尺寸为 40 x 40 (单位像素),为了做到自适应屏幕分辨率,我们还需要提供它的 2 倍图,3 倍图,这样,一张图片就对应 3 个尺寸,如下:
# 一张图片提供 3个尺寸 test.png # 尺寸 40 x 40 test@2x.png # 尺寸 80 x 80 test@3x.png # 尺寸 120 x 120复制代码
name@nx 是 n (n > 1) 倍图命名规范,React Native 也是根据命名判断图片尺寸的。
- 使用
在引用图片的时候直接使用 不加倍率后缀的图片名 ,比如,直接使用 test.png ,如下:
/**
* ScreenTab3/view.js
*/
<Image
source={require("../../assets/images/test.png")}
style={{ height: pxToDp(80), width: pxToDp(80) }}
/>复制代码
三 修改桌面图标、App 展示名称,设置启动页
修改桌面图标、App 展示名称相对简单,设置启动页稍微麻烦。
IOS的设置并没有mac去尝试,可参考@L小庸的https://juejin.im/post/5a9602c45188257a7262e3fb尝试,
踩坑: 开发要连真机调试,尤其是涉及原生功能的部分,切记。
3.1 设置桌面图标
因为 App 图标对应多个尺寸,手动改写太麻烦,这个网站可以自动生成MakeAppIcon。
并不是所有尺寸的图片都需要,见下文。
- 安卓
安卓的 app 图标相对简单,只需要设置桌面图标。设置位置在 yourApp/android/app/src/main/res/ 目录下,
这个目录默认有5个文件夹,里面各对应放置了一种尺寸的桌面图标图片,图片尺寸不同,但名称相同,统一为 ic_launcher.png ,具体如下所示:
| 文件夹名称 | 含义 | 文件夹内部图片尺寸 | 文件夹内部图片名称 |
|---|---|---|---|
| mipmap-ldpi | Low Density Screen | 36x36 | ic_launcher.png |
| mipmap-mdpi | Medium Density Screen | 48x48 | ic_launcher.png |
| mipmap-hdpi | High Density Screen | 72x72 | ic_launcher.png |
| mipmap-xhdpi | Extra-high density screen | 96x96 | ic_launcher.png |
| mipmap-xxhdpi | xx-high density screen | 144x144 | ic_launcher.png |
| mipmap-xxxhdpi | xxx-high density screen | 192x192 | ic_launcher.png |
如果你使用了 MakeAppIcon 的服务,直接将对应文件夹全部放入 res/ 目录下就好,不然就手动替换图标。
可以根据实际需求删除不必要的文件,比如,120 DPI 的屏幕很少了,那么这个文件夹就可以不要
3.2 修改 App 展示名称
IOS的参考@L小庸的https://juejin.im/post/5a9602c45188257a7262e3fb尝试
- 安卓
安卓修改 App 展示名称在这个文件中 yourApp/android/app/src/main/res/values/strings.xml 。
strings.xml 这个文件很简单,全部内容如下:
<resources>
<string name="app_name">你的app名称</string>
</resources>
复制代码复制代码
替换 你的app名称 为你想要的名字就好。
安卓的话,还要修改默认包名( applicationId ),如果不修改,如果系统监测到当前应用的 applicationId 和已安装的某个应用相同而签名不同,会报错:“签名不一致 该应用可能已被恶意篡改”。
在这个文件中修改包名: yourApp/android/app/build.gradle :
// ...
defaultConfig {
applicationId "com.yourAppId"
// ...
}
// ...复制代码
3.3 设置启动页
这里使用了第三方插件 react-native-splash-screen ,官网教程已经很详细,这里做简要介绍。
- 项目中安装依赖
1)下载依赖
yarn add react-native-splash-screen复制代码
2)添加到项目中
react-native link react-native-splash-screen复制代码
3)在 React Native 配置
这里指的是设置启动页什么时候消失,下面的代码是首页加载完 5s 后启动页消失。
export default class ScreenHome extends Component {
// ...other code
componentDidMount() {
// 隐藏启动页,如果不设置消失时间,在组件加载完启动页自动隐藏
setTimeout(() => {
SplashScreen.hide();
}, 5000);
}
// ...other code
}复制代码
- iOS 设置参考@L小庸的https://juejin.im/post/5a9602c45188257a7262e3fb尝试
- 安卓配置
1)更新 MainActivity.java(yourApp/android/app/src/main/java/com/yourApp/MainActivity.java) :
import android.os.Bundle; // here
import com.facebook.react.ReactActivity;
// react-native-splash-screen >= 0.3.1
import org.devio.rn.splashscreen.SplashScreen; // here
// react-native-splash-screen < 0.3.1
import com.cboy.rn.splashscreen.SplashScreen; // here
public class MainActivity extends ReactActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
SplashScreen.show(this); // here
super.onCreate(savedInstanceState);
}
// ...other code
}复制代码
2)新建 launch_screen.xml
在 app/src/main/res/layout 中创建 launch_screen.xml (如果没有 layout 目录,新建),内容如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/launch_screen">
</LinearLayout>复制代码
3)准备不同尺寸的启动页图片并放到项目中
安卓是通过文件夹路径寻找启动页面的,所以,多张尺寸的启动页名称相同,都为 launch_screen.png ,但要放在不同文件夹中,文件夹放置目录为 yourApp/android/app/src/main/res/ ,名称及对应放置的图片尺寸如下:
| 文件夹名称 | 含义 | 文件夹内部图片尺寸 | 文件夹内部图片名称 |
|---|---|---|---|
| drawable-ldpi | Low Density Screen | 240x320 | launch_screen.png |
| drawable-mdpi | Medium Density Screen | 320x480 | launch_screen.png |
| drawable-hdpi | High Density Screen | 480x800 | launch_screen.png |
| drawable-xhdpi | Extra-high density screen | 720x1280 | launch_screen.png |
| drawable-xxhdpi | xx-high density screen | 960x1600 | launch_screen.png |
| drawable-xxxhdpi | xxx-high density screen | 1280x1920 | launch_screen.png |
480x800 起步放置 4 张图片就好。
文件必须是 png 格式的图片,开始随便找了一张图片,结果打包各种报错 ,命名需对应尺寸
4)优化启动页出现前的短暂白屏(copy,踩坑填坑)
到这里,启动页功能已经 ok,但如果仔细看,可以看到启动页出现前会有短暂白屏,此时可通过更改 android/app/src/main/res/values/styles.xml 解决:
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<!--设置透明背景-->
<item name="android:windowIsTranslucent">true</item>
</style>
</resources>复制代码
这种方案实际没有根本解决问题:会发现这样设置以后点击图片不能立即弹出应用,而有短暂的等待时间,待填坑。
5) 解决安卓 6.0,7.0 安装配置完成后出现闪退 ,参考下面设置:(copy,踩坑填坑)
在 android/app/src/main/res/values 下面新建 colors.xml 文件,内容如下:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- this is referenced by react-native-splash-screen and will throw an error if not defined. its value does nothing, just here to avoid a runtime error. -->
<color name="primary_dark">#000000</color>
</resources>复制代码
primary_dark 的颜色值作为状态栏的颜色。
3.4 最终效果图
设置完桌面图标、修改 APP 展示名称及设置启动页之后的效果图如下:
demo里后续添加了自己练习的部分,所以效果图和代码稍有差别,需要的只用care文章里的步骤就好
四 打包发布
4.1 安卓打包发布
- 生成签名
keytool -genkey -v -keystore my-release-key.keystore -alias my-key-alias -keyalg RSA -keysize 2048 -validity 10000
- 设置 gradle 变量
1)首先将签名文件 my-release-key.keystore 放在目录 yourApp/android/app/ 下
2)修改文件 yourApp/android/gradle.properties 添加下面代码 (替换 ***** 为正确的 keystore 密码、别名、和 key 密码):
注意这里的 MYAPP_RELEASE_STORE_PASSWORD和 MYAPP_RELEASE_KEY_PASSWORD一定要和生成签名时的密码保持一致
MYAPP_RELEASE_STORE_FILE=my-release-key.keystore MYAPP_RELEASE_KEY_ALIAS=my-key-alias MYAPP_RELEASE_STORE_PASSWORD=***** MYAPP_RELEASE_KEY_PASSWORD=*****复制代码
3)添加签名信息到 app 的 gradle 配置中
编辑文件 yourApp/android/app/build.gradle 加入签名信息
android {
...
defaultConfig { ... }
signingConfigs {
release {
if (project.hasProperty('MYAPP_RELEASE_STORE_FILE')) {
storeFile file(MYAPP_RELEASE_STORE_FILE)
storePassword MYAPP_RELEASE_STORE_PASSWORD
keyAlias MYAPP_RELEASE_KEY_ALIAS
keyPassword MYAPP_RELEASE_KEY_PASSWORD
}
}
}
buildTypes {
release {
...
signingConfig signingConfigs.release
}
}
}复制代码
- 打包
在终端输入下面命令
cd android && ./gradlew assembleRelease复制代码
等待构建完成,便可以在 yourApp/android/app/build/outputs/apk/release/app-release.apk 中找到编译后的发布版本。
NOTE:如果遇到这个错误: Execution failed for task ':app:processReleaseResources' ,做下述修改:,个人没有遇到,mark先~
在 yourApp/android/gradle.properties 文件最后添加下面代码:
classpath 'com.android.tools.build:gradle:3.0.0' distributionUrl=https://services.gradle.org/distributions/gradle-4.1-all.zip android.enableAapt2=false复制代码
NOTE:如果遇到Execution failed for task ':app:bundleReleaseJsAndAssets'.不幸的遇到了这个错…………
删除 android/app/build and android/build folders and run react-native run-android again
五 小结
到目前为止,从改造官方 demo 开始,一个比较完整的 React Native App 完成了,在此基础上可以不断扩展完善。
后面如果有有空会在各部分加入更多详细的demo和使用场景,以及踩坑分享。
当然,从生产角度来说,这个 demo 的完成度不高,比如,很多样式还是最原始的状态、比如 WebView(App 中嵌入 H5)、下拉刷新等也没有涉及。其中 WebView、下拉刷新等常用功能会逐步集成到这个 demo 中,但样式并不打算做过多优化,因为从使用角度来讲,样式的完成度越高意味着可定制性越差,并且,那样也会导致代码的可读性变差。希望这个 demo 可以成为完整、普适但不臃肿的脚手架。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Alone Together
Sherry Turkle / Basic Books / 2011-1-11 / USD 28.95
Consider Facebookit’s human contact, only easier to engage with and easier to avoid. Developing technology promises closeness. Sometimes it delivers, but much of our modern life leaves us less connect......一起来看看 《Alone Together》 这本书的介绍吧!