react native 仿微信聊天室实例代码


Posted in Javascript onSeptember 17, 2019

一、前言

9月,又到开学的季节。为每个一直默默努力的自己点赞!最近都沉浸在react native原生app开发中,之前也有使用vue/react/angular等技术开发过聊天室项目,另外还使用RN技术做了个自定义模态弹窗rnPop组件。

一、项目简述

基于react+react-native+react-navigation+react-redux+react-native-swiper+rnPop等技术开发的仿微信原生App界面聊天室——RN_ChatRoom,实现了原生app启动页、AsyncStorage本地存储登录拦截、集成rnPop模态框功能(仿微信popupWindow弹窗菜单)、消息触摸列表、发送消息、表情(动图),图片预览,拍摄图片、发红包、仿微信朋友圈等功能。

二、技术点

  • MVVM框架:react / react-native / react-native-cli
  • 状态管理:react-redux / redux
  • 页面导航:react-navigation
  • rn弹窗组件:rnPop
  • 打包工具:webpack 2.0
  • 轮播组件:react-native-swiper
  • 图片/相册:react-native-image-picker
{
 "name": "RN_ChatRoom",
 "version": "0.0.1",
 "aboutMe": "QQ:282310962 、 wx:xy190310",
 "dependencies": {
  "react": "16.8.6",
  "react-native": "0.60.4"
 },
 "devDependencies": {
  "@babel/core": "^7.5.5",
  "@babel/runtime": "^7.5.5",
  "@react-native-community/async-storage": "^1.6.1",
  "@react-native-community/eslint-config": "^0.0.5",
  "babel-jest": "^24.8.0",
  "eslint": "^6.1.0",
  "jest": "^24.8.0",
  "metro-react-native-babel-preset": "^0.55.0",
  "react-native-gesture-handler": "^1.3.0",
  "react-native-image-picker": "^1.0.2",
  "react-native-swiper": "^1.5.14",
  "react-navigation": "^3.11.1",
  "react-redux": "^7.1.0",
  "react-test-renderer": "16.8.6",
  "redux": "^4.0.4",
  "redux-thunk": "^2.3.0"
 },
 "jest": {
  "preset": "react-native"
 }
}

react native 仿微信聊天室实例代码

react native 仿微信聊天室实例代码

react native 仿微信聊天室实例代码

react native 仿微信聊天室实例代码

react native 仿微信聊天室实例代码

react native 仿微信聊天室实例代码

react native 仿微信聊天室实例代码

react native 仿微信聊天室实例代码

react native 仿微信聊天室实例代码

react native 仿微信聊天室实例代码

react native 仿微信聊天室实例代码

react native 仿微信聊天室实例代码

react native 仿微信聊天室实例代码

react native 仿微信聊天室实例代码

react native 仿微信聊天室实例代码

◆ App全屏幕启动页splash模板

react-native如何全屏启动? 设置StatusBar顶部条背景为透明 translucent={true},并配合RN动画Animated

/**
 * @desc 启动页面
 */

import React, { Component } from 'react'
import { StatusBar, Animated, View, Text, Image } from 'react-native'

export default class Splash extends Component{
  constructor(props){
    super(props)
    this.state = {
      animFadeIn: new Animated.Value(0),
      animFadeOut: new Animated.Value(1),
    }
  }

  render(){
    return (
      <Animated.View style={[GStyle.flex1DC_a_j, {backgroundColor: '#1a4065', opacity: this.state.animFadeOut}]}>
        <StatusBar backgroundColor='transparent' barStyle='light-content' translucent={true} />

        <View style={GStyle.flex1_a_j}>
          <Image source={require('../assets/img/ic_default.jpg')} style={{borderRadius: 100, width: 100, height: 100}} />
        </View>
        <View style={[GStyle.align_c, {paddingVertical: 20}]}>
          <Text style={{color: '#dbdbdb', fontSize: 12, textAlign: 'center',}}>RN-ChatRoom v1.0.0</Text>
        </View>
      </Animated.View>
    )
  }

