文章目录

  • 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库 内置常用组件

  1. 下载
    需要使用到图标 因此也需要安装 react-native-vector-icons
yarn add react-native-elements react-native-vector-icons
  // 引用
 react-native link react-native-vector-icons
  1. 引入和使用
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服务

  1. 申请 高度地图的key
  2. 下载依赖
yarn add  react-native-amap-geolocation
  1. 配置文件
  1. 编辑 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')
  1. 编辑 android/app/build.gradle,新增依赖:
dependencies {
+   implementation project(':react-native-amap-geolocation')
}
  1. 编辑 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;
        }
}
  1. 代码
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();
  1. 启动报错Native module AMapGeolocation tried to override AMapGeolocationModule

因为新版的react-native会自动在MainApplication.java中引入依赖,不需要我们手动天剑,去掉之前添加的代码就好了

  1. 获取本地位置,调用高德地图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

  1. 安装
yarn add react-native-picker
  1. 代码
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

图片裁切组件

  1. 安装
yarn add  react-native-image-crop-picker
  1. 使用
import ImagePicker from 'react-native-image-crop-picker';

    ImagePicker.openPicker({
      width: 300,
      height: 400,
      cropping: true
    }).then(image => {
      console.log(image);
    });
  1. 注意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)

  1. 安装依赖
  • mobx 核心库
  • mobx-react 方便在react中使用mobx技术的库
  • @babel/plugin-proposal-decoratorsrn 项目支持 es7 的装饰器语法的库
yarn add mobx mobx-react @babel/plugin-proposal-decorators
  1. babel.config.js添加以下配置
plugins: [
    ['@babel/plugin-proposal-decorators', { 'legacy': true }]
  ]
  1. 新建文件 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();
  1. 在根组件中挂载

通过 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>
    );
  }
}
  1. 其他组件中使用
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 开通服务
  1. 首先 需要我们自己先在极光上注册账号 开通服务,拿到对应的密钥
2.2.11.2 简单使用
  1. 安装依赖
yarn add jmessage-react-plugin jcore-react-native
  1. 配置
  1. 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>
  1. 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
    }
}
  1. 根目录下新建文件和添加以下配置 react-native.config.js
module.exports = {
  dependencies: {
    'jmessage-react-plugin': {
      platforms: {
        android: {
          packageInstance: 'new JMessageReactPackage(false)'
        }
      }
    },
  }
};
  1. 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')
  1. 在 根组件中进行测试 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 实现步骤

  1. 分析
  1. tabbar结构上,存放着四个页面(模块),分别是 交友,圈子,消息,我的
  1. 用到的组件
  1. react-native-tab-navigator
  2. react-native-svg-uri

2.3.2 react-native-tab-navigator

底部导航栏

  1. 下载
yarn add react-native-tab-navigator
  1. 代码
    要有 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都需要重新登录的问题。

  1. 首先在登录操作中,加入存储
// 存储用户数据到本地缓存中,永久存储
        AsyncStorage.setItem('userinfo',JSON.stringify(
            {
                mobile:phoneNumber,
                token:res.data.token,
                userId:res.data.id
            }
        )) //必须将对象转换成字符串格式,不然就会数据丢失
  1. 在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

顶部吸顶效果

  1. 下载
yarn add react-native-image-header-scroll-view

注意:使用1.0.0+版本时出现报错,改为0.10.3正常显示

  1. 组件中引用
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 推荐列表