本站文章均为 李华明Himi 原创,转载务必在明显处注明: 
转载自【黑米GameDev街区】 原文链接: http://www.himigame.com/react-native/2346.html

补充说明:

一:很多童鞋问,键盘调出来被挡住了,那么下面给出三个解决方案:

1. 在render最外层包一个ScrollView,然后当键盘调出时,scrollTo即可实现。

2. 在底部添加一个可变化高度的view,根据键盘获取、失去焦点时,进行处理实现

3. 使用插件:react-native-keyboard-spacer :https://github.com/Andr3wHur5t/react-native-keyboard-spacer

二:有的童鞋说对话框的背景没有根据内容长短自适应,OK ,下面给出自动适应的样式与修改:

先看效果图:

1. 导入一个组件:Dimensions

2. 我们先将 renderEveryData 的函数改为如下:

    renderEveryData(eData) {var sWidth = Dimensions.get('window').widthreturn (<View style={eData.isMe==true?styles.everyRowRight:styles.everyRow}><Imagesource={eData.isMe==true? null:require('./res/headIcon/ox1.png')}style={eData.isMe==true?null:styles.talkImg}/><View style={{width:sWidth - 100}}><View style={eData.isMe==true?styles.talkViewRight:styles.talkView}><Text style={ eData.isMe==true?styles.talkTextRight:styles.talkText }>{eData.talkContent}</Text></View></View><Imagesource={eData.isMe==true? require('./res/headIcon/ox2.png') :null}style={eData.isMe==true?styles.talkImgRight:null}/></View>);}

3. 用到的样式如下:

  everyRow:{flexDirection:'row',alignItems: 'center'},everyRowRight:{flexDirection:'row',alignItems: 'center',justifyContent:'flex-end'},talkView: {backgroundColor: 'white',padding: 10,borderRadius:5,marginLeft:5,marginRight:55,marginBottom:10,alignSelf:'flex-start',},talkViewRight: {backgroundColor: '#90EE90',padding: 10,borderRadius:5,marginLeft:55,marginRight:5,marginBottom:10,alignSelf:'flex-end',},talkText: {fontSize: 16,fontWeight: 'bold',},talkTextRight: {fontSize: 16,fontWeight: 'bold',alignSelf:'flex-end',},talkImg: {height: 40,width: 40,marginLeft:10,marginBottom:10},talkImgRight: {height: 40,width: 40,marginRight:10,marginBottom:10},

width:sWidth – 100:这里是来限定Text每一行的最大宽度。

sWidth:是获取的屏幕宽。

因此通过修改这里的值来指定你想要的每一行最大宽度吧。

——————————————–以上为补充内容,下面是正文——————————————–

本篇Himi来利用ListView和TextInput这两种组件实现对话、聊天框。

首先需要准备的有几点:(组件的学习就不赘述了,简单且官方有文档)

1. 学习下 ListView:

官方示例:http://reactnative.cn/docs/0.27/tutorial.html#content

官方文档:http://reactnative.cn/docs/0.27/listview.html#content

2. 学习下:TextInput:

官方文档:http://reactnative.cn/docs/0.27/textinput.html#content

3.  获取组件实例常用的两种方式:

有时候,渲染出来的组件,我们需要拿到它的实例进行调用其函数等操作。假设有如下代码段:

render() {return (<Text>Himi</Text>)
}

如上,如果我们想要拿到这个Text组件的实例对象,有如下两种形式:

第一种:

render() {return (<Text>Himi</Text>)
}

使用时:this.refs._text ,通过this.refs进行获取。

第二种:

render() {var _text;return (<Text ref={(text) => { _text = text; }}>Himi</Text>)
}

使用时:_text ,直接用这个变量即可。

如上都有了一定了解时,那么下面我们进行本篇的正题:

  制作一个对话、聊天框,内容可滚动,且最新的消息永远保持在最底部显示!

