ReactNative 仿微信聊天 App 实例分享|RN 仿朋友圈 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
如果想在 V2EX 获得更好的推广效果,欢迎了解 PRO 会员机制:
pro/about
xiaoyan2017

ReactNative 仿微信聊天 App 实例分享|RN 仿朋友圈

  •  1
     
  •   xiaoyan2017 2019 年 9 月 4 日 3228 次点击
    这是一个创建于 2425 天前的主题,其中的信息可能已经有所发展或是发生改变。

    今天给大家分享的是 RN 聊天室项目,基于 react-native+react-navigation+react-redux+react-native-image-picker+rnPop 等技术实现高仿微信聊天 APP 界面,从搭建到开发完 前前后后发了两周左右吧,多半是晚上挤出时间来弄,开发过程采坑不少,好在 reactNative 社区比较完善,很多困难都在网上找到了解决方法。

    使用技术:

    • MVVM 框架:react / react-native / react-native-cli
    • 状态管理:react-redux
    • 页面导航:react-navigation
    • rn 弹窗组件:rnPop
    • 打包工具:webpack 2.0
    • 轮播组件:react-native-swiper
    • 图片 /相册:react-native-image-picker

    效果预览

    { "name": "RN_ChatRoom", "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" } } 

    ReactNative 实现公共样式

    <View style={[GStyle.borT, GStyle.bg_45cff5, GStyle.mt_10]}></View> global.GStyle = { pixel: 1 / PixelRatio.get(), // 边框 borT: {borderTopWidth: 1 / PixelRatio.get(), borderTopColor: '#dedede',}, borB: {borderBottomWidth: 1 / PixelRatio.get(), borderBottomColor: '#dedede',}, /* __ 布局控制 */ align_l: {textAlign: 'left'}, align_c: {textAlign: 'center'}, align_r: {textAlign: 'right'}, pos_rel: {position: 'relative'}, pos_abs: {position: 'absolute'}, /* __ 颜色(背景、文字) */ bg_fff: {backgroundColor: '#fff'}, bg_45cff5: {backgroundColor: '#45cff5'}, c_fff: {color: '#fff'}, c_999: {color: '#999'}, c_45cff5: {color: '#45cff5'}, /* __ 字号 */ fs_14: {fontSize: 14}, fs_16: {fontSize: 16}, fs_20: {fontSize: 20}, fs_24: {fontSize: 24}, /* __ 字体 */ ff_ic: {fontFamily: 'iconfont'}, ff_ar: {fontFamily: 'arial'}, iconfont: {fontFamily: 'iconfont', fontSize: 16,}, /* __ 间距( 5/10/15/20/25/30/50 ) */ mt_10: {marginTop: 10}, mt_15: {marginTop: 15}, mt_20: {marginTop: 20}, mb_10: {marginBottom: 10}, mb_15: {marginBottom: 15}, mb_20: {marginBottom: 20}, /* __ 行高 */ lh_20: {lineHeight: 20}, lh_25: {lineHeight: 25}, lh_30: {lineHeight: 30}, lh_35: {lineHeight: 35}, lh_40: {lineHeight: 40}, flex1: {flex: 1}, flex2: {flex: 2}, flex_alignC: {alignItems: 'center'}, flex_alignT: {alignItems: 'flex-start'}, flex_alignB: {alignItems: 'flex-end'}, } 

    react-native 实现全屏幕启动页,可自定义背景图

    reactNative 全屏启动页制作(隐藏状态栏,实现沉浸式) 只需把 StatusBar 设置为透明即可,这样状态栏和背景页面一体了。 <statusbar backgroundcolor="transparent" barstyle="light-content" translucent="{true}"></statusbar>

    /** * @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); }) } } 

    react-navigation 导航器实现自定义顶部导航条 headerBar 组件

    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]}>&#xe63f;</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() } } 

    reactNative 自定义 modal 弹窗|dialog 对话框

    由于 RN 提供的弹窗有时不能满足项目需求,这时就需要自己重新定制弹窗,不过依旧基于 Modal 来实现。 看看下面这个,自己开发的 rnPop 弹窗组件, 功能效果还不错~~ 支持多种调用方式,具体的可以去看看这篇文章介绍 https://www.cnblogs.com/xiaoyan2017/p/11292096.html

    static defaultProps = { isVisible: false, //弹窗显示 title: '', //标题 content: '', //内容 style: null, //自定义弹窗样式 {object} contentStyle: null, //内容样式 skin: '', //自定义弹窗风格 icon: '', //自定义弹窗图标 shade: true, //遮罩层 shadeClose: true, //点击遮罩层关闭 opacity: '', //遮罩层透明度 time: 0, //弹窗自动关闭秒数 xtime: false, //显示关闭秒数 end: null, //销毁弹窗时回调函数 anim: 'scaleIn', //弹窗动画( scaleIn / fadeIn / top / bottom / left / right ) follow: null, //跟随定位(适用于在长按位置定位弹窗) position: '', //弹窗位置 btns: null, //弹窗按钮(不设置则不显示按钮)[{...options}, {...options}] } 

    reactNative 如何实现聊天表情、在 TextInput 插入表情

    在社交软件中,基本上都会有 emoji 表情功能。聊天中要显示文字和 emoji 表情的混排(下图所示),在原生 iOS 开发时,可以用富文本 NSAttributedString 实现,安卓中用 SpannableString 实现。当用到 React-Native 来开发这个功能的时候,貌似没有直接的现成的实现方案。 方法一: 通过特殊符处理,[高兴] (:88 类似这样的,处理起来比较麻烦,而且图片多了会影响页面性能。 具体实现方法可参考: https://www.jianshu.com/p/2331860db169 方法二: 使用 emoj 表情符,这种方式处理比较简单,网上很多表情符可用,而且不需要特殊处理,性能也还不错。如果要求不高,推荐这种方式。

    faceList: [ { nodes: [ '','','','','','','', '','','''','','','', '','','','','','','del', ] }, { nodes: [ '','','','','','','', '','','','','','','', '','','','','','','del', ] }, { nodes: [ '','','','','','','', '','','','','','','', '','','','','','','del', ] }, ... ] 

    好了,以上就是今天的分享,希望以后能给大家分享更多知识。希望能喜欢

    1 条回复    2019-09-04 15:30:20 +08:00
    eluotao
        1
    eluotao  
       2019 年 9 月 4 日
    UI 体验 都不像朋友圈
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2876 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 35ms UTC 03:30 PVG 11:30 LAX 20:30 JFK 23:30
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86