文章目录
- React Native实战
- 一、项目准备
- 1.1 创建原始项目
- 1.2 使用 react-navigation 搭建页面路由
- 1.2.1 安装react-navigation相关依赖
- 1.2.2 修改App文件
- 1.2.3 项目启动报错
- 二、项目开发
- 2.1 登录页面
- 2.1.1 背景图片实现
- 2.1.2 透明状态栏
- 2.1.3 手机dp单位与px单位的转化
- 2.1.4 引入react-native-elements
- 2.1.5 input输入框的使用
- 2.1.6 发送axios请求
- 2.1.7 创建渐变色的点击按钮
- 2.1.8 创建loading效果
- 2.1.9 根据showLogin是否显示填写验证码界面
- 2.1.10 创建验证码输入框
- 2.1.11 验证码倒计时
- 2.1.12 发送验证码验证请求
- 2.2 完善个人信息页面
- 2.2.1 使用阿里巴巴字体svg
- 2.2.2 datepicker 日期选择器
- 2.2.3 高德地图组件
- 2.2.4 创建城市选择组件
- 2.2.5 设置头像组件
- 2.2.6 实现头像审核中的效果
- 2.2.7 在 Android 上支持 GIF 和 WebP 格式图片
- 2.2.8 RN引入本地图片和外网图片方式
- 2.2.9 使用mobx存储全局数据(类似redux)
- 2.2.10 封装 request 实现自动携带 token
- 2.2.11 注册 极光用户(实现实时聊天)
- 2.2.11.1 [开通服务](https://www.jiguang.cn)
- 2.2.11.2 简单使用
- 2.3 实现tabbar结构
- 2.3.1 实现步骤
- 2.3.2 [react-native-tab-navigator](https://www.npmjs.com/package/react-native-tab-navigator)
- 2.3.3 将token存储到本地缓存
- 2.4 交友页面
- 2.4.1 实现顶部图片吸顶效果
- 2.4.2 访客模块
- 2.4.3 今日佳人模块
- 2.4.4 rn中使用普通的iconfont字体
- 2.4.5 筛选功能
- 2.4.6 推荐列表
React Native实战
一、项目准备
1.1 创建原始项目
npx react-native init tanhuajiaoyou
1.2 使用 react-navigation 搭建页面路由
react-navigation官网
1.2.1 安装react-navigation相关依赖
yarn add react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view @react-navigation/stack @react-navigation/native
1.2.2 修改App文件
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import Login from '../pages/account/login';
import Demo from '../components/Demo';
const Stack = createStackNavigator();
const Nav = () => {
return (
<NavigationContainer>
<Stack.Navigator headerMode="none" initialRouteName="Login">
<Stack.Screen name="Login" component={Login} />
<Stack.Screen name="Demo" component={Demo} />
</Stack.Navigator>
</NavigationContainer>
);
}
export default Nav;
1.2.3 项目启动报错
"RNCSafeAreaView was not found in the UIManager"
和
Failed to find build tools revision 29.0.2 - Android Studio
其实都是SDK build tools 版本不匹配的情况
参考网址
进入到设置界面,展开“外观&行为”-“系统设置”,点击“Android SDK”-“SDK Tools”。
设置SDK Tools的版本
二、项目开发
2.1 登录页面
2.1.1 背景图片实现
<Image style={styles.image} source={require('../../../res/profileBackground.jpg')}/>
2.1.2 透明状态栏
<StatusBar backgroundColor="transparent" translucent={true}/>
2.1.3 手机dp单位与px单位的转化
import {Dimensions} from 'react-native';
// 设计稿的宽度/元素的宽度 = 手机屏幕/手机中元素的宽度
// 手机中元素的宽度 = 手机屏幕 * 元素的宽度 / 设计稿的宽度
/**
* 付民康 2021/3/11
* desc: 获取手机屏幕的宽度
* @params
**/
export const screenWidth = Dimensions.get('window').width;
/**
* 付民康 2021/3/11
* desc: 获取手机屏幕的高度
* @params
**/
export const screenHeight = Dimensions.get('window').height;
// 默认设计稿宽度为375;
let designWidth = 375;
/**
* 付民康 2021/3/11
* desc: 将px转为dp
* @params elePx:元素的宽度或者高度 单位px
**/
export const pxToDp = (elePx) => screenWidth * elePx / designWidth;
2.1.4 引入react-native-elements
一套ui库 内置常用组件
- 下载
需要使用到图标 因此也需要安装react-native-vector-icons
yarn add react-native-elements react-native-vector-icons
// 引用
react-native link react-native-vector-icons
- 引入和使用
import { Icon } from 'react-native-elements'
<Icon name='rowing' />
2.1.5 input输入框的使用
<Input
placeholder='请输入手机号码'
// 最大长度
maxLength={11}
// 输入框键盘的类型
keyboardType='phone-pad'
// 输入框绑定的值
value={phoneNumber}
// 输入框内部样式
inputStyle={{color:'#333'}}
// 文本改变事件
onChangeText={phoneNumberChangeText}
// 错误提示
errorMessage={"手机号码格式不正确"}
// 输入点击完成事件
onSubmitEditing={phoneNumberSubmitEditing}
// 左侧的icon图标
leftIcon={{ type: 'font-awesome', name: 'phone',color:'#ccc',size: pxToDp(20)}}
/>
2.1.6 发送axios请求
1.编写request.js文件,配置axios
import axios from 'axios';
import {BASE_URI} from './pathMap';
import Toast from '../utils/Toast';
// 创建axios请求
const instance = axios.create({
baseURL:BASE_URI
})
// 添加请求拦截器
instance.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
Toast.showLoading("请求中");
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
// 添加响应拦截器
instance.interceptors.response.use(function (response) {
// 对响应数据做点什么
Toast.hideLoading();
return response;
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error);
});
export default {
get:instance.get,
post:instance.post
}
2.创建pathMap.js,用来存放接口地址与参数
/**
* 接口基地址
*/
export const BASE_URI = "http://157.122.54.189:9089";
/**
* 登录 获取验证码
*/
export const ACCOUNT_LOGIN = '/user/login';// 登录
3.手机输入完成触发发送验证事件
const phoneNumberSubmitEditing = async () => {
// 1.对手机号码合法性校验,正则
if (!validatePhone(phoneNumber)) {
// 没有通过,设置号码不合法并且提示
setPhoneValid(false);
} else {
setPhoneValid(true);
}
/*
* 2.将手机号码发送到后台的接口,获取验证码 axios
* 1.发送异步请求的时候 自动显示等待框
* 2.请求回来 等待框 自动隐藏
* 3.关键 等待框、axios的拦截器
* */
const res = await request.post(ACCOUNT_LOGIN, {
phone: phoneNumber,
});
console.log(res);
// 3,将登录页面切换成 填写验证码的页面
if (res.code == '10000') {
// 请求成功
setShowLogin(false);
}
console.log(phoneNumber);
};
4.在index.js中添加配置接口调试工具
GLOBAL.XMLHttpRequest = GLOBAL.originalXMLHttpRequest || GLOBAL.XMLHttpRequest
2.1.7 创建渐变色的点击按钮
1.引入react-native-linear-gradient依赖
npm install react-native-linear-gradient --save
或者
yarn add react-native-linear-gradient
2.编写带渐变色点击按钮
import React from 'react';
import {View, Text, StyleSheet,TouchableOpacity} from 'react-native';
import LinearGradient from 'react-native-linear-gradient';
import {pxToDp} from '../../utils/stylesKits';
const THButton = (props) => {
const {
style={},
textStyle={},
children='sign'
} = props
return (
<TouchableOpacity onPress={props.onPress} style={{...styles.touchableOpacity,...style}}>
<LinearGradient start={{x:0,y:0}} end={{x:1,y:0}} colors={['#9b63cd', '#DD6989']} style={styles.linearGradient}>
<Text style={{...styles.buttonText,...textStyle}}>
{children}
</Text>
</LinearGradient>
</TouchableOpacity>
);
};
const styles = StyleSheet.create({
touchableOpacity: {
width: '100%',
height: '100%',
overflow:'hidden'
},
linearGradient: {
flex: 1,
paddingLeft: pxToDp(15),
paddingRight: pxToDp(15),
borderRadius: pxToDp(5),
justifyContent:"center",
alignItems:"center"
},
buttonText: {
fontSize: pxToDp(18),
fontFamily: 'Gill Sans',
textAlign: 'center',
color: '#ffffff',
backgroundColor: 'transparent',
},
});
export default THButton;
2.1.8 创建loading效果
1.引入teaset依赖
yarn add teaset
2.创建Toast.js文件
import React from 'react';
import {ActivityIndicator} from 'react-native';
import {Toast, Theme} from 'teaset';
let customKey = null;
Toast.showLoading = (text) => {
if (customKey) return;
customKey = Toast.show({
text: text,
icon: (
<ActivityIndicator size="large" color={Theme.toastIconTintColor} />
),
position: 'center',
duration: 60 * 1000,
});
};
Toast.hideLoading = () => {
if (!customKey) return;
Toast.hide(customKey);
customKey = null;
};
export default Toast;
3.在axios接口拦截器中使用
// 添加请求拦截器
instance.interceptors.request.use(function (config) {
// 显示loading
Toast.showLoading("请求中");
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
// 添加响应拦截器
instance.interceptors.response.use(function (response) {
// 隐藏loading
Toast.hideLoading();
return response.data;
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error);
});
2.1.9 根据showLogin是否显示填写验证码界面
{showLogin ? renderLogin() : renderVcode()}
2.1.10 创建验证码输入框
1.引入react-native-confirmation-code-field依赖
yarn add react-native-confirmation-code-field
2.编写验证码输入框
import React, {useState} from 'react';
import {SafeAreaView, Text, StyleSheet} from 'react-native';
import {
CodeField,
Cursor,
useBlurOnFulfill,
useClearByFocusCell,
} from 'react-native-confirmation-code-field';
const styles = StyleSheet.create({
root: {flex: 1, padding: 20},
title: {textAlign: 'center', fontSize: 30},
codeFieldRoot: {marginTop: 20},
cell: {
width: 40,
height: 40,
lineHeight: 38,
fontSize: 24,
borderBottomWidth: 2,
borderColor: '#00000030',
textAlign: 'center',
color:'#7d53ea'
},
focusCell: {
borderColor: '#7d53ea',
},
});
const CELL_COUNT = 6;
const THCode = () => {
const [value, setValue] = useState('');
const ref = useBlurOnFulfill({value, cellCount: CELL_COUNT});
const [props, getCellOnLayoutHandler] = useClearByFocusCell({
value,
setValue,
});
return (
<SafeAreaView style={styles.root}>
<CodeField
ref={ref}
{...props}
value={value}
onChangeText={setValue}
cellCount={CELL_COUNT}
rootStyle={styles.codeFieldRoot}
keyboardType="number-pad"
textContentType="oneTimeCode"
renderCell={({index, symbol, isFocused}) => (
<Text
key={index}
style={[styles.cell, isFocused && styles.focusCell]}
onLayout={getCellOnLayoutHandler(index)}>
{symbol || (isFocused ? <Cursor /> : null)}
</Text>
)}
/>
</SafeAreaView>
);
};
export default THCode;
2.1.11 验证码倒计时
const [btnText, setBtnText] = React.useState("重获验证码"); // 获取验证码按钮的文本
const [isCountDowning, setIsCountDowning] = React.useState(false); // 是否在倒计时中
/**
* 付民康 2021/3/12
* desc: 开启获取验证码的定时器
* @params
**/
const countDown = () => {
// 判断是否在倒计时中
if(isCountDowning) return
// 进入倒计时
setIsCountDowning(true);
// 倒计时长
let seconds = 5;
// 重新获取(10s)
setBtnText(`重新获取(${seconds}s)`)
// 创建定时器
let timeId = setInterval(()=>{
seconds--;
setBtnText((pre)=>{
return `重新获取(${seconds}s)`
})
if (seconds===0) {
// 清楚定时器
clearInterval(timeId);
setBtnText((pre)=>{
return `重新获取`
});
// 离开倒计时
setIsCountDowning(false);
}
},1000)
};
2.1.12 发送验证码验证请求
发送验证码验证请求,根据接口返回,跳转到不同的页面
const onVcodeSubmitEditing = async () => {
/*
* 1.对验证码做校验——长度检验
* 2.将手机号和验证码一起发送到后台
* 3.返回值渲染不同页面
* 4.新用户->完善个人信息
* 5.老用户->交友-首页
* */
// 1.对验证码做校验——长度检验
if (vcodeText.length!=6) {
Toast.message("验证码不正确",2000,"center");
return;
}
// 2.将手机号和验证码一起发送到后台
const res = await request.post(ACCOUNT_VALIDATEVCODE,{
phone:phoneNumber,
vcode:vcodeText
})
if (res.code!="10000") {
Toast.message("验证码不正确",2000,"center");
console.log(res);
return;
}
if(res.data.isNew) {
// 新用户
alert("新用户 跳转到信息页面")
props.navigation.navigate("Userinfo");
} else {
// 老用户
alert("老用户 跳转到交友页面")
}
};
2.2 完善个人信息页面
2.2.1 使用阿里巴巴字体svg
1.安装依赖
yarn add react-native-svg react-native-svg-uri
2.创建iconSvg.js文件
export const male =
'<svg id="iconman-sel" viewBox="0 0 1024 1024"><path d="M299.148894 861.971499s153.977396-61.389681 152.971008-76.988698c-1.509582-27.675676-4.025553-66.421622-4.025553-66.421622 0-30.69484 24.656511-55.351351 55.351351-55.351351 30.191646 0 55.351351 24.656511 55.351352 55.351351v50.319411c0 14.592629 137.875184 159.009337 137.875184 159.009336" fill="#FDE8CF"></path><path d="M366.576904 810.142506s35.223587-0.503194 55.351352 0c23.650123 0.503194 51.325799 94.097297 85.039803 90.574939 41.261916-4.025553 55.351351-100.638821 81.014251-100.638821h60.383292c60.886486 0 98.12285 12.076658 98.12285 72.963145V1016.452088H265.938084v-143.410319c0-60.383292 39.752334-60.383292 100.63882-62.899263z" fill="#14AA82"></path><path d="M512 167.563636c150.958231 0 273.234398 122.276167 273.234398 273.234398v26.669288c0 150.958231-122.276167 273.234398-273.234398 273.234398s-273.234398-122.276167-273.234398-273.234398v-26.669288c0-150.958231 122.276167-273.234398 273.234398-273.234398z" fill="#FDE8CF"></path><path d="M834.54742 340.662408C834.54742 190.207371 696.169042 27.675676 564.332187 10.063882H462.183784C311.225553 10.063882 194.484521 168.06683 194.484521 318.521867c0 0 3.019165 34.720393-0.503194 21.134153C213.102703 417.651106 199.516462 394.000983 255.371007 374.879607c44.784275-15.095823 117.74742 31.701229 182.156266 26.166093 44.281081-3.522359 120.766585-35.726781 160.015725-54.848157 33.210811 22.643735 94.600491 61.892875 126.804914 60.886486 72.963145-3.019165 110.199509-66.421622 110.199508-66.421621z" fill="#2B435B"></path><path d="M236.752826 518.79312c-38.242752 0-68.937592-27.172482-68.937593-60.383292S198.510074 397.523342 236.752826 397.523342M789.763145 398.026536c36.73317 0 66.421622 27.172482 66.421622 60.886486s-56.35774 60.383292-66.421622 60.383292" fill="#FDE8CF"></path><path d="M404.316462 425.199017c18.114988 0 33.210811 15.095823 33.210811 33.210811s-15.095823 33.210811-33.210811 33.210811c-18.114988 0-33.210811-14.592629-33.210811-33.210811 0-18.114988 14.592629-33.210811 33.210811-33.210811z" fill="#012428"></path><path d="M603.078133 458.409828m-33.210811 0a33.210811 33.210811 0 1 0 66.421621 0 33.210811 33.210811 0 1 0-66.421621 0Z" fill="#012428"></path><path d="M600.058968 466.460934m-24.656511 0a24.656511 24.656511 0 1 0 49.313022 0 24.656511 24.656511 0 1 0-49.313022 0Z" fill="#012428"></path><path d="M365.570516 524.328256c21.134152 0 38.745946 8.5543 38.745946 19.121375s-17.1086 19.121376-38.745946 19.121376-38.745946-8.5543-38.745946-19.121376 17.1086-19.121376 38.745946-19.121375zM657.92629 524.328256c21.134152 0 38.745946 8.5543 38.745946 19.121375s-17.1086 19.121376-38.745946 19.121376-38.745946-8.5543-38.745946-19.121376 17.611794-19.121376 38.745946-19.121375z" fill="#FA6E6E"></path><path d="M580.937592 579.679607c-0.503194 39.752334-32.707617 71.453563-72.459951 70.950368-39.24914-0.503194-70.950369-32.204423-70.950368-70.950368" fill="#EB4545"></path></svg>';
export const female =
'<svg id="iconnv" viewBox="0 0 1024 1024"><path d="M836.560197 696.420639V340.662408C836.560197 190.207371 698.181818 27.675676 566.344963 10.063882H464.19656C313.238329 10.063882 196.497297 168.06683 196.497297 318.521867l-5.535135 377.395578c0 150.455037-33.210811 152.467813-38.745946 162.531695s176.62113 38.745946 176.62113 38.745946H439.036855l143.410319-16.605405s238.010811-19.62457 237.507617-38.745946c0-6.038329 82.523833-42.771499 38.745946-38.745946-24.656511 3.019165-21.637346-15.599017-22.14054-106.67715z" fill="#EB4545"></path><path d="M301.161671 861.971499S455.139066 800.581818 454.132678 784.982801c-1.509582-27.675676-4.025553-66.421622-4.025553-66.421622 0-30.69484 24.656511-55.351351 55.351352-55.351351s55.351351 24.656511 55.351351 55.351351v50.319411c0 14.592629 137.875184 159.009337 137.875184 159.009336" fill="#FDE8CF"></path><path d="M378.653563 805.110565c15.095823 1.006388 29.688452 3.522359 44.281081 6.541523 23.146929 5.535135 52.332187 92.084521 86.046191 89.065357 41.261916-4.025553 58.87371-93.594103 85.039804-93.594103 18.114988-1.006388 36.229975-3.522359 53.841769-6.541524 62.899263 0.503194 98.626044 69.94398 98.12285 130.327273v80.511056l-478.034398-0.503194V915.813268c0-60.383292 49.816216-108.186732 110.702703-110.702703z" fill="#29BE96"></path><path d="M514.012776 167.563636c150.958231 0 273.234398 122.276167 273.234398 273.234398v26.669288c0 150.958231-122.276167 273.234398-273.234398 273.234398-150.958231 0-273.234398-122.276167-273.234398-273.234398v-26.669288c0-150.958231 122.276167-273.234398 273.234398-273.234398z" fill="#FDE8CF"></path><path d="M836.560197 340.662408C836.560197 190.207371 698.181818 27.675676 566.344963 10.063882H464.19656C313.238329 10.063882 196.497297 168.06683 196.497297 318.521867c0 0 3.019165 34.720393-0.503194 21.134153 19.121376 77.491892 5.535135 53.841769 60.886487 34.720393 44.784275-15.095823 117.74742 31.701229 182.156265 26.166093 44.281081-3.522359 120.766585-35.726781 160.015725-54.848157 33.210811 22.643735 94.600491 61.892875 126.804914 60.886486 73.466339-2.515971 110.702703-65.918428 110.702703-65.918427z" fill="#EB4545"></path><path d="M238.765602 518.79312c-38.242752 0-68.937592-27.172482-68.937592-60.383292S200.52285 397.523342 238.765602 397.523342M791.775921 398.026536c36.73317 0 66.421622 27.172482 66.421622 60.886486s-29.688452 60.383292-66.421622 60.383292" fill="#FDE8CF"></path><path d="M406.329238 425.199017c18.114988 0 33.210811 15.095823 33.210811 33.210811 0 18.114988-15.095823 33.210811-33.210811 33.210811-18.114988 0-33.210811-14.592629-33.21081-33.210811 0-18.114988 14.592629-33.210811 33.21081-33.210811z" fill="#012428"></path><path d="M605.090909 458.409828m-33.210811 0a33.210811 33.210811 0 1 0 66.421622 0 33.210811 33.210811 0 1 0-66.421622 0Z" fill="#012428"></path><path d="M602.071744 466.460934m-24.656511 0a24.656511 24.656511 0 1 0 49.313023 0 24.656511 24.656511 0 1 0-49.313023 0Z" fill="#012428"></path><path d="M367.583292 524.328256c21.134152 0 38.745946 8.5543 38.745946 19.121375s-17.1086 19.121376-38.745946 19.121376-38.745946-8.5543-38.745946-19.121376 17.1086-19.121376 38.745946-19.121375zM659.939066 524.328256c21.134152 0 38.745946 8.5543 38.745946 19.121375s-17.1086 19.121376-38.745946 19.121376-38.745946-8.5543-38.745946-19.121376 17.611794-19.121376 38.745946-19.121375z" fill="#FA6E6E"></path></svg>';
3.组件中引入svg
import {female, male} from '../../../res/fonts/iconSvg';
<SvgUri svgXmlData={male} width={'36'} height={'36'}/>
<SvgUri svgXmlData={female} width={'36'} height={'36'}/>
2.2.2 datepicker 日期选择器
1.引入日期选择器依赖
yarn add react-native-datepicker
2.引入datepicker组件
import DatePicker from 'react-native-datepicker';
const dateNow = new Date();
const currentDate = `${dateNow.getFullYear()}-${dateNow.getMonth() + 1}-${dateNow.getDate() + 1}`;
const [birthday, setBirthday] = React.useState('2019-12-26'); // 生日
<DatePicker
androidMode={'spinner'}
style={{width: '100%'}}
date={birthday}
mode="date"
placeholder="设置生日"
format="YYYY-MM-DD"
minDate="1900-01-01"
maxDate={currentDate}
confirmBtnText="确定"
cancelBtnText="取消"
customStyles={{
dateIcon: {
display: 'none',
},
dateInput: {
marginLeft: pxToDp(10),
borderWidth: 0,
borderBottomWidth: pxToDp(1.1),
alignItems: 'flex-start',
paddingLeft: pxToDp(2),
},
placeholderTextL: {
fontSize: pxToDp(18),
color: '#afafaf',
},
// ... You can check the source to find the other keys.
}}
onDateChange={(birthday) => {
setBirthday(birthday);
}}
/>
2.2.3 高德地图组件
高德地图组件
分别使用了两个功能,一个是AndroidSDK和一个web服务
- 申请 高度地图的key
- 下载依赖
yarn add react-native-amap-geolocation
- 配置文件
- 编辑
android/settings.gradle
,设置项目路径:
+ include ':react-native-amap-geolocation'
+ project(':react-native-amap-geolocation').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-amap-geolocation/lib/android')
- 编辑
android/app/build.gradle
,新增依赖:
dependencies {
+ implementation project(':react-native-amap-geolocation')
}
- 编辑
MainApplication.java
:
+ import cn.qiuxiang.react.geolocation.AMapGeolocationPackage;
public class MainApplication extends Application implements ReactApplication {
@Override
protected List<ReactPackage> getPackages() {
@SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();
// Packages that cannot be autolinked yet can be added manually here, for example:
+ packages.add(new AMapGeolocationPackage());
return packages;
}
}
- 代码
import { PermissionsAndroid, Platform } from "react-native";
import { init, Geolocation } from "react-native-amap-geolocation";
import axios from "axios";
class Geo {
async initGeo() {
if (Platform.OS === "android") {
await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.ACCESS_COARSE_LOCATION);
}
await init({
ios: "e8b092f4b23cef186bd1c4fdd975bf38",
android: "e8b092f4b23cef186bd1c4fdd975bf38"
});
return Promise.resolve();
}
async getCurrentPosition() {
return new Promise((resolve, reject) => {
console.log("开始定位");
Geolocation.getCurrentPosition(({ coords }) => {
resolve(coords);
}, reject);
})
}
async getCityByLocation() {
const { longitude, latitude } = await this.getCurrentPosition();
const res = await axios.get("https://restapi.amap.com/v3/geocode/regeo", {
params: { location: `${longitude},${latitude}`, key: "83e9dd6dfc3ad5925fc228c14eb3b4d6", }
});
return Promise.resolve(res.data);
}
}
export default new Geo();
- 启动报错
Native module AMapGeolocation tried to override AMapGeolocationModule
因为新版的react-native会自动在MainApplication.java中引入依赖,不需要我们手动天剑,去掉之前添加的代码就好了
- 获取本地位置,调用高德地图api
1.首先要在app.js中简历初始化高德地图定位
React.useEffect(() => {
init();
}, []);
// 初始化地理api
const init = async () => {
await Geo.initGeo();
setIsInitGeo(true);
};
2.在个人信息页面获取当前位置的api信息
React.useEffect(() => {
init();
}, []);
// 初始化调用高德地图api
const init = async () => {
const res = await Geo.getCityByLocation();
console.log(res);
if (res) {
setAddress(res.regeocode.formatted_address);
setCity(res.addressComponent.city.replace('市', ''));
}
};
2.2.4 创建城市选择组件
react-native-picker
自定义picker
- 安装
yarn add react-native-picker
- 代码
import Picker from 'react-native-picker';
Picker.init({
pickerData: CityJson,
selectedValue: ["北京", "北京"],
wheelFlex: [1, 1, 0], // 显示省和市
pickerConfirmBtnText: "确定",
pickerCancelBtnText: "取消",
pickerTitleText: "选择城市",
onPickerConfirm: data => {
// data = [广东,广州,天河]
this.setState(
{
city: data[1]
}
);
}
});
Picker.show();
2.2.5 设置头像组件
使用 react-native-image-crop-picker
图片裁切组件
- 安装
yarn add react-native-image-crop-picker
- 使用
import ImagePicker from 'react-native-image-crop-picker';
ImagePicker.openPicker({
width: 300,
height: 400,
cropping: true
}).then(image => {
console.log(image);
});
- 注意react-native-image-crop-picker必须安装0.33版本
need to downgrade to 0.33.
OR
Upgrade your gradle version classpath 'com.android.tools.build:gradle:4.0.1' or classpath('com.android.tools.build:gradle:4.0.1')
2.2.6 实现头像审核中的效果
使用Overlay图层
import {Overlay} from 'teaset';
let overlayView = (
<Overlay.View
style={{flex: 1, backgroundColor: '#000'}}
modal={true}
overlayOpacity={0}
ref={v => this.overlayView = v}
>
<View>
{内容}
</View>
</Overlay.View>
);
Overlay.show(overlayView);
2.2.7 在 Android 上支持 GIF 和 WebP 格式图片
默认情况下 Android 是不支持 GIF 和 WebP 格式的。你需要在android/app/build.gradle
文件中根据需要手动添加以下模块:
dependencies {
// 如果你需要支持Android4.0(API level 14)之前的版本
implementation 'com.facebook.fresco:animated-base-support:1.3.0'
// 如果你需要支持GIF动图
// implementation 'com.facebook.fresco:fresco:2.0.0'
// implementation 'com.facebook.fresco:animated-gif:2.0.0'
implementation 'com.facebook.fresco:fresco:1.12.0'
implementation 'com.facebook.fresco:animated-gif:1.12.0'
// 如果你需要支持WebP格式,包括WebP动图
implementation 'com.facebook.fresco:animated-webp:2.1.0'
implementation 'com.facebook.fresco:webpsupport:2.0.0'
// 如果只需要支持WebP格式而不需要动图
implementation 'com.facebook.fresco:webpsupport:2.0.0'
}
2.2.8 RN引入本地图片和外网图片方式
<Image style={{width: '100%', height: '100%', position: 'absolute', left: 0, top: 0, zIndex: 100}}
source={require('../../../res/scan.gif')}/>
<Image style={{width: '60%', height: '60%'}}
source={{uri: image.path}}/>
2.2.9 使用mobx存储全局数据(类似redux)
- 安装依赖
-
mobx
核心库 -
mobx-react
方便在react中使用mobx技术的库 -
@babel/plugin-proposal-decorators
让rn
项目支持es7
的装饰器语法的库
yarn add mobx mobx-react @babel/plugin-proposal-decorators
- 在
babel.config.js
添加以下配置
plugins: [
['@babel/plugin-proposal-decorators', { 'legacy': true }]
]
- 新建文件
mobx\index.js
用来存放 全局数据
import { observable, action } from "mobx";
class RootStore {
// observable 表示数据可监控 表示是全局数据
@observable name = "hello";
// action行为 表示 changeName是个可以修改全局共享数据的方法
@action changeName(name) {
this.name = name;
}
}
export default new RootStore();
- 在根组件中挂载
通过
Provider
来挂载和传递
import React, { Component } from 'react';
import { View} from 'react-native';
import rootStore from "./mobx";
import { Provider} from "mobx-react";
class Index extends Component {
// 正常
render() {
return (
<View >
<Provider rootStore={rootStore} >
<Sub1></Sub1>
</Provider>
</View>
);
}
}
- 其他组件中使用
import React, { Component } from 'react';
import { View, Text } from 'react-native';
import {inject,observer } from "mobx-react";
@inject("rootStore") // 注入 用来获取 全局数据的
@observer // 当全局发生改变了 组件的重新渲染 从而显示最新的数据
class Sub1 extends Component {
changeName = () => {
// 修改全局数据
this.props.rootStore.changeName(Date.now());
}
render() {
console.log(this);
return (
<View><Text onPress={this.changeName}>{this.props.rootStore.name}</Text></View>
);
}
}
export default Index;
2.2.10 封装 request 实现自动携带 token
在request.js中添加
import RootStore from './../mobx/index';
// post 自动带上token
privatePost: (url, data = {}, options = {}) => {
const token = RootStore.token;
const headers = options.headers || {};
return instance.post(url, data, {
...options,
headers: {
'Authorization': `Bearer ${token}`,
...headers,
},
});
},
2.2.11 注册 极光用户(实现实时聊天)
jmessage-react-plugin
极光推送 react-native 版本
2.2.11.1 开通服务
- 首先 需要我们自己先在极光上注册账号 开通服务,拿到对应的密钥
2.2.11.2 简单使用
- 安装依赖
yarn add jmessage-react-plugin jcore-react-native
- 配置
android\app\src\main\AndroidManifest.xml
加入以下代码
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
<!-- 极光的配置 -->
<meta-data android:name="JPUSH_CHANNEL" android:value="${APP_CHANNEL}" />
<meta-data android:name="JPUSH_APPKEY" android:value="${JPUSH_APPKEY}" />
<!-- 极光的配置 -->
</application>
android\app\build.gradle
加入以下代码和按需修改
android {
compileSdkVersion rootProject.ext.compileSdkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
defaultConfig {
applicationId "com.awesomeproject22"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "1.0"
multiDexEnabled true // 新增的
manifestPlaceholders = [
JPUSH_APPKEY: "c0c08d3d8babc318fe25bb0c", //在此替换你的APPKey
APP_CHANNEL: "developer-default" //应用渠道号
]
}
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "com.facebook.react:react-native:+" // From node_modules
compile project(':jmessage-react-plugin') // 新增的
compile project(':jcore-react-native') // 新增的
if (enableHermes) {
def hermesPath = "../../node_modules/hermes-engine/android/";
debugImplementation files(hermesPath + "hermes-debug.aar")
releaseImplementation files(hermesPath + "hermes-release.aar")
} else {
implementation jscFlavor
}
}
- 根目录下新建文件和添加以下配置
react-native.config.js
module.exports = {
dependencies: {
'jmessage-react-plugin': {
platforms: {
android: {
packageInstance: 'new JMessageReactPackage(false)'
}
}
},
}
};
android\settings.gradle
加入如下配置
include ':jmessage-react-plugin'
project(':jmessage-react-plugin').projectDir = new File(rootProject.projectDir, '../node_modules/jmessage-react-plugin/android')
include ':jcore-react-native'
project(':jcore-react-native').projectDir = new File(rootProject.projectDir, '../node_modules/jcore-react-native/android')
- 在 根组件中进行测试
App.js
import React from 'react';
import { View, Text } from "react-native";
import JMessage from "jmessage-react-plugin";
class App extends React.Component {
componentDidMount() {
JMessage.init({
'appkey': 'c0c08d3d8babc318fe25bb0c',
'isOpenMessageRoaming': true,
'isProduction': false,
'channel': ''
})
JMessage.login({
username: "18665711956",
password: "18665711956"
}, (res) => {
console.log("登录成功");
console.log(res);
}, (err) => {
console.log("登录失败");
console.log(err);
})
}
render() {
return (
<View>
<Text>goods</Text>
</View>
);
}
}
export default App;
2.3 实现tabbar结构
2.3.1 实现步骤
- 分析
- tabbar结构上,存放着四个页面(模块),分别是 交友,圈子,消息,我的
- 用到的组件
- react-native-tab-navigator
- react-native-svg-uri
2.3.2 react-native-tab-navigator
底部导航栏
- 下载
yarn add react-native-tab-navigator
- 代码
要有 SvgUri 和 Friend 等其他依赖
import React, { Component } from 'react';
import { View, Text,StyleSheet } from 'react-native';
import SvgUri from 'react-native-svg-uri';
import Friend from "./pages/friend";
import Group from "./pages/group";
import Message from "./pages/message";
import My from "./pages/my";
import TabNavigator from 'react-native-tab-navigator';
import SvgData from "./res/svg";
const dataSource = [
{
icon: SvgData.friend,
selectedIcon: SvgData.selectdFriend,
tabPage: 'Friend',
tabName: '交友',
badge: 0,
component: Friend
},
{
icon: SvgData.group,
selectedIcon: SvgData.selectdGroup,
tabPage: 'Group',
tabName: '圈子',
badge: 0,
component: Group
},
{
icon: SvgData.message,
selectedIcon:SvgData.selectdMessage,
tabPage: 'Message',
tabName: '消息',
badge: 5,
component: Message
},
{
icon: SvgData.my,
selectedIcon: SvgData.selectdMy,
tabPage: 'My',
tabName: '我的',
badge: 0,
component: My
}
];
class Index extends Component {
state = {
selectedTab: "Friend"
}
render() {
return (
<View style={{ flex: 1, backgroundColor: '#F5FCFF' }}>
<TabNavigator >
{dataSource.map((v, i) => {
return (
<TabNavigator.Item
key={i}
selected={this.state.selectedTab === v.tabPage}
title={v.tabName}
tabStyle={stylesheet.tab}
titleStyle={{ color: '#999999' }}
selectedTitleStyle={{ color: '#c863b5' }}
renderIcon={() => <SvgUri width="23" height="23" svgXmlData={v.icon} />}
renderSelectedIcon={() => <SvgUri width="23" height="23" svgXmlData={v.selectedIcon} />}
badgeText={v.badge}
onPress={() => this.setState({ selectedTab: v.tabPage })}>
<v.component />
</TabNavigator.Item>
)
})}
</TabNavigator>
</View>
)
}
}
const stylesheet = StyleSheet.create({
tab: {
justifyContent: "center"
},
tabIcon: {
color: "#999",
width: 23,
height: 23
}
})
export default Index;
2.3.3 将token存储到本地缓存
解决每一次打开app都需要重新登录的问题。
- 首先在登录操作中,加入存储
// 存储用户数据到本地缓存中,永久存储
AsyncStorage.setItem('userinfo',JSON.stringify(
{
mobile:phoneNumber,
token:res.data.token,
userId:res.data.id
}
)) //必须将对象转换成字符串格式,不然就会数据丢失
- 在app.js中获取本地缓存数据,并存入到mobx中
// 获取缓存中的用户数据
const getUserInfo = async () => {
const strUserInfo = await AsyncStorage.getItem("userinfo");
const userinfo = strUserInfo?JSON.parse(strUserInfo):{};
// 判断 有没有token
if (userinfo.token) {
// 把缓存中的数据存到mobx中
RootStore.setUserInfo(userinfo.mobile,userinfo.token,userinfo.userId);
}
}
2.4 交友页面
2.4.1 实现顶部图片吸顶效果
需要用到插件 react-native-image-header-scroll-view
顶部吸顶效果
- 下载
yarn add react-native-image-header-scroll-view
注意:使用1.0.0+版本时出现报错,改为0.10.3正常显示
- 组件中引用
import HeaderImageScrollView from 'react-native-image-header-scroll-view';
<HeaderImageScrollView
maxHeight={pxToDp(130)}
minHeight={pxToDp(44)}
headerImage={require('../../res/headfriend.png')}
renderForeground={() => (
<View style={{height: pxToDp(130), justifyContent: 'center', alignItems: 'center'}}>
<StatusBar backgroundColor={'transparent'} translucent={true} />
</View>
)}
>
<View style={{height: 1000}}>
<--主要内容-->
</View>
</HeaderImageScrollView>
2.4.2 访客模块
2.4.3 今日佳人模块
2.4.4 rn中使用普通的iconfont字体
2.4.5 筛选功能
2.4.6 推荐列表