一:首先我们先简单布局一个聊天场景,布局+各种小组件的使用(代码简单,不多说):

import React, {Component
} from 'react';
import {View,Text,TouchableHighlight,Image,PixelRatio,ListView,StyleSheet,TextInput,Alert,} from 'react-native';var datas =[{isMe:false,talkContent:'最近在学习React Native哦!',},{isMe:true,talkContent:'听说是个跨平台开发原生App的开源引擎',},{isMe:false,talkContent:'嗯啊,很不错,可以尝试下吧。过了这段时间继续研究UE去了。唉~技术出身,就是放不下技术呀~',},{isMe:false,talkContent:'感觉编不下去对话了呀......感觉编不下去对话了呀......感觉编不下去对话了呀......感觉编不下去对话了呀......',},{isMe:true,talkContent:'无语!',},{isMe:false,talkContent:'自说自话,好难!随便补充点字数吧,嗯 就酱紫 :) ',},{isMe:true,talkContent:'感觉编不下去对话了呀......感觉编不下去对话了呀..',},{isMe:false,talkContent:'GG,思密达编不下去了!',},
];export default class FarmChildView extends React.Component {constructor(props) {super(props);this.state = {inputContentText:'',dataSource: new ListView.DataSource({rowHasChanged: (row1, row2) => row1 !== row2,}),};this.listHeight = 0;this.footerY = 0;}componentDidMount() {this.setState({dataSource: this.state.dataSource.cloneWithRows(datas)});}renderEveryData(eData) {return (<View style={{flexDirection:'row',alignItems: 'center'}}><Imagesource={eData.isMe==true? null:require('./res/headIcon/ox1.png')}style={eData.isMe==true?null:styles.talkImg}/><View style={eData.isMe==true?styles.talkViewRight:styles.talkView}><Text style={ styles.talkText }>{eData.talkContent}</Text></View><Imagesource={eData.isMe==true? require('./res/headIcon/ox2.png') :null}style={eData.isMe==true?styles.talkImgRight:null}/></View>);}myRenderFooter(e){}pressSendBtn(){}render() {return (<View style={ styles.container }><View style={styles.topView}><Text style={{fontSize:20,marginTop:15,color:'#f00'}}>Himi React Native 系列教程</Text></View><ListViewref='_listView'onLayout={(e)=>{this.listHeight = e.nativeEvent.layout.height;}}dataSource={this.state.dataSource}renderRow={this.renderEveryData.bind(this)}renderFooter={this.myRenderFooter.bind(this)}/><View style={styles.bottomView}><View style={styles.searchBox}><TextInputref='_textInput'onChangeText={(text) =>{this.state.inputContentText=text}}placeholder=' 请输入对话内容'returnKeyType='done'style={styles.inputText}/></View><TouchableHighlightunderlayColor={'#AAAAAA'}activeOpacity={0.5}onPress={this.pressSendBtn.bind(this)}><View style={styles.sendBtn}><Text style={ styles.bottomBtnText }>发送</Text></View></TouchableHighlight></View></View>);}
}var styles = StyleSheet.create({container: {flex: 1,backgroundColor: '#EEEEEE'},topView:{alignItems: 'center',backgroundColor: '#DDDDDD',height: 52,padding:5},bottomView:{flexDirection: 'row',alignItems: 'center',backgroundColor: '#DDDDDD',height: 52,padding:5},sendBtn: {alignItems: 'center',backgroundColor: '#FF88C2',padding: 10,borderRadius:5,height:40,},bottomBtnText: {flex: 1,fontSize: 18,fontWeight: 'bold',},talkView: {flex: 1,alignItems: 'center',backgroundColor: 'white',flexDirection: 'row',padding: 10,borderRadius:5,marginLeft:5,marginRight:55,marginBottom:10},talkImg: {height: 40,width: 40,marginLeft:10,marginBottom:10},talkText: {flex: 1,fontSize: 16,fontWeight: 'bold',},talkViewRight: {flex: 1,alignItems: 'center',backgroundColor: '#90EE90',flexDirection: 'row',justifyContent: 'flex-end',padding: 10,borderRadius:5,marginLeft:55,marginRight:5,marginBottom:10},talkImgRight: {height: 40,width: 40,marginRight:10,marginBottom:10},searchBox: {height: 40,flexDirection: 'row',flex:1,  // 类似于android中的layout_weight,设置为1即自动拉伸填充borderRadius: 5,  // 设置圆角边backgroundColor: 'white',alignItems: 'center',marginLeft:5,marginRight:5,marginTop:10,marginBottom:10,},inputText: {flex:1,backgroundColor: 'transparent',fontSize: 20,marginLeft:5},
});

以上一共做了这么几件事:

  1. 顶部添加一个标题

  2. 添加一个ListView

  3. 底部添加一个输入框和发送按钮

以上代码需要讲解的有几点:

1. inputContentText 这个state中的变量用于记录用户在TextInput输入的内容

2.  this.listHeight = 0; 获取到ListHeight的高度

this.footerY = 0; 记录ListView内容的最底部的Y位置。

(作用后续讲)

3.  myRenderFooter(e){} 这里是当ListView的 renderFooter 函数触发时候调用的。(作用后续讲)

4. pressSendBtn 是当当点击发送按钮后,调用我们的自定义函数。

先看下布局后的效果图(点击查看动态效果):

二:下面我们实现点击发送后,将用户在输入框内输入的内容添加到我们的ListView上,并重绘!

主要处理逻辑,Himi已经设计好了,就是在 pressSendBtn 函数中处理即可,处理代码段如下:

pressSendBtn(){if(this.state.inputContentText.trim().length <= 0){Alert.alert('提示', '输入的内容不能为空');return;}datas.push({isMe:false,talkContent:this.state.inputContentText,});this.refs._textInput.clear();this.setState({inputContentText:'',dataSource: this.state.dataSource.cloneWithRows(datas)})}

1. if(  this.state.inputContentText.trim().length <= 0 )

inputContentText用来记录用户在输入框输入的内容,因此这里我们先对内容是否为空进行判定!

trim () 函数不多说了吧,去掉字符串首尾空格。纯空格的内容也不允许发送~

   2. datas.push 

这里是我们将新的数据添加到ListView中,其中文字内容就是我们记录的用户输入的内容

   3. this.refs._textInput.clear()

这里就是我们一开始准备工作介绍的小3节,通过this.refs._textInput()来获取我们定义的TextInput组件实例。

   4. 最后我们调用了 this.setState函数来对其两个变量进行修改:

inputContentText :把记录用户刚才输入在聊天框内的内容清空。

dataSource:更新ListView的数据,因为我们刚添加了一条数据

 效果图如下(点击查看动态效果):

三:让新的数据永远展示在ListView的底部,其实就是想要一个新数据添加后,自动从下滚上来的效果。

Himi在做这一步的时候考虑过几种方式,下面介绍两种比较容易理解实现的方式:

a) 通过计算每个ListView的每一行View的高度来计算出位置,然后与ListView的视图高度进行对比,最后确定是否进行滚动操作(超出ListView的视图才应该滚动)

