iPhone蓝牙编程之实现语音聊天
2012-11-04 10:36
369 查看
感谢原创人。致敬!!
【IT168技术】在我之前的iPhone文章中,我们已经介绍过使用GameKit框架在两个设备之间通过蓝牙通信,在这篇文章中,我将为大家介绍GameKit框架另一个很酷的功能 — 语音聊天。
Gamekit中的语音聊天服务允许两台iPhone/iPod Touch之间建立语音聊天,语音聊天要么通过互联网,要么通过蓝牙实现,你将在本文中看到如何通过蓝牙通信信道实现语音聊天。
创建项目
首先使用Xcode创建一个新的基于视图的iPhone应用程序,取名为Bluetooth,在Xcode的框架组上点击右键,选择“增加”,从现有框架中选择一个增加到项目上,这里选择GameKit.framework,同时再增加一个AVFoundation框架。
在BluetoothViewController.h文件中,增加下面的代码:
1 #import
<UIKit/UIKit.h>
2 #import
<GameKit/GameKit.h>
3 #import
<AVFoundation/AVFoundation.h>
4 @interface BluetoothViewController : UIViewController
5 <GKVoiceChatClient>
{
6
7 GKSession
*currentSession;
8
9 IBOutlet
UIButton *connect;
10 IBOutlet
UIButton *disconnect;
11 }
12 @property (nonatomic, retain) GKSession
*currentSession;
13 @property (nonatomic, retain) UIButton
*connect;
14 @property (nonatomic, retain) UIButton
*disconnect;
15
16 -(IBAction) btnMute:(id) sender;
17 -(IBAction) btnUnmute:(id) sender;
18 -(IBAction) btnConnect:(id) sender;
19 -(IBAction) btnDisconnect:(id) sender;
20 @end
21
拖动一个wav文件(如无特殊说明,指beep.wav)到Xcode的Resources文件夹上。
双击BluetoothViewController.xib文件在Interface Builder编辑它,使用三个圆形按钮填充到视图窗口中,如图1所示。
图 1 使用三个圆形按钮填充视图窗口
在BluetoothViewController.xib窗口中,执行以下连接:
按住CTRL键,点击文件所有者项目,将其拖放到Connect按钮上,选择连接;
按住CTRL键,点击文件所有者项目,将其拖放到Disconnect按钮上,选择断开连接;
按住CTRL键,点击Connect按钮,将其拖放到文件所有者项目上,选择btnConnect;
按住CTRL键,点击Disconnect按钮,将其拖放到文件所有者项目上,选择btnDisconnect;
在Mute按钮上点击右键,将Touch Down事件连接到文件所有者项目上,选择btnMute;
在Mute按钮上点击右键,将Touch Up Inside事件连接到文件所有者项目上,选择btnUnmute;
为了验证所有连接是否正确,在文件所有者项目上点击右键,查看其连接,如图2所示。
图 2 验证出口和行为的连接
在BluetoothViewController.m文件中,增加下面的代码:
1 #import
"BluetoothViewController.h"
2 #import
<GameKit/GameKit.h>
3 #import
<AVFoundation/AVFoundation.h>
4
5 @implementation BluetoothViewController
6 @synthesize currentSession;
7 @synthesize connect;
8 @synthesize disconnect;
9
10 GKPeerPickerController
*picker;
11 NSString
*recorderFilePath;
12
13 AVAudioPlayer
*audioPlayer;
14
15 - (void)viewDidLoad {
16
17 [connect setHidden:NO];
18 [disconnect setHidden:YES];
19
20 [super viewDidLoad];
21 }
22
23 - (IBAction) btnConnect:(id) sender {
24
25 //---选择一个附近的蓝牙设备---
26 picker
= [[GKPeerPickerController alloc] init];
27 picker.delegate
= self;
28 picker.connectionTypesMask
= GKPeerPickerConnectionTypeNearby;
29
30 [connect setHidden:YES];
31 [disconnect setHidden:NO];
32
33 [picker show];
34
35 }
36
37 -(IBAction) btnDisconnect:(id) sender {
38
39 //---从其它设备断开连接---
40 [self.currentSession disconnectFromAllPeers];
41 [self.currentSession release];
42 currentSession
= nil;
43
44 [connect setHidden:NO];
45 [disconnect setHidden:YES];
46
47 }
48
49 - (void)peerPickerController:(GKPeerPickerController
*)picker
50 didConnectPeer:(NSString
*)peerID
51 toSession: (GKSession
*) session {
52
53 self.currentSession
= session;
54 session.delegate
= self;
55 [session setDataReceiveHandler: self withContext:nil];
56 picker.delegate
= nil;
57 [picker dismiss];
58 [picker autorelease];
59
60 }
61
62 - (void)peerPickerControllerDidCancel:(GKPeerPickerController
*)picker {
63
64 picker.delegate
= nil;
65 [picker autorelease];
66
67 [connect setHidden:NO];
68 [disconnect setHidden:YES];
69
70 }
71
72 -(IBAction) btnMute:(id) sender {
73
74 //---静音语音聊天---
75 [GKVoiceChatService defaultVoiceChatService].microphoneMuted
= YES;
76
77 }
78
79 -(IBAction) btnUnmute:(id) sender {
80
81 //---取消静音---
82 [GKVoiceChatService defaultVoiceChatService].microphoneMuted
= NO;
83
84 }
85
86 //---返回一个表示本地用户的唯一ID
---
87 -(NSString
*) participantID
88 {
89 return currentSession.peerID;
90 }
91
92 -(void) voiceChatService:(GKVoiceChatService
*) voiceChatService
93 sendData:(NSData
*) data
94 toParticipantID:(NSString
*)participantID {
95
96 [currentSession sendData:data toPeers:
97 [NSArray arrayWithObject:participantID]
98 withDataMode:GKSendDataReliable
error:nil];
99
100 }
101
102 - (void)session:(GKSession
*)session
103 peer:(NSString
*)peerID
104 didChangeState:(GKPeerConnectionState)state {
105
106 switch (state)
107 {
108 case GKPeerStateConnected:
109 {
110 //---播放一个音频文件---
111 NSString
*soundFilePath
= [[NSBundle mainBundle]
112 pathForResource:@"beep" ofType:@"wav"];
113
114 NSURL
*fileURL
= [[NSURL alloc]
115 initFileURLWithPath: soundFilePath];
116
117 AVAudioPlayer
*audioPlayer
=
118 [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL
119
error:nil];
120
121 [fileURL release];
122 [audioPlayer play];
123
124 NSError
*error;
125 AVAudioSession
*audioSession
=
126 [AVAudioSession sharedInstance];
127
128 if (![audioSession
129 setCategory:AVAudioSessionCategoryPlayAndRecord
130 error:&error])
{
131 NSLog(@"Error setting the AVAudioSessionCategoryPlayAndRecord category: %@",
132 [error localizedDescription]);
133 }
134
135 if (![audioSession setActive: YES
error:
&error]) {
136 NSLog(@"Error activating audioSession: %@",
137 [error description]);
138 }
139
140 [GKVoiceChatService defaultVoiceChatService].client
= self;
141
142 //---初始化语音聊天---
143 if (![[GKVoiceChatService defaultVoiceChatService]
144 startVoiceChatWithParticipantID:peerID
error:&error]) {
145 NSLog(@"Error starting startVoiceChatWithParticipantID: %@",
146 [error userInfo]);
147 }
148 } break;
149
150 case GKPeerStateDisconnected:
151 {
152 [[GKVoiceChatService defaultVoiceChatService]
153 stopVoiceChatWithParticipantID:peerID];
154
155 [self.currentSession release];
156 currentSession
= nil;
157
158 [connect setHidden:NO];
159 [disconnect setHidden:YES];
160
161 } break;
162 }
163 }
164
165 - (void) receiveData:(NSData
*)data
166 fromPeer:(NSString
*)peer
167 inSession:(GKSession
*)session
168 context:(void
*)context {
169
170 //---初始化完成后开始语音聊天---
171 [[GKVoiceChatService defaultVoiceChatService]
172 receivedData:data fromParticipantID:peer];
173
174 }
175
176 - (void)dealloc {
177
178 if (currentSession) [currentSession release];
179 [connect release];
180 [disconnect release];
181
182 [super dealloc];
183 }
184 @end
185
理解语音聊天是如何工作的
当两个蓝牙设备连接时,首先播放beep.wav文件发出嘟嘟声,然后启动语音会话(通过session:peer:didChangeState:方法实现)。
1 NSString
*soundFilePath
= [[NSBundle mainBundle]
2 pathForResource:@"beep" ofType:@"wav"];
3
4 NSURL
*fileURL
= [[NSURL alloc]
5 initFileURLWithPath: soundFilePath];
6
7 AVAudioPlayer
*audioPlayer
=
8 [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL
9
error:nil];
10
11 [fileURL release];
12 [audioPlayer play];
13
14 NSError
*error;
15 AVAudioSession
*audioSession
=
16 [AVAudioSession sharedInstance];
17
18 if (![audioSession
19 setCategory:AVAudioSessionCategoryPlayAndRecord
20 error:&error])
{
21 NSLog(@"Error setting the AVAudioSessionCategoryPlayAndRecord category: %@",
22 [error localizedDescription]);
23 }
24
25 if (![audioSession setActive: YES
error:
&error]) {
26 NSLog(@"Error activating audioSession: %@",
27 [error description]);
28 }
29
30 [GKVoiceChatService defaultVoiceChatService].client
= self;
31
提示:有趣的是,如果你不启动音频播放器,语音聊天不能正常工作。
然后检索GKVoiceChatService类的单一实例,调用它的startVoiceChatWithParticipantID:error:方法启动语音聊天。
1 if (![[GKVoiceChatService defaultVoiceChatService]
2 startVoiceChatWithParticipantID:peerID
error:&error]) {
3 NSLog(@"Error starting startVoiceChatWithParticipantID: %@",
4 [error userInfo]);
5 }
6
调用startVoiceChatWithParticipantID:error:方法将会请求voiceChatService:sendData:toParticipantID:方法,它将会使用当前的蓝牙会话发送配置数据到其它设备。
1 -(void) voiceChatService:(GKVoiceChatService
*) voiceChatService
2 sendData:(NSData
*) data
3 toParticipantID:(NSString
*)participantID {
4
5 [currentSession sendData:data toPeers:
6 [NSArray arrayWithObject:participantID]
7 withDataMode:GKSendDataReliable
error:nil];
8
9 }
10
其它连接的设备接收到发来的配置数据后,通过调用receivedData:fromParticipantID:方法启动语音聊天服务。
GKVoiceChatService使用两个设备之间交互的配置信息创建它自己的连接传输语音数据,还可以将microphoneMuted属性的值设为YES使麦克风静音。
1 [GKVoiceChatService defaultVoiceChatService].microphoneMuted
= YES;
测试应用程序
在开始测试之前,先要把应用程序两台iPhone或iPod Touch上,对于iPod Touch,你需要使用一个外置麦克风,因为它默认是不带有麦克风的,一个好选择是Griffin iTalk Pro,如图3所示,它是iPod Touch麦克风及其零配件插入的基座,另外也支持苹果头戴式耳机及其配件。
应用程序部署到设备上后,启动应用程序,按下Connect按钮使用蓝牙相互连接,当两个设备连接好后,就可以开始聊天了,如果要临时静音,按下Mute按钮一直不放即可,放开Mute按钮后,声音就又恢复了。
在这篇文章中,你看到了如何使用Gamekit框架提供的GKVoiceChatService类在两个设备之间无缝地建立语音通信,关于两个设备之间的语音是如何传输的暂时没有必要知道,所有需要了解的就是要知道调用什么方法初始化语音聊天,另外你还要知道,语音聊天不仅可以通过蓝牙通信信道实现,在其它任何通信信道上都可以实现,实际上,如果使用TCP/IP连接两台设备,也可以实现语音聊天,在今后的文章中,我将会涉及。
【IT168技术】在我之前的iPhone文章中,我们已经介绍过使用GameKit框架在两个设备之间通过蓝牙通信,在这篇文章中,我将为大家介绍GameKit框架另一个很酷的功能 — 语音聊天。
Gamekit中的语音聊天服务允许两台iPhone/iPod Touch之间建立语音聊天,语音聊天要么通过互联网,要么通过蓝牙实现,你将在本文中看到如何通过蓝牙通信信道实现语音聊天。
创建项目
首先使用Xcode创建一个新的基于视图的iPhone应用程序,取名为Bluetooth,在Xcode的框架组上点击右键,选择“增加”,从现有框架中选择一个增加到项目上,这里选择GameKit.framework,同时再增加一个AVFoundation框架。
在BluetoothViewController.h文件中,增加下面的代码:
1 #import
<UIKit/UIKit.h>
2 #import
<GameKit/GameKit.h>
3 #import
<AVFoundation/AVFoundation.h>
4 @interface BluetoothViewController : UIViewController
5 <GKVoiceChatClient>
{
6
7 GKSession
*currentSession;
8
9 IBOutlet
UIButton *connect;
10 IBOutlet
UIButton *disconnect;
11 }
12 @property (nonatomic, retain) GKSession
*currentSession;
13 @property (nonatomic, retain) UIButton
*connect;
14 @property (nonatomic, retain) UIButton
*disconnect;
15
16 -(IBAction) btnMute:(id) sender;
17 -(IBAction) btnUnmute:(id) sender;
18 -(IBAction) btnConnect:(id) sender;
19 -(IBAction) btnDisconnect:(id) sender;
20 @end
21
拖动一个wav文件(如无特殊说明,指beep.wav)到Xcode的Resources文件夹上。
双击BluetoothViewController.xib文件在Interface Builder编辑它,使用三个圆形按钮填充到视图窗口中,如图1所示。
图 1 使用三个圆形按钮填充视图窗口
在BluetoothViewController.xib窗口中,执行以下连接:
按住CTRL键,点击文件所有者项目,将其拖放到Connect按钮上,选择连接;
按住CTRL键,点击文件所有者项目,将其拖放到Disconnect按钮上,选择断开连接;
按住CTRL键,点击Connect按钮,将其拖放到文件所有者项目上,选择btnConnect;
按住CTRL键,点击Disconnect按钮,将其拖放到文件所有者项目上,选择btnDisconnect;
在Mute按钮上点击右键,将Touch Down事件连接到文件所有者项目上,选择btnMute;
在Mute按钮上点击右键,将Touch Up Inside事件连接到文件所有者项目上,选择btnUnmute;
为了验证所有连接是否正确,在文件所有者项目上点击右键,查看其连接,如图2所示。
图 2 验证出口和行为的连接
在BluetoothViewController.m文件中,增加下面的代码:
1 #import
"BluetoothViewController.h"
2 #import
<GameKit/GameKit.h>
3 #import
<AVFoundation/AVFoundation.h>
4
5 @implementation BluetoothViewController
6 @synthesize currentSession;
7 @synthesize connect;
8 @synthesize disconnect;
9
10 GKPeerPickerController
*picker;
11 NSString
*recorderFilePath;
12
13 AVAudioPlayer
*audioPlayer;
14
15 - (void)viewDidLoad {
16
17 [connect setHidden:NO];
18 [disconnect setHidden:YES];
19
20 [super viewDidLoad];
21 }
22
23 - (IBAction) btnConnect:(id) sender {
24
25 //---选择一个附近的蓝牙设备---
26 picker
= [[GKPeerPickerController alloc] init];
27 picker.delegate
= self;
28 picker.connectionTypesMask
= GKPeerPickerConnectionTypeNearby;
29
30 [connect setHidden:YES];
31 [disconnect setHidden:NO];
32
33 [picker show];
34
35 }
36
37 -(IBAction) btnDisconnect:(id) sender {
38
39 //---从其它设备断开连接---
40 [self.currentSession disconnectFromAllPeers];
41 [self.currentSession release];
42 currentSession
= nil;
43
44 [connect setHidden:NO];
45 [disconnect setHidden:YES];
46
47 }
48
49 - (void)peerPickerController:(GKPeerPickerController
*)picker
50 didConnectPeer:(NSString
*)peerID
51 toSession: (GKSession
*) session {
52
53 self.currentSession
= session;
54 session.delegate
= self;
55 [session setDataReceiveHandler: self withContext:nil];
56 picker.delegate
= nil;
57 [picker dismiss];
58 [picker autorelease];
59
60 }
61
62 - (void)peerPickerControllerDidCancel:(GKPeerPickerController
*)picker {
63
64 picker.delegate
= nil;
65 [picker autorelease];
66
67 [connect setHidden:NO];
68 [disconnect setHidden:YES];
69
70 }
71
72 -(IBAction) btnMute:(id) sender {
73
74 //---静音语音聊天---
75 [GKVoiceChatService defaultVoiceChatService].microphoneMuted
= YES;
76
77 }
78
79 -(IBAction) btnUnmute:(id) sender {
80
81 //---取消静音---
82 [GKVoiceChatService defaultVoiceChatService].microphoneMuted
= NO;
83
84 }
85
86 //---返回一个表示本地用户的唯一ID
---
87 -(NSString
*) participantID
88 {
89 return currentSession.peerID;
90 }
91
92 -(void) voiceChatService:(GKVoiceChatService
*) voiceChatService
93 sendData:(NSData
*) data
94 toParticipantID:(NSString
*)participantID {
95
96 [currentSession sendData:data toPeers:
97 [NSArray arrayWithObject:participantID]
98 withDataMode:GKSendDataReliable
error:nil];
99
100 }
101
102 - (void)session:(GKSession
*)session
103 peer:(NSString
*)peerID
104 didChangeState:(GKPeerConnectionState)state {
105
106 switch (state)
107 {
108 case GKPeerStateConnected:
109 {
110 //---播放一个音频文件---
111 NSString
*soundFilePath
= [[NSBundle mainBundle]
112 pathForResource:@"beep" ofType:@"wav"];
113
114 NSURL
*fileURL
= [[NSURL alloc]
115 initFileURLWithPath: soundFilePath];
116
117 AVAudioPlayer
*audioPlayer
=
118 [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL
119
error:nil];
120
121 [fileURL release];
122 [audioPlayer play];
123
124 NSError
*error;
125 AVAudioSession
*audioSession
=
126 [AVAudioSession sharedInstance];
127
128 if (![audioSession
129 setCategory:AVAudioSessionCategoryPlayAndRecord
130 error:&error])
{
131 NSLog(@"Error setting the AVAudioSessionCategoryPlayAndRecord category: %@",
132 [error localizedDescription]);
133 }
134
135 if (![audioSession setActive: YES
error:
&error]) {
136 NSLog(@"Error activating audioSession: %@",
137 [error description]);
138 }
139
140 [GKVoiceChatService defaultVoiceChatService].client
= self;
141
142 //---初始化语音聊天---
143 if (![[GKVoiceChatService defaultVoiceChatService]
144 startVoiceChatWithParticipantID:peerID
error:&error]) {
145 NSLog(@"Error starting startVoiceChatWithParticipantID: %@",
146 [error userInfo]);
147 }
148 } break;
149
150 case GKPeerStateDisconnected:
151 {
152 [[GKVoiceChatService defaultVoiceChatService]
153 stopVoiceChatWithParticipantID:peerID];
154
155 [self.currentSession release];
156 currentSession
= nil;
157
158 [connect setHidden:NO];
159 [disconnect setHidden:YES];
160
161 } break;
162 }
163 }
164
165 - (void) receiveData:(NSData
*)data
166 fromPeer:(NSString
*)peer
167 inSession:(GKSession
*)session
168 context:(void
*)context {
169
170 //---初始化完成后开始语音聊天---
171 [[GKVoiceChatService defaultVoiceChatService]
172 receivedData:data fromParticipantID:peer];
173
174 }
175
176 - (void)dealloc {
177
178 if (currentSession) [currentSession release];
179 [connect release];
180 [disconnect release];
181
182 [super dealloc];
183 }
184 @end
185
理解语音聊天是如何工作的
当两个蓝牙设备连接时,首先播放beep.wav文件发出嘟嘟声,然后启动语音会话(通过session:peer:didChangeState:方法实现)。
1 NSString
*soundFilePath
= [[NSBundle mainBundle]
2 pathForResource:@"beep" ofType:@"wav"];
3
4 NSURL
*fileURL
= [[NSURL alloc]
5 initFileURLWithPath: soundFilePath];
6
7 AVAudioPlayer
*audioPlayer
=
8 [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL
9
error:nil];
10
11 [fileURL release];
12 [audioPlayer play];
13
14 NSError
*error;
15 AVAudioSession
*audioSession
=
16 [AVAudioSession sharedInstance];
17
18 if (![audioSession
19 setCategory:AVAudioSessionCategoryPlayAndRecord
20 error:&error])
{
21 NSLog(@"Error setting the AVAudioSessionCategoryPlayAndRecord category: %@",
22 [error localizedDescription]);
23 }
24
25 if (![audioSession setActive: YES
error:
&error]) {
26 NSLog(@"Error activating audioSession: %@",
27 [error description]);
28 }
29
30 [GKVoiceChatService defaultVoiceChatService].client
= self;
31
提示:有趣的是,如果你不启动音频播放器,语音聊天不能正常工作。
然后检索GKVoiceChatService类的单一实例,调用它的startVoiceChatWithParticipantID:error:方法启动语音聊天。
1 if (![[GKVoiceChatService defaultVoiceChatService]
2 startVoiceChatWithParticipantID:peerID
error:&error]) {
3 NSLog(@"Error starting startVoiceChatWithParticipantID: %@",
4 [error userInfo]);
5 }
6
调用startVoiceChatWithParticipantID:error:方法将会请求voiceChatService:sendData:toParticipantID:方法,它将会使用当前的蓝牙会话发送配置数据到其它设备。
1 -(void) voiceChatService:(GKVoiceChatService
*) voiceChatService
2 sendData:(NSData
*) data
3 toParticipantID:(NSString
*)participantID {
4
5 [currentSession sendData:data toPeers:
6 [NSArray arrayWithObject:participantID]
7 withDataMode:GKSendDataReliable
error:nil];
8
9 }
10
其它连接的设备接收到发来的配置数据后,通过调用receivedData:fromParticipantID:方法启动语音聊天服务。
GKVoiceChatService使用两个设备之间交互的配置信息创建它自己的连接传输语音数据,还可以将microphoneMuted属性的值设为YES使麦克风静音。
1 [GKVoiceChatService defaultVoiceChatService].microphoneMuted
= YES;
测试应用程序
在开始测试之前,先要把应用程序两台iPhone或iPod Touch上,对于iPod Touch,你需要使用一个外置麦克风,因为它默认是不带有麦克风的,一个好选择是Griffin iTalk Pro,如图3所示,它是iPod Touch麦克风及其零配件插入的基座,另外也支持苹果头戴式耳机及其配件。
应用程序部署到设备上后,启动应用程序,按下Connect按钮使用蓝牙相互连接,当两个设备连接好后,就可以开始聊天了,如果要临时静音,按下Mute按钮一直不放即可,放开Mute按钮后,声音就又恢复了。
在这篇文章中,你看到了如何使用Gamekit框架提供的GKVoiceChatService类在两个设备之间无缝地建立语音通信,关于两个设备之间的语音是如何传输的暂时没有必要知道,所有需要了解的就是要知道调用什么方法初始化语音聊天,另外你还要知道,语音聊天不仅可以通过蓝牙通信信道实现,在其它任何通信信道上都可以实现,实际上,如果使用TCP/IP连接两台设备,也可以实现语音聊天,在今后的文章中,我将会涉及。
相关文章推荐
- iPhone蓝牙编程之实现语音聊天 .
- android 轻松实现在线即时聊天【图片、语音、表情、文字】等!含源码!
- 蓝牙语音功能的实现
- 基于IMX6平台+RTL8723BU模块的蓝牙语音通话的实现——音频基础概念
- VC编程实现文本语音转换
- java网络编程一:模拟qq聊天功能,实现一对一聊天
- 实现局域网语音聊天工具的雏形方案
- iOS网络编程实践--NSStream实现TCP Socket iPhone客户端
- iphone聊天用几种图形(表情)与文字混排的实现与比较
- Java Socket编程实现聊天小案例
- Unity 实现简单的语音聊天 [iOS版本]
- 模仿微信语音聊天功能(3) 核心部分,录音功能的实现
- iphone之使用讯飞语音sdk实现语音识别功能
- Android之蓝牙 聊天程序的设计和实现 bluetooth chat
- android仿微信聊天页面,以及实现语音功能
- Android 蓝牙开发实例--蓝牙聊天程序的设计和实现
- iPhone 掌握蓝牙通信编程
- Qt实现基于G.729A(G729A)的语音聊天
- Java一步一脚印—通过简单的TCP网络编程实现局域网的聊天对话
- ListView、RecyclerView 两种方式实现聊天界面支持文字、表情、图片和语音信息,支持下拉加载更多