  componentDidMount(){
    // 判断是否登录
    storage.get('hasLogin', (err, object) => {
      setTimeout(() => {
        Animated.timing(
          this.state.animFadeOut, {duration: 300, toValue: 0}
        ).start(()=>{
          // 跳转页面
          util.navigationReset(this.props.navigation, (!err && object && object.hasLogin) ? 'Index' : 'Login')
        })
      }, 1500);
    })
  }
}

◆ RN本地存储技术async-storage

/**
 * @desc 本地存储函数
 */
import AsyncStorage from '@react-native-community/async-storage'
export default class Storage{
  static get(key, callback){
    return AsyncStorage.getItem(key, (err, object) => {
      callback(err, JSON.parse(object))
    })
  }
  static set(key, data, callback){
    return AsyncStorage.setItem(key, JSON.stringify(data), callback)
  }
  static del(key){
    return AsyncStorage.removeItem(key)
  }
  static clear(){
    AsyncStorage.clear()
  }
}
global.storage = Storage

声明全局global变量,只需在App.js页面一次引入、多个页面均可调用。

storage.set('hasLogin', { hasLogin: true })
storage.get('hasLogin', (err, object) => { ... })

◆ App主页面模板及全局引入组件

import React, { Fragment, Component } from 'react'
import { StatusBar } from 'react-native'
// 引入公共js
import './src/utils/util'
import './src/utils/storage'
// 导入样式
import './src/assets/css/common'
// 导入rnPop弹窗
import './src/assets/js/rnPop/rnPop.js'
// 引入页面路由
import PageRouter from './src/router'
class App extends Component{
 render(){
  return (
   <Fragment>
    {/* <StatusBar backgroundColor={GStyle.headerBackgroundColor} barStyle='light-content' /> */}
    {/* 页面 */}
    <PageRouter />
    {/* 弹窗模板 */}
    <RNPop />
   </Fragment>
  )
 }
}

export default App

◆ react-navigation页面导航器/地址路由、底部tabbar

由于react-navigation官方顶部导航器不能满足需求,如是自己封装了一个,功能效果有些类似微信导航。

react native 仿微信聊天室实例代码