b) 根据官方ListView提供的renderFooter函数来完成!

renderFooter:

官方解释:“页头与页脚会在每次渲染过程中都重新渲染(如果提供了这些属性)。如果它们重绘的性能开销很大,把他们包装到一个StaticContainer或者其它恰当的结构中。页脚会永远在列表的最底部,而页头会在最顶部。”

粗糙的理解:每次绘制都会调用renderFooter这个绘制函数,而renderFooter就是绘制ListView最底部的位置。这里不是ListView视图最底部,而且ListView内容高度的最底部位置!!

因此我们通过ListView的renderFooter 绘制一个0高度的view,通过获取其Y位置,其实就是获取到了ListView内容高度底部的Y位置。

这里我们来介绍b方案,简单便捷。关于a方案,我想大家自己都很容易理解实现。

其实通过上面布局这段代码中,可以看到,Himi也已经对renderFooter的函数也绑到了自定义函数myRenderFooter上,所以我们只要在renderFooter中处理即可,如下代码:

 myRenderFooter(e){return <View onLayout={(e)=> {this.footerY= e.nativeEvent.layout.y;if (this.listHeight && this.footerY &&this.footerY>this.listHeight) {var scrollDistance = this.listHeight - this.footerY;this.refs._listView.scrollTo({y:-scrollDistance});}}}/>}

