react-native聊天室|RN版聊天App仿微信实例|RN仿微信界面

一、前言

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-navigationrn
  • 弹窗组件: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"
 }
}

◆ 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官方顶部导航器不能满足需求,如是自己封装了一个,功能效果有些类似微信导航。

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表情符。

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聊天室|RN版聊天App仿微信实例|RN仿微信界面,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

(0)

相关推荐

  • react-native滑动吸顶效果的实现过程

    前言 最近公司开发方向偏向移动端,于是就被调去做RN(react-native),体验还不错,当前有个需求是首页中间吸顶的效果,虽然已经很久没写样式了,不过这种常见样式应该是so-easy,没成想翻车了,网上搜索换了几个方案都不行,最后去github上复制封装好的库来实现,现在把翻车过程记录下来. 需求效果 翻车过程 第一种方案 失败 一开始的思路是这样的,大众思路,我们需要监听页面的滚动状态,当页面滚动到要吸顶元素所处的位置的时候,我们设置它为固定定位,不过很遗憾,RN对于position属性

  • react-native动态切换tab组件的方法

    在APP中免不了要使用tab组件,有的是tab切换,也有的是tab分类切换. 这些组件分成下面两种. 第一种非常简单,同时大多数第三方组件都能达到效果.这里重点讲述第二种,我们要让第二种组件不仅能左右滑动,同时还能够在点击的时候自动滑动,将点击的位置滑动到正中间. 准备 我们先来分析一波.一个滑动组件在APP上是一种什么状态. 这里可以看出,tab组件需要考虑到长度超过APP的屏幕,并且在超过之后能够滑动. 同时计算出当前位置需要滑动多少距离才能够将位置居中. 需要滑动的位置=点击位置的左边距-

  • react-native封装插件swiper的使用方法

    首先创建简单的react-native项目,创建一个文件夹.然后用命令符输入 react-native init swiper 创建完成之后开发项目,我用的vs 打开控制台,安装swiper依赖. 安装:npm i react-native-swiper --save 查看:npm view react-native-swiper 删除:npm rm react-native-swiper --save 这里还需要 npm i 下更新下本地的依赖库 启动app项目 ios: react-nati

  • react-native android状态栏的实现

    react-native 开发App的时候难免会遇到状态栏的,背景颜色和字体颜色与App内容页面,色调适配,间言之就是将状态栏颜色与App颜色一致,使用户界面更加整体. 1.android设备系统元素 导航栏:就是设备顶部的网络.时间.电量等信息栏 ActionBar: 返回按钮以及系统默认的header区域,RN开发中一般不会用到,RN中在navigation中进行定制 导航栏: 设备下方的物理返回.回桌面.选择应用程序等系统导航栏 2.状态栏的呈现形式 默认展示,一直显示手机系统的状态栏 透

  • react native基于FlatList下拉刷新上拉加载实现代码示例

    react native 的上拉加载一直困扰着自己,一直用的第三方组件,但是可维护性不高,而且也不太好用,最近工作没那么忙,就研究下了官方的FlatList,做出来的成果,比第三方组件流畅度高好多,而且也很好用 官方介绍:https://reactnative.cn/docs/flatlist/ 下面是效果图: ios效果图 android效果图 总体思路就是:就是计算屏幕高度,然后减去导航的头部,根据列表高度计算出每页的个数,然后向上取整.这样做的目的是:防止不满屏状态下的,onEndReac

  • React Native开发封装Toast与加载Loading组件示例

    在App开发中,我们避免不了使用的两个组件,一个Toast,一个网络加载Loading,在RN开发中,也是一样,React Native官方并没有提供者这两个常用组件,需要开发者自己根据需求来自定义.作者就在其他组件的基础上在进行二次封装,使用起来更加简单,更具扩展性,同学们只需将Toast与Loading文件拖到项目中,install对应的组件库即可 效果图 Toast和Loading Demo地址 https://github.com/guangqiang-liu/react-native-

  • react-native-video实现视频全屏播放的方法

    react-native-video是github上一个专用于React Native做视频播放的组件.这个组件是React Native上功能最全最好用的视频播放组件,还在持续开发之中,虽然还有些bug,但基本不影响使用,强力推荐. 本篇文章主要介绍下怎么使用react-native-video播放视频,以及如何实现全屏播放,屏幕旋转时视频播放器大小随之调整,显示全屏或收起全屏. 首先来看看react-native-video有哪些功能. 基本功能 控制播放速率 控制音量大小 支持静音功能 支

  • react-native聊天室|RN版聊天App仿微信实例|RN仿微信界面

    一.前言 9月,又到开学的季节.为每个一直默默努力的自己点赞!最近都沉浸在react native原生app开发中,之前也有使用vue/react/angular等技术开发过聊天室项目,另外还使用RN技术做了个自定义模态弹窗rnPop组件. 一.项目简述 基于react+react-native+react-navigation+react-redux+react-native-swiper+rnPop等技术开发的仿微信原生App界面聊天室--RN_ChatRoom,实现了原生app启动页.As

  • Java聊天室之实现聊天室服务端功能

    目录 一.题目描述 二.解题思路 三.代码详解 多学一个知识点 一.题目描述 题目实现:实现聊天室服务器端功能.运行程序,服务端等待客户端连接,并显示客户端的连接信息. 二.解题思路 创建一个服务类:ChatServerFrame,继承JFrame类 定义一个Hashtable对象,用于存储登录用户的用户名和套接字对象. 定义createSocket()方法,用于创建服务器套接字对象.获得连接到服务器的客户端套接字对象以及启动线程对象对客户端发送的信息进行处理. 定义内部线程类ServerThr

  • Java聊天室之实现聊天室客户端功能

    目录 一.题目描述 二.解题思路 三.代码详解 一.题目描述 题目实现:实现聊天室客户端.运行程序,用户登录服务器后,可以从用户列表中选择单个用户进行聊天,也可以选择多个用户进行聊天. 二.解题思路 创建一个服务类:ChatClientFrame,继承JFrame类.用于进行用户登录.发送聊天信息和显示聊天信息,在该类中完成窗体界面的设计. 定义createClientSocket)方法,用于创建套接字对象.输出流对象以及启动线程对象对服务器转发的信息进行处理. 定义内部线程类ClientThr

  • java实现简单聊天室单人版

    本文实例为大家分享了java实现简单聊天室的具体代码,供大家参考,具体内容如下 先整理下思路: 1.创建一个通信服务端,传入端口号和相关的流后等待客户端连接,并初始化图形界面. 2.创建一个JFrame,用于写出聊天的界面,这里界面名称由其他类传入. 3.把客户端创建的方法写进JFrame(当然这里很粗糙的方法) 4.设置按钮的监听事件,发送消息和离线功能 首先创建一个服务端的类 import java.io.IOException; import java.net.ServerSocket;

  • 用PHP+MySQL搭建聊天室功能实例代码

    下面,以一个简单的聊天室设计为例,介绍PHP+MySQL在网页开发中的应用 1.总体设计 1.1 构思与规划: 聊天室的基本原理,就是把每个连上同一网页的用户传送的发言数据储存起来,然后将所有的发言数据传给每一用户.也就是说,用数据库汇集每个人的发言,并将数据库中的数据传给每一个人就实现了聊天室的功能. 1.2 表设计 首先使用MySQL建立表chat用来储存用户的发言: 复制代码 代码如下: mysql> CREATE TABLE chat -> (chtime DATATIME, ->

  • 基于PHP+MySQL的聊天室设计

    笔者在网站开发中,采用PHP4.0+MySQL3.23.38建立了多种应用.下面,以一个简单的聊天室设计为例,介绍PHP+MySQL在网页开发中的应用. 1.总体设计 1. 1 构思与规划: 聊天室的基本原理,就是把每个连上同一网页的用户传送的发言数据储存起来,然后将所有的发言数据传给每一用户.也就是说,用数据库汇集每个人的发言,并将数据库中的数据传给每一个人就实现了聊天室的功能. 1.2 表设计 首先使用MySQL建立表chat用来储存用户的发言: 复制代码 代码如下: mysql> CREA

  • PHP设计聊天室步步通

    聊天室可以采用完全自由的方式运行,你可以随意输入呢称,不用密码,不保存你的聊天状态,优点是:自由,非常适合于游客!另外一个方法是注册聊天室,每个进入聊天室的人都要输入自己的用户名和密码才能进入!优点:充分体现个性,非常适合于老朋友,他们的呢称不会被人恶意侵占使用.我的聊天室使用注册方法! 注册通常采用2种方法: 1.先注册然后进入聊天   2.自动注册 然后在里面修改自己的资料!我采用第2种方法!!每个新进入的聊友的用户名会被自动保存到注册到数据库内,下次登陆必须输入准确的密码才能进入! 下面是

  • C#制作简单的多人在线即时交流聊天室

    实现网页版的在线聊天室的方法有很多,在没有来到HTML5之前,常见的有:定时轮询.长连接+长轮询.基于第三方插件(如FLASH的Socket),而如果是HTML5,则比较简单,可以直接使用WebSocket,当然HTML5目前在PC端并没有被所有浏览器支持,所以我的这个聊天室仍是基于长连接+长轮询+原生的JS及AJAX实现的多人在线即时交流聊天室,这个聊天室其实是我上周周末完成的,功能简单,可能有些不足,但可以满足在线即时聊天需求,分享也是给大家提供一个思路,大家可以基于此来实现更好的在线即时聊

  • vue组件父子间通信之综合练习(聊天室)

    本文实例为大家分享了vue组件父子间通信之聊天室的具体代码,供大家参考,具体内容如下 <!doctype html> <html> <head> <meta charset="UTF-8"> <title>组件父子间通信之综合练习</title> <script src="js/vue.js"></script> </head> <body> &l

  • Android中基于XMPP协议实现IM聊天程序与多人聊天室

    简单的IM聊天程序 由于项目需要做一个基于XMPP协议的Android通讯软件.故开始研究XMPP. XMPP协议采用的是客户端-服务器架构,所有从一个客户端发到另一个客户端的消息和数据都必须经过XMPP服务器转发,而且支持服务器间DNS的路由,也就是说可以构建服务器集群,使不同的 服务器下的客户端也可以通信,XMPP的前身是一个开源组织制定的网络通信协议--Jabber,XMPP的核心是在网络上分片段发送XML流的协议,这个协议是XMPP的即时通讯指令的传递手段.       为了防止服务器间

随机推荐