标签:
ConnectionManager
已经设计完成了,它的价值需要在ChatActivity
中体现出来。
实现对ConnectionManager
各个状态的监听,当ConnectionManager
的状态有变化、收到发送的数据时,需要让ChatActivity
知道,它才能将各种变化反应到用户界面上。
ConnectionManager
定义了ConnectionListener
接口,状态变化、数据的接收可以通过这个接口获得。创建一个ConnectionListener
监听器,
public class ChatActivity extends AppCompatActivity {
......
private ConnectionManager.ConnectionListener mConnectionListener = new ConnectionManager.ConnectionListener() {
@Override
public void onConnectStateChange(int oldState, int State) {
}
@Override
public void onListenStateChange(int oldState, int State) {
}
@Override
public void onSendData(boolean suc, byte[] data) {
}
@Override
public void onReadData(byte[] data) {
}
};
......
}
因为监听器触发的函数不一定是在UI线程被调用的,例如onConnectStateChange()
,所以不能在监听器当中对界面做修改,必须把界面更新的任务交给UI线程进行。
安卓系统提供了Handler
的机制,让其它非UI线程能通过Handler
把界面更新的操作,从工作线程布置给主线程完成。
创建一个能在主线程当中工作的Handler,
public class ChatActivity extends AppCompatActivity {
......
private final static int MSG_SENT_DATA = 0;
private final static int MSG_RECEIVE_DATA = 1;
private final static int MSG_UPDATE_UI = 2;
//不使用参数创建Handler,说明这个Handler是给主线程服务的
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SENT_DATA: {
//UI线程处理发送成功的数据,
//把文字内容展示到主界面上
}
break;
case MSG_RECEIVE_DATA: {
//UI线程处理接收到的对方发送的数据,
//把文字内容展示到主界面上
}
break;
case MSG_UPDATE_UI: {
//更新界面上的菜单等显示状态
}
break;
}
}
};
......
}
将ConnectionManager
通知的内容,转交给主线程的Handler
处理,
private ConnectionManager.ConnectionListener mConnectionListener = new ConnectionManager.ConnectionListener() {
@Override
public void onConnectStateChange(int oldState, int State) {
//连接状态的变化通知给UI线程,请UI线程处理
mHandler.obtainMessage(MSG_UPDATE_UI).sendToTarget();
}
@Override
public void onListenStateChange(int oldState, int State) {
//监听状态的变化通知给UI线程,请UI线程处理
mHandler.obtainMessage(MSG_UPDATE_UI).sendToTarget();
}
@Override
public void onSendData(boolean suc, byte[] data) {
//将发送的数据交给UI线程,请UI线程处理
mHandler.obtainMessage(MSG_SENT_DATA, suc?1:0, 0, data).sendToTarget();
}
@Override
public void onReadData(byte[] data) {
//将收到的数据交给UI线程,请UI线程处理
mHandler.obtainMessage(MSG_RECEIVE_DATA, data).sendToTarget();
}
};
创建ConnectionManager
,添加监听,
private ConnectionManager mConnectionManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
......
mConnectionManager = new ConnectionManager(mConnectionListener);
......
}
当聊天应用运行起来的时候,需要开启对其它蓝牙设备可能接入的监听,
private ConnectionManager mConnectionManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
......
//开启监听
mConnectionManager.startListen();
......
}
当应用退出的时候,要断开可能存在的连接并停止监听,
@Override
protected void onDestroy() {
super.onDestroy();
//移除Handler中可能存在的各种任务
mHandler.removeMessages(MSG_UPDATE_UI);
mHandler.removeMessages(MSG_SENT_DATA);
mHandler.removeMessages(MSG_RECEIVE_DATA);
//停止监听
if(mConnectionManager != null) {
mConnectionManager.disconnect();
mConnectionManager.stopListen();
}
}
当用户点击菜单栏的启动连接
菜单项时,会启动DeviceListActivity
,让用户从刷新的列表中,选取一个希望连接的设备。用户选择后,会把选中设备的地址返回给ChatActivity
。
这样,就可以利用ConnectionManager
发起主动连接的请求了,
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == RESULT_CODE_BTDEVICE && resultCode == RESULT_OK) {
//取出传回来的地址
String deviceAddr = data.getStringExtra("DEVICE_ADDR");
//得到蓝牙设备的地址后,就可以通过ConnectionManager模块去连接设备
mConnectionManager.connect(deviceAddr);
}
}
之后,ConnectionManager
的各种状态变化,就会通过监听器ConnectionListener
,传递到ChatActivity
当中,据此更新界面就好了。
这种情况,并不需要用户去做任何点击的操作。
之后,ConnectionManager
的各种状态变化,就会通过监听器ConnectionListener
,传递到ChatActivity
当中,据此更新界面就好了。
我们之前已经通过ChatActivity
的onCreateOptionsMenu()
方法,把菜单项添加到了菜单栏。现在需要菜单项随着ConnectionManager
状态的变化,跟着做变化了。
当监听器的onConnectStateChange()
或者onListenStateChange
被触发后,我们将变化通过Handler
通知到了UI线程,
private ConnectionManager.ConnectionListener mConnectionListener = new ConnectionManager.ConnectionListener() {
@Override
public void onConnectStateChange(int oldState, int State) {
//连接状态的变化通知给UI线程,请UI线程处理
mHandler.obtainMessage(MSG_UPDATE_UI).sendToTarget();
}
@Override
public void onListenStateChange(int oldState, int State) {
//监听状态的变化通知给UI线程,请UI线程处理
mHandler.obtainMessage(MSG_UPDATE_UI).sendToTarget();
}
......
};
---------------------------------------------
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
......
case MSG_UPDATE_UI: {
//更新界面上的菜单等显示状态
updateUI();
}
break;
}
}
};
在更新UI到方法updateUI()
中,修改菜单项的显示,
private void updateUI()
{
if(mConnectionManager == null) {
return;
}
//默认情况下,禁止点击发送按钮和文字编辑框
if(mConnectionMenuItem == null) {
mMessageEditor.setEnabled(false);
mSendBtn.setEnabled(false);
return;
}
//设置成连接状态,允许点击发送按钮和文字编辑框
if(mConnectionManager.getCurrentConnectState() == ConnectionManager.CONNECT_STATE_CONNECTED) {
mConnectionMenuItem.setTitle(R.string.disconnect);
mMessageEditor.setEnabled(true);
mSendBtn.setEnabled(true);
}
//设置成正在连接状态,禁止点击发送按钮和文字编辑框
else if(mConnectionManager.getCurrentConnectState() == ConnectionManager.CONNECT_STATE_CONNECTING) {
mConnectionMenuItem.setTitle(R.string.cancel);
mMessageEditor.setEnabled(false);
mSendBtn.setEnabled(false);
}
//设置成未连接状态,禁止点击发送按钮和文字编辑框
else if(mConnectionManager.getCurrentConnectState() == ConnectionManager.CONNECT_STATE_IDLE) {
mConnectionMenuItem.setTitle(R.string.connect);
mMessageEditor.setEnabled(false);
mSendBtn.setEnabled(false);
}
}
菜单项的响应也需要根据当前的连接状态,做进一步的修改,
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId())
{
case R.id.connect_menu: {
//如果当前处于连接状态,点击后就取消当前连接
if(mConnectionManager.getCurrentConnectState() == ConnectionManager.CONNECT_STATE_CONNECTED) {
mConnectionManager.disconnect();
}
//如果当前处于正在连接,点击后就取消当前连接
else if(mConnectionManager.getCurrentConnectState() == ConnectionManager.CONNECT_STATE_CONNECTING) {
mConnectionManager.disconnect();
}
//如果当前处于未连接状态,点击后就启动查找当前可连接设备的Activity
else if(mConnectionManager.getCurrentConnectState() == ConnectionManager.CONNECT_STATE_IDLE) {
Intent i = new Intent(ChatActivity.this, DeviceListActivity.class);
startActivityForResult(i, RESULT_CODE_BTDEVICE);
}
}
return true;
......
}
}
聊天文字发送成功或者接收到对方发来的文字时,要显示到列表中。为此,我们需要专门设计一个Adapter
来展示它们。
首先定义一个记录每条信息的数据结构ChatMessage
,每一条消息要注明是由谁发来的,是自己还是对方,
public class ChatMessage {
//主动发出的消息
static public final int MSG_SENDER_ME = 0;
//接收到的消息
static public final int MSG_SENDER_OTHERS = 1;
public int messageSender;
public String messageContent;
}
我们采用类似微信聊天的样子来展示聊天内容。每条消息的背景图片是9patch形式的PNG图片,将它们放在res\drawable
目录中。针对不同的屏幕像素密度,设计了对应的图片,放到对应的drawable
目录下就行了。例如为xxhdip设计的背景图片就放在res\drawable-xxhdip
目录中。
这些图片可以在示例代码中获得。
定义展示对方发来信息的布局-others_list_item.xml
,
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal" android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="58dp"
android:padding="5dp">
<!--显示类似头像的图片-->
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_device_bluetooth"/>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"> ---设置为1,让文字显示尽情利用右边区域
<!--显示文字内容-->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:id="@+id/message_content"
android:padding="5dp"
android:textSize="16sp"
android:gravity="center_vertical"
android:background="@drawable/others"/>
</FrameLayout>
</LinearLayout>
定义展示自己发送信息的布局-me_list_item.xml
,
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal" android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="58dp"
android:padding="5dp"
android:gravity="right">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"> ---设置为1,让文字显示尽情利用左边区域
<!--显示文字内容-->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/message_content"
android:gravity="center_vertical"
android:layout_gravity="right"
android:padding="5dp"
android:textSize="16sp"
android:background="@drawable/me"/>
</FrameLayout>
<!--显示类似头像的图片-->
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_device_bluetooth"/>
</LinearLayout>
定义Adapter
-MessageAdapter
,
public class MessageAdapter extends ArrayAdapter<ChatMessage> {
private final LayoutInflater mInflater;
private int mResourceMe;
private int mResourceOthers;
//要指定自己发送的消息显示用的布局-me_list_item,
//以及对方发送消息显示用的布局-others_list_item
public MessageAdapter(Context context, int resourceMe, int resourceOthers) {
super(context, 0);
mInflater = LayoutInflater.from(context);
mResourceMe = resourceMe;
mResourceOthers = resourceOthers;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ChatMessage message = getItem(position);
//根据消息类型的不同,使用不同的布局作为消息项
convertView = mInflater.inflate(message.messageSender == ChatMessage.MSG_SENDER_ME ? mResourceMe : mResourceOthers, parent, false);
//显示消息的内容
TextView name = (TextView) convertView.findViewById(R.id.message_content);
name.setText(message.messageContent);
return convertView;
}
}
使用聊天列表,
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
......
mMessageListView = (ListView) findViewById(R.id.message_list);
MessageAdapter adapter = new MessageAdapter(this, R.layout.me_list_item, R.layout.others_list_item);
mMessageListView.setAdapter(adapter);
......
}
当连接建立以后,禁止点击的发送按钮和文字编辑框将被解禁。再文字编辑框中编辑好文字,点击发送按钮,就能将文字发送出去了。
为按钮创建监听器,当点击后,获取文字编辑框中的数据,再使用ConnectionManager
提供的接口,把数据发送出去,
private View.OnClickListener mSendClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
//获取要发送的文字内容
String content = mMessageEditor.getText().toString();
if(content != null) {
content = content.trim();
if(content.length() > 0) {
//利用ConnectionManager发送数据
boolean ret = mConnectionManager.sendData(content.getBytes());
if(!ret) {
Toast.makeText(ChatActivity.this, R.string.send_fail, Toast.LENGTH_SHORT).show();
}
}
}
}
};
注册监听函数,
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
......
mSendBtn = (ImageButton) findViewById(R.id.send_btn);
mSendBtn.setOnClickListener(mSendClickListener);
......
}
在ConnectionListener
的onSendData
回调方法中,将发送结果传递给UI线程,让UI线程把聊天内容更新到消息列表中,
private ConnectionManager.ConnectionListener mConnectionListener = new ConnectionManager.ConnectionListener() {
......
@Override
public void onSendData(boolean suc, byte[] data) {
//发送的结果传递给UI线程,文字的二进制内容包含在data参数中
mHandler.obtainMessage(MSG_SENT_DATA, suc?1:0, 0, data).sendToTarget();
}
......
};
---------------------------------------------
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SENT_DATA: {
//获取发送的文字内容
byte [] data = (byte []) msg.obj;
boolean suc = msg.arg1 == 1;
if(data != null && suc) {
//发送成功后创建消息
ChatMessage chatMsg = new ChatMessage();
chatMsg.messageSender = ChatMessage.MSG_SENDER_ME;
chatMsg.messageContent = new String(data);
//将消息展示到消息列表中
MessageAdapter adapter = (MessageAdapter) mMessageListView.getAdapter();
adapter.add(chatMsg);
adapter.notifyDataSetChanged();
mMessageEditor.setText("");
}
}
break;
......
}
}
};
当接收到对方发来的消息时,ConnectionListener
的onReadData
回调方法,将发送结果传递给UI线程,让UI线程把聊天内容更新到消息列表中,
private ConnectionManager.ConnectionListener mConnectionListener = new ConnectionManager.ConnectionListener() {
......
@Override
public void onReadData(byte[] data) {
//接收到的内容传递给UI线程,文字的二进制内容包含在data参数中
mHandler.obtainMessage(MSG_RECEIVE_DATA, data).sendToTarget();
}
};
-------------------------------------------------
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
......
case MSG_RECEIVE_DATA: {
byte [] data = (byte []) msg.obj;
if(data != null) {
ChatMessage chatMsg = new ChatMessage();
chatMsg.messageSender = ChatMessage.MSG_SENDER_OTHERS;
chatMsg.messageContent = new String(data);
//将消息展示到消息列表中
MessageAdapter adapter = (MessageAdapter) mMessageListView.getAdapter();
adapter.add(chatMsg);
adapter.notifyDataSetChanged();
}
}
break;
......
}
}
};
至此,蓝牙聊天的整个流程都得以实现了。
手把手教你做蓝牙聊天应用(五)-界面使用ConnectionManager
标签:
原文地址:http://blog.csdn.net/anddlecn/article/details/51916792