1. 首先我们先绘制一个0高度的view : return <View/>

2. 通过ListView的onLayout函数来获取并执行我们的滚动等逻辑。

onLayout 函数官方说明:

“当组件挂载或者布局变化的时候调用

参数为:{nativeEvent: { layout: {x, y, width, height}}}

这个事件会在布局计算完成后立即调用一次,不过收到此事件时新的布局可能还没有在屏幕上呈现,尤其是一个布局动画正在进行中的时候。”

3.  this.footerY= e.nativeEvent.layout.y; 

this.footerY 一开始说过了,用来记录0高度view的相对于ListView所在底部的Y位置。

注:这里Himi定义成this.footerY,原因是Himi也尝试了其他方式实现聊天滚动,为了方便使用。因此大家这里也可以定义var临时的即可。或者直接得到使用都无所谓啦~

4.  if( this.listHeight && this.footerY &&this.footerY>this.listHeight )

this.listHeight:与第三步类似,Himi通过ListView的onLayout函数获取到其高度记录在此变量上。

这里的判断目的:当最新的内容高度大雨ListView视图高度后,再开始执行滚动逻辑。

5. 最后的滚动逻辑代码段:

var scrollDistance = this.listHeight – this.footerY;
this.refs._listView.scrollTo({y:-scrollDistance});

首先通过当前ListView的视图高度-内容底部Y位置,获取到相差的举例 scrollDistance,这个距离就是我们需要ListView 滚动的举例,且取反滚动!

最后 _listView 是我们ListView的组件实例,因为ListView中也有ScrollView的特性,因此我们可以使用其:

scrollTo({x: 0, y: 0, animated: true})

对我们ListView进行动画滚动操作!

截此,我们的聊天、对话框完成,效果图如下(点击查看动态图):

   备注:每一行数据中Himi都定义了一个 isMe 的字段,这里来表示说话是对方还是自己。

isMe = true :  头像在右边,说话底为绿色。

isMe =false : 头像放左侧,说话底为白色。

其实这里Himi就是想做一些区分,模仿聊天的对话形式,所以加的变量。大家也可以各种自定义的啦~

