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 相关文章推荐
各浏览器中querySelector和querySelectorAll的实现差异分析
May 23 Javascript
基于jQuery的360图片展示实现代码
Jun 14 Javascript
跟我学习javascript的函数和函数表达式
Nov 16 Javascript
值得分享的轻量级Bootstrap Table表格插件
May 30 Javascript
jQuery插件HighCharts绘制2D带Label的折线图效果示例【附demo源码下载】
Mar 08 Javascript
vue2.0之多页面的开发的示例
Jan 30 Javascript
详解PHP后期静态绑定分析与应用
Mar 21 Javascript
vue引入axios同源跨域问题
Sep 27 Javascript
js自定义input文件上传样式
Oct 26 Javascript
vue引入微信sdk 实现分享朋友圈获取地理位置功能
Jul 04 Javascript
小程序点餐界面添加购物车左右摆动动画
Sep 23 Javascript
微信小程序入门之绘制时钟
Oct 22 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
德劲1103的维修打理经验
2021/03/02 无线电
php+mysql实现无限级分类 | 树型显示分类关系
2006/11/19 PHP
PHP MVC模式在网站架构中的实现分析
2010/03/04 PHP
php将gd生成的图片缓存到memcache的小例子
2013/06/05 PHP
Yii的CDbCriteria查询条件用法实例
2014/12/04 PHP
PHP常见错误提示含义解释(实用!值得收藏)
2016/04/25 PHP
PHP 等比例缩放图片详解及实例代码
2016/09/18 PHP
PHP设计模式之状态模式定义与用法详解
2018/04/02 PHP
QQ登录简单实现代码
2021/03/09 Javascript
javascript 常用功能总结
2012/03/18 Javascript
JavaScript中的稀疏数组与密集数组[译]
2012/09/17 Javascript
JS实现控制表格只显示行边框或者只显示列边框的方法
2015/03/31 Javascript
详解JavaScript编程中正则表达式的使用
2015/10/25 Javascript
js 截取或者替换字符串中的数字实现方法
2016/06/13 Javascript
JavaScript实现广告弹窗效果
2016/08/09 Javascript
关于JavaScript数组你所不知道的3件事
2016/08/24 Javascript
Javascript vue.js表格分页,ajax异步加载数据
2016/10/24 Javascript
使用konva和vue-konva库实现拖拽滑块验证功能
2020/04/27 Javascript
[01:03:41]完美世界DOTA2联赛PWL S3 DLG vs Phoenix 第一场 12.17
2020/12/19 DOTA
python安装PIL模块时Unable to find vcvarsall.bat错误的解决方法
2016/09/19 Python
django实现前后台交互实例
2017/08/07 Python
Python方法的延迟加载的示例代码
2017/12/18 Python
Python的条件表达式和lambda表达式实例
2019/01/31 Python
Python爬虫库BeautifulSoup获取对象(标签)名,属性,内容,注释
2020/01/25 Python
利用Python如何实时检测自身内存占用
2020/05/09 Python
GAZMAN官网:澳大利亚领先的男装品牌
2019/12/19 全球购物
Douglas意大利官网:购买香水和化妆品
2020/05/27 全球购物
Linux文件操作命令都有哪些
2016/07/23 面试题
GWT的应用有哪两种部署模式
2012/12/21 面试题
劳动竞赛活动方案
2014/02/20 职场文书
反对邪教标语
2014/06/30 职场文书
公务员群众路线心得体会
2014/11/03 职场文书
2015年度团总支工作总结
2015/04/23 职场文书
培训班通知
2015/04/25 职场文书
行政处罚决定书
2015/06/24 职场文书
mysql数据插入覆盖和时间戳的问题及解决
2022/03/25 MySQL