export default class HeaderBar extends Component {
  constructor(props){
    super(props)
    this.state = {
      searchInput: ''
    }
  }
  render() {
    /**
     * 更新
     * @param { navigation | 页面导航 }
     * @param { title | 标题 }
     * @param { center | 标题是否居中 }
     * @param { search | 是否显示搜索 }
     * @param { headerRight | 右侧Icon按钮 }
     */
    let{ navigation, title, bg, center, search, headerRight } = this.props
    return (
      <View style={GStyle.flex_col}>
        <StatusBar backgroundColor={bg ? bg : GStyle.headerBackgroundColor} barStyle='light-content' translucent={true} />
        <View style={[styles.rnim__topBar, GStyle.flex_row, {backgroundColor: bg ? bg : GStyle.headerBackgroundColor}]}>
          {/* 返回 */}
          <TouchableOpacity style={[styles.iconBack]} activeOpacity={.5} onPress={this.goBack}><Text style={[GStyle.iconfont, GStyle.c_fff, GStyle.fs_18]}></Text></TouchableOpacity>
          {/* 标题 */}
          { !search && center ? <View style={GStyle.flex1} /> : null }
          {
            search ? 
            (
              <View style={[styles.barSearch, GStyle.flex1, GStyle.flex_row]}>
                <TextInput onChangeText={text=>{this.setState({searchInput: text})}} style={styles.barSearchText} placeholder='搜索' placeholderTextColor='rgba(255,255,255,.6)' />
              </View>
            )
            :
            (
              <View style={[styles.barTit, GStyle.flex1, GStyle.flex_row, center ? styles.barTitCenter : null]}>
                { title ? <Text style={[styles.barCell, {fontSize: 16, paddingLeft: 0}]}>{title}</Text> : null }
              </View>
            )
          }
          {/* 右侧 */}
          <View style={[styles.barBtn, GStyle.flex_row]}>
            { 
              !headerRight ? null : headerRight.map((item, index) => {
                return(
                  <TouchableOpacity style={[styles.iconItem]} activeOpacity={.5} key={index} onPress={()=>item.press ? item.press(this.state.searchInput) : null}>
                    {
                      item.type === 'iconfont' ? item.title : (
                        typeof item.title === 'string' ? 
                        <Text style={item.style ? item.style : null}>{`${item.title}`}</Text>
                        :
                        <Image source={item.title} style={{width: 24, height: 24, resizeMode: 'contain'}} />
                      )
                    }
                    {/* 圆点 */}
                    { item.badge ? <View style={[styles.iconBadge, GStyle.badge]}><Text style={GStyle.badge_text}>{item.badge}</Text></View> : null }
                    { item.badgeDot ? <View style={[styles.iconBadgeDot, GStyle.badge_dot]}></View> : null }
                  </TouchableOpacity>
                )
              })
            }
          </View>
        </View>
      </View>
    )
  }
  goBack = () => {
    this.props.navigation.goBack()
  }
}
// 创建底部TabBar
const tabNavigator = createBottomTabNavigator(
  // tabbar路由(消息、通讯录、我)
  {
    Index: {
      screen: Index,
      navigationOptions: ({navigation}) => ({
        tabBarLabel: '消息',
        tabBarIcon: ({focused, tintColor}) => (
          <View>
            <Text style={[ GStyle.iconfont, GStyle.fs_20, {color: (focused ? tintColor : '#999')} ]}></Text>
            <View style={[GStyle.badge, {position: 'absolute', top: -2, right: -15,}]}><Text style={GStyle.badge_text}>12</Text></View>
          </View>
        )
      })
    },
    Contact: {
      screen: Contact,
      navigationOptions: {
        tabBarLabel: '通讯录',
        tabBarIcon: ({focused, tintColor}) => (
          <View>
            <Text style={[ GStyle.iconfont, GStyle.fs_20, {color: (focused ? tintColor : '#999')} ]}></Text>
          </View>
        )
      }
    },
    Ucenter: {
      screen: Ucenter,
      navigationOptions: {
        tabBarLabel: '我',
        tabBarIcon: ({focused, tintColor}) => (
          <View>
            <Text style={[ GStyle.iconfont, GStyle.fs_20, {color: (focused ? tintColor : '#999')} ]}></Text>
            <View style={[GStyle.badge_dot, {position: 'absolute', top: -2, right: -6,}]}></View>
          </View>
        )
      }
    }
  },
  // tabbar配置
  {
    ...
  }
)

◆ RN聊天页面功能模块