【REACT NATIVE 系列教程之十三】利用LISTVIEW与TEXTINPUT制作聊天/对话框获取组件实例常用的两种方式...相关推荐

  1. 【REACT NATIVE 系列教程之十二】REACT NATIVE(JS/ES)与IOS(OBJECT-C)交互通信

    一用到跨平台的引擎必然要有引擎与各平台原生进行交互通信的需要.那么Himi先讲解React Native与iOS之间的通信交互. 本篇主要分为两部分讲解:(关于其中讲解的OC语法等不介绍,不懂的请自行 ...

  2. 【REACT NATIVE 系列教程之六】重写SHOULDCOMPONENTUPDATE指定组件是否进行重绘

    前几天,Himi 写练手项目时,遇到子更新父state中的某一个属性值,且对父进行重绘时,父包含的所有子组件都进行重绘 – -- 非常尴尬. 查阅了RN文档,终于在生命周期篇看到了想要的答案. 仔细看 ...

  3. 【REACT NATIVE 系列教程之四】刷新组件RENDER(重新渲染)的三种方式详解

    本站文章均为 李华明Himi 原创,转载务必在明显处注明:  转载自[黑米GameDev街区] 原文链接: http://www.himigame.com/react-native/2242.html ...

  4. 利用jquery的qrcode.js插件生成二维码的两种方式的使用

    2019独角兽企业重金招聘Python工程师标准>>> 利用jquery的qrcode.js插件生成二维码的额两种方式,canvas(即画布)方式和table方式(原文地址http: ...

  5. React+Redux系列教程

    2019独角兽企业重金招聘Python工程师标准>>> 参考项目:https://github.com/lewis617/react-redux-tutorial 参考项目下载地址: ...

  6. python微信爬取教程_PYTHON爬虫之旅系列教程之【利用Python开发微信公众平台一】...

    感谢大家的等待,好啦,都准备好瓜子.板凳,老司机要发车啦-- 本系列课程讲述"PYTHON爬虫之旅",具体大纲可参考:[PYTHON爬虫之旅]概要目录. 本节课讲述如何利用Pyth ...

  7. Provisioning Services 7.8 入门系列教程之十三 使用 Boot Device Management(BDM)

    续Provisioning Services 7.8 入门系列教程之十二 实现高可用性 可以使用 Boot Device Management 实用程序将 IP 和引导信息(引导设备)交付给目标设备, ...

  8. springboot2 war页面放在那_Spring Boot2 系列教程(三十三)整合 Spring Security

    Spring Security 是 Spring 家族中的一个安全管理框架,实际上,在 Spring Boot 出现之前,Spring Security 就已经发展了多年了,但是使用的并不多,安全管理 ...

  9. Google Earth Engine (GEE)——利用两种方式进行EVI指数(含函数的两种定义方式)

    如何快速使用波段进行指数的计算,我们这里利用两种方式进行EVI指数计算,一种是利用expression的方式进行分析,虽然两种方法的结算结果都一样,但是代码有多有少,大家可以参考使用,但是两者的作用对 ...

最新文章

  1. Pytorch入门.pptx
  2. 玩通信设备的,来这里学习
  3. 文献记录(part37)--A two-stage hybrid ant colony optimization for high-dimensional feature selection
  4. bufferedreader接收不到数据_PS4、Xbox手柄和Switch跨次元组队?全靠八位堂USB无线接收器...
  5. 吉林艺术学院监考人员被指为考生改画 学校回应
  6. BZOJ3236 [Ahoi2013]作业
  7. node-glob 正则表达式
  8. 实现一个简易版的微博,包含 client 和 server 两部分,并实现四个基础功能:关注、取关、发微博、获取用户微博列表
  9. 逃跑吧少年辅助快乐羊儿吧
  10. cacti安装和配置 技术交流群:146510248
  11. 愤怒的小鸟的html制作,如何做html5山寨版愤怒的小鸟
  12. Python的底气,是从哪儿来的?
  13. 99%的程序都没有考虑的网络异常
  14. navicat如何配置链接mysql并且新建数据库设置字符集和排列规则
  15. 甲骨文公司总部Oracle技术沙龙《Hadoop》邀请函-2013.8.31(周六)
  16. Java继承和多态实现例子
  17. php 网贷 源码,thinkphp仿百度钱包网贷借款源码
  18. 在线 服务器 web,web服务器是什么?
  19. Wordpress关闭所有评论
  20. 人工智能,机器学习与深度学习,到底是什么关系

热门文章

  1. 语音跟踪:信号分解、锁相、鸡尾酒会效应、基于PR的信号分离
  2. Python中的seek函数 指针 使用教程
  3. 我们并非生活在“虚幻世界”宇宙或是三维空间
  4. CloudCC CRM探讨如何建立完善的服务体系
  5. MathType可以在Word、PPT中插入矩阵吗
  6. Linux命令Find实例
  7. 读书笔记之Unix命令
  8. 很多想法、很多感慨。
  9. Symbian 资源文件解析
  10. JavaScript 渐变效果