1、表情处理:原本是想着使用图片表情gif,可是在RN里面textInput文本框不能插入图片,只能通过定义一些特殊字符 :66: (:12 [奋斗] 解析表情,处理起来有些麻烦,而且图片多了影响性能,如是就改用emoj表情符。

react native 仿微信聊天室实例代码react native 仿微信聊天室实例代码

faceList: [
  {
    nodes: [
      '?','?','?','?','?','?','?',
      '?','?','?','?','?','?','?',
      '?','?','?','?','?','?','del',
    ]
  },
  ...
  {
    nodes: [
      '?','?','?','?','?','?','?',
      '?','?','?','?','?','??','??',
      '??','??','?‍?','??‍?','??‍?','??‍✈️','del',
    ]
  },
  ...
]

2、光标定位:在指定光标处插入内容,textInput提供了光标起始位置

let selection = this.textInput._lastNativeSelection || null;
this.textInput.setNativeProps({
selection : { start : xxx, end : xxx}
})

3、textInput判断内容是否为空,过滤空格、回车

isEmpty = (html) => {
return html.replace(/\r\n|\n|\r/, "").replace(/(?:^[ \t\n\r]+)|(?:[ \t\n\r]+$)/g, "") == ""
}
/**
 * 聊天模块JS----------------------------------------------------
 */
// ...滚动至聊天底部
scrollToBottom = (t) => {
  let that = this
  this._timer = setTimeout(() => {
    that.refs.scrollView.scrollToEnd({animated: false})
  }, t ? 16 : 0);
}
// ...隐藏键盘
hideKeyboard = () => {
  Keyboard && Keyboard.dismiss()
}
// 点击表情
handlePressEmotion = (img) => {
  if(img === 'del') return
  let selection = this.editorInput._lastNativeSelection || null;
  if (!selection){
    this.setState({
      editorText : this.state.editorText + `${img}`,
      lastRange: this.state.editorText.length
    })
  }
  else {
    let startStr = this.state.editorText.substr(0 , this.state.lastRange ? this.state.lastRange : selection.start)
    let endStr = this.state.editorText.substr(this.state.lastRange ? this.state.lastRange : selection.end)
    this.setState({
      editorText : startStr + `${img}` + endStr,
      lastRange: (startStr + `${img}`).length
    })
  } 
}
// 发送消息
isEmpty = (html) => {
  return html.replace(/\r\n|\n|\r/, "").replace(/(?:^[ \t\n\r]+)|(?:[ \t\n\r]+$)/g, "") == ""
}
handleSubmit = () => {
  // 判断是否为空值
  if(this.isEmpty(this.state.editorText)) return
  let _msg = this.state.__messageJson
  let _len = _msg.length
  // 消息队列
  let _data = {
    id: `msg${++_len}`,
    msgtype: 3,
    isme: true,
    avator: require('../../../assets/img/uimg/u__chat_img11.jpg'),
    author: '王梅(Fine)',
    msg: this.state.editorText,
    imgsrc: '',
    videosrc: ''
  }
  _msg = _msg.concat(_data)
  this.setState({ __messageJson: _msg, editorText: '' })
  this.scrollToBottom(true)
}

// >>> 【选择区功能模块】------------------------------------------
// 选择图片
handleLaunchImage = () => {
  let that = this
  ImagePicker.launchImageLibrary({
    // title: '请选择图片来源',
    // cancelButtonTitle: '取消',
    // takePhotoButtonTitle: '拍照',
    // chooseFromLibraryButtonTitle: '相册图片',
    // customButtons: [
    //   {name: 'baidu', title: 'baidu.com图片'},
    // ],
    // cameraType: 'back',
    // mediaType: 'photo',
    // videoQuality: 'high',
    // maxWidth: 300,
    // maxHeight: 300,
    // quality: .8,
    // noData: true,
    storageOptions: {
      skipBackup: true,
    },
  }, (response) => {
    // console.log(response)
    if(response.didCancel){
      console.log('user cancelled')
    }else if(response.error){
      console.log('ImagePicker Error')
    }else{
      let source = { uri: response.uri }
      // let source = {uri: 'data:image/jpeg;base64,' + response.data}
      that.setState({ imgsrc: source })
      that.scrollToBottom(true)
    }
  })
}

总结

以上所述是小编给大家介绍的react native 仿微信聊天室实例代码,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

Javascript 相关文章推荐
MC Dialog js弹出层 完美兼容多浏览器(5.6更新)
May 06 Javascript
IE6/7/8/9不支持exec的简写方式
May 25 Javascript
灵活应用js调试技巧解决样式问题的步骤分享
Mar 15 Javascript
11个用于提高排版水平的基于jquery的文字效果插件
Sep 14 Javascript
js完美实现@提到好友特效(兼容各大浏览器)
Mar 16 Javascript
Backbone.js框架中Model与Collection的使用实例
May 07 Javascript
简单实现jQuery级联菜单
Jan 09 Javascript
探索Vue高阶组件的使用
Jan 08 Javascript
vue单页面在微信下只能分享落地页的解决方案
Apr 15 Javascript
javascript实现导航栏分页效果
Jun 27 Javascript
解决vue中使用proxy配置不同端口和ip接口问题
Aug 14 Javascript
layer父页获取弹出层输入框里面的值方法
Sep 02 Javascript
Vue 自定义指令功能完整实例
Sep 17 #Javascript
JS扁平化输出数组的2种方法解析
Sep 17 #Javascript
JS代码屏蔽F12,右键,粘贴,复制,剪切,选中,操作实例
Sep 17 #Javascript
uni app仿微信顶部导航条功能
Sep 17 #Javascript
layui 关闭open弹出框 刷新table表格页面的方法
Sep 16 #Javascript
解决layer.confirm选择完之后消息框不消失的问题
Sep 16 #Javascript
解决layui轮播图有数据不显示的情况
Sep 16 #Javascript
You might like
php flush类输出缓冲剖析
2008/10/19 PHP
解析PHPExcel使用的常用说明以及把PHPExcel整合进CI框架的介绍
2013/06/24 PHP
深入PHP购物车模块功能分析(函数讲解,附源码)
2013/06/25 PHP
PHP实现把文本中的URL转换为链接的auolink()函数分享
2014/07/29 PHP
PHP函数http_build_query使用详解
2014/08/20 PHP
PHPExcel实现表格导出功能示例【带有多个工作sheet】
2018/06/13 PHP
用js怎么把&amp;字符换成&quot;&amp;amp:&quot;
2006/10/19 Javascript
Javascript学习笔记一 之 数据类型
2010/12/15 Javascript
jQuery.query.js 取参数的两点问题分析
2012/08/06 Javascript
jquery根据name属性查找的小例子
2013/11/21 Javascript
jQuery中empty()方法用法实例
2015/01/16 Javascript
vue.js 获取当前自定义属性值
2017/06/01 Javascript
详解webpack+gulp实现自动构建部署
2017/06/29 Javascript
Nodejs下使用gm圆形裁剪并合成图片的示例
2018/02/22 NodeJs
详解vue-cli 快速搭建单页应用之遇到的问题及解决办法
2018/03/01 Javascript
用原生JS实现爱奇艺首页导航栏代码实例
2019/09/19 Javascript
element 中 el-menu 组件的无限极循环思路代码详解
2020/04/26 Javascript
Vue2.0 $set()的正确使用详解
2020/07/28 Javascript
JavaScript代码模拟鼠标自动点击事件示例
2020/08/07 Javascript
js实现搜索提示框效果
2020/09/05 Javascript
[01:13:51]TNC vs Serenity 2018国际邀请赛小组赛BO2 第二场 8.18
2018/08/19 DOTA
Python实现聊天机器人的示例代码
2018/07/09 Python
python微信聊天机器人改进版(定时或触发抓取天气预报、励志语录等,向好友推送)
2019/04/25 Python
Python Django 页面上展示固定的页码数实现代码
2019/08/21 Python
关于tf.matmul() 和tf.multiply() 的区别说明
2020/06/18 Python
Python创建自己的加密货币的示例
2021/03/01 Python
在HTML5中使用MathML数学公式的简单讲解
2016/02/19 HTML / CSS
整理HTML5移动端开发的常用触摸事件
2016/04/15 HTML / CSS
html5借用repeating-linear-gradient实现一把刻度尺(ruler)
2019/09/09 HTML / CSS
AmazeUI 手机版页面的顶部导航条Header与侧边导航栏offCanvas的示例代码
2020/08/19 HTML / CSS
Hotels.com拉丁美洲:从豪华酒店到经济型酒店的预定优惠和折扣
2019/12/09 全球购物
struct和class的区别
2015/11/20 面试题
会计系毕业个人自荐信格式
2013/09/23 职场文书
领导干部四风问题自我剖析材料
2014/09/25 职场文书
老人与海读书笔记
2015/06/26 职场文书
linux下导入、导出mysql数据库命令的实现方法
2021/05/26 MySQL