本次主要整合所有P2P相关的内容,实现两人聊天界面的具体功能。
P2PChatActivity
1.onCreate:主要分为5部分:初始化当前聊天会话、初始化标题栏、初始化消息列表、初始化监听器、加载历史消息记录
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setStatusBarColor(R.color.app_blue_color);
setContentView(R.layout.activity_p2p_chat);
createChatSession();
initTitle();
ButterKnife.bind(this);
mInputLayout.setLayoutListener(this);
mInputLayout.bindInputLayout(this, mRecyclerView);
initMsgList();
initListener();
// 注册监听
NIMClient.getService(MsgServiceObserve.class)
.observeReceiveMessage(mMsgReceiveObserver, true);
NIMClient.getService(MsgServiceObserve.class)
.observeMsgStatus(mMsgStatusObserver, true);
NIMClient.getService(MsgServiceObserve.class)
.observeAttachmentProgress(mProgressObserver, true);
// 加载历史消息数据
loadMessage();
}
2.初始化当前聊天会话
private void createChatSession() {
chatInfo = (NimUserInfo) getIntent().getSerializableExtra("NimUserInfo");
NimUserInfo myInfo = NimUserHandler.getInstance().getUserInfo();
mChatSession = new ChatSession();
mChatSession.setSessionId(chatInfo.getAccount());
mChatSession.setSessionType(SessionTypeEnum.P2P);
mChatSession.setChatAccount(chatInfo.getAccount());
mChatSession.setMyAccount(myInfo.getAccount());
mChatSession.setChatInfo(chatInfo);
mChatSession.setMyInfo(myInfo);
mChatHandler = new ChatMsgHandler(this, mChatSession);
}
3.初始化标题栏:
private void initTitle() {
if (!TextUtils.isEmpty(mChatSession.getChatNick())) {
setTitleBar(mChatSession.getChatNick(), true, true);
} else {
setTitleBar(mChatSession.getChatInfo().getName(), true, true);
}
mIvMenu.setImageResource(R.mipmap.people);
// 打开详细资料界面
mIvMenu.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(P2PChatActivity.this, FriendInfoActivity.class);
intent.putExtra("NimUserInfo", chatInfo);
intent.putExtra("FLAG", FriendInfoActivity.FLAG_PURE_SHOW_FRIEND);
startActivity(intent);
}
});
}
4.初始化消息列表:
private void initMsgList() {
mLayoutManager = (LinearLayoutManager) mRecyclerView.getLayoutManager();
mMsgList = new ArrayList<>();
mListAdapter = new MessageListAdapter(this, mMsgList, mChatSession);
mListAdapter.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(RViewHolder holder, IMMessage message) {
switch (message.getMsgType()) {
case image:
showAttachOnActivity(ShowImageActivity.class, message);
break;
case audio:
playAudio(holder, message);
break;
case video:
//showAttachOnActivity(ShowVideoActivity.class, message);
break;
case location:
showAttachOnActivity(ShowLocActivity.class, message);
break;
}
}
});
mRecyclerView.setAdapter(mListAdapter);
mRecyclerView.setLoadingListener(new MsgRecyclerView.OnLoadingListener() {
@Override
public void loadPreMessage() {
loadMessage();
}
});
}
5.初始化各类消息监听
private void initListener() {
mRecyclerView.setOnTouchListener(new MyTouchListener());
final View decorView = getWindow().getDecorView();
decorView.getViewTreeObserver()
.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
private int previousKeyboardHeight = 0;
@Override
public void onGlobalLayout() {
Rect rect = new Rect();
getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
int displayHeight = rect.bottom;
int height = decorView.getHeight();
int keyboardHeight = height - displayHeight;
if (previousKeyboardHeight != keyboardHeight) {
boolean hide = (double) displayHeight / height > 0.8;
if (!hide){
//点击文本框后弹出键盘,消息滚到最后一条
mLayoutManager.scrollToPosition(mMsgList.size());
}
}
}
});
// 网易云信消息接收监听
mMsgReceiveObserver = new Observer<List<IMMessage>>() {
@Override
public void onEvent(List<IMMessage> imMessages) {
// 通过判断,决定是否添加收到消息的时间
IMMessage imMessage = imMessages.get(0);
if (mMsgList.isEmpty()) {
if (imMessage.getSessionType() == SessionTypeEnum.P2P
&& imMessage.getSessionId().equals(mChatSession.getChatAccount())) {
mMsgList.add(mChatHandler.createTimeMessage(imMessage));
}
} else {
IMMessage lastMsg = mMsgList.get(mMsgList.size() - 1);
if (imMessage.getSessionType() == SessionTypeEnum.P2P
&& imMessage.getSessionId().equals(mChatSession.getChatAccount())
&& imMessage.getTime() - lastMsg.getTime() > TEN_MINUTE) {
mMsgList.add(mChatHandler.createTimeMessage(imMessage));
}
}
// 将收到的消息添加到列表中
int receiveCount = 0;
for (IMMessage message : imMessages) {
if (message.getSessionType() == SessionTypeEnum.P2P
&& message.getSessionId().equals(mChatSession.getChatAccount())) {
mMsgList.add(message);
receiveCount++;
}
}
if (receiveCount > 0) {
mListAdapter.notifyDataSetChanged();
// 对于整个 mListAdapter 来说,第0个 item 是 HeadView
// 即mMsgList的第 i 条数据,相当于mListAdapter来说是第 i+1 条
mLayoutManager.scrollToPosition(mMsgList.size());
}
}
};
// 网易云信消息状态监听
mMsgStatusObserver = new Observer<IMMessage>() {
@Override
public void onEvent(IMMessage message) {
// 收到消息状态更新,从后往前更新消息状态
for (int i = mMsgList.size() - 1; i >= 0; i--) {
// 时间 item UUid 为空
if (TextUtils.isEmpty(mMsgList.get(i).getUuid())) {
continue;
}
if (mMsgList.get(i).getUuid().equals(message.getUuid())) {
mMsgList.get(i).setStatus(message.getStatus());
mMsgList.get(i).setAttachStatus(message.getAttachStatus());
// 对于整个 mListAdapter 来说,第0个 item 是 HeadView
// 即 mMsgList 的第 i 条数据,相当于mListAdapter来说是第 i+1 条
mListAdapter.notifyItemChanged(i + 1);
break;
}
}
}
};
// 附件传输进度监听
mProgressObserver = new Observer<AttachmentProgress>() {
@Override
public void onEvent(AttachmentProgress progress) {
}
};
}
6.消息加载与加载回调
private void loadMessage() {
if (mMsgList.isEmpty()) {
// 记录为空时,以当前时间为锚点
IMMessage anchorMessage = MessageBuilder.createEmptyMessage(mChatSession.getSessionId(),
mChatSession.getSessionType(), System.currentTimeMillis());
mChatHandler.loadMessage(anchorMessage, this);
} else {
// 否则,以最上一条消息为锚点
IMMessage firstMsg = mMsgList.get(0);
if (TextUtils.isEmpty(firstMsg.getUuid())) {
firstMsg = mMsgList.get(1);
}
mChatHandler.loadMessage(firstMsg, this);//下面的loadSuccess/Fail即为ChatMsgHandler.OnLoadMsgListener的接口重写
}
}
@Override
public void loadSuccess(List<IMMessage> messages, IMMessage anchorMessage) {
mRecyclerView.hideHeadView();
boolean scroll = false;
// 如果原本没有,为第一次加载,需要在加载完成后移动到最后一项
if (mMsgList.isEmpty()) {
scroll = true;
}
if (!messages.isEmpty()) {
mMsgList.addAll(0, mChatHandler.dealLoadMessage(messages, anchorMessage));
mListAdapter.notifyDataSetChanged();//mListAdapter为被观察者
}
if (scroll) {
mLayoutManager.scrollToPosition(mMsgList.size());
}
}
@Override
public void loadFail(String message) {
mRecyclerView.hideHeadView();
ToastUtils.showMessage(this, "消息加载失败:" + message);
Log.e(TAG, "load message fail:" + message);
}
7.页面跳转回调(选择图片界面和定位界面)
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == SELECT_PHOTO) {
if (resultCode == RESULT_OK) {
String[] images = data.getStringArrayExtra("images");
for (String path : images) {
sendMessage(mChatHandler.createImageMessage(path));
}
}
} else if (requestCode == SELECT_LOCATION) {
if (resultCode == RESULT_OK) {
String address = data.getStringExtra("address");
LatLonPoint loc = data.getParcelableExtra("location");
sendMessage(mChatHandler.createLocMessage(loc, address));
}
}
}
/*** 消息发送 ***/
//自己的界面用下述方式刷新,对方用网易云信消息接收监听mMsgReceiveObserver刷新
private void sendMessage(IMMessage message) {
if (mMsgList.isEmpty() ||
message.getTime() - mMsgList.get(mMsgList.size() - 1).getTime() > TEN_MINUTE) {
mMsgList.add(mChatHandler.createTimeMessage(message));
}
// 将新消息加入列表并刷新界面
mMsgList.add(message);
mListAdapter.notifyItemInserted(mMsgList.size());
mLayoutManager.scrollToPosition(mMsgList.size());
// 发送消息并监听消息发送状态
NIMClient.getService(MsgService.class).sendMessage(message, false);
}
8.输入面板事件回调:
@Override
public void sendBtnClick(String textMessage) {
sendMessage(mChatHandler.createTextMessage(textMessage));
}
@Override
public void photoTextBtnClick(String path){
sendMessage(mChatHandler.createImageMessage(path));
}
@Override
public void photoBtnClick() {
Intent intent = new Intent(this, PhotoSelectActivity.class);
startActivityForResult(intent, SELECT_PHOTO);
}
@Override
public void locationBtnClick() {
Intent intent = new Intent(this, SelectLocActivity.class);
startActivityForResult(intent, SELECT_LOCATION);
}
@Override
public void audioRecordFinish(String audioFilePath, long recordTime) {
sendMessage(mChatHandler.createAudioMessage(audioFilePath, recordTime));
}
@Override
public void audioRecordError(String message) {
ToastUtils.showMessage(this, "录音出错:" + message);
}
@Override
public void exLayoutShow() {
mLayoutManager.scrollToPosition(mMsgList.size());
}
/*** 标题栏返回按钮事件 *****/
@OnClick(R.id.iv_back_btn)
public void finishActivity() {
KeyboardUtil.inputBmpList.clear();
ChatInputLayout.currentBmpList.clear();
this.finish();
}
//系统返回键
public void onBackPressed() {
KeyboardUtil.inputBmpList.clear();
ChatInputLayout.currentBmpList.clear();
this.finish();
}
9.消息列表触摸事件:隐藏键盘
/****** 消息列表触摸事件 *******/
private class MyTouchListener implements View.OnTouchListener {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
mInputLayout.hideOverView();
}
return false;
}
}
10.activity生命周期,注册或注销各类监听
@Override
protected void onResume() {
super.onResume();
// 设置当前聊天对象,即如果为mChatPersonAccount用户的消息,则不在通知了进行通知
NIMClient.getService(MsgService.class).setChattingAccount(mChatSession.getChatAccount(),
mChatSession.getSessionType());
AudioPlayManager.resume();
}
@Override
protected void onPause() {
super.onPause();
// 当前无聊天对象,需要通知栏提醒
NIMClient.getService(MsgService.class)
.setChattingAccount(MsgService.MSG_CHATTING_ACCOUNT_NONE,
SessionTypeEnum.None);
AudioPlayManager.pause();
}
@Override
protected void onDestroy() {
super.onDestroy();
// 注销各类监听事件
NIMClient.getService(MsgServiceObserve.class)
.observeReceiveMessage(mMsgReceiveObserver, false);
NIMClient.getService(MsgServiceObserve.class)
.observeMsgStatus(mMsgStatusObserver, false);
NIMClient.getService(MsgServiceObserve.class)
.observeAttachmentProgress(mProgressObserver, true);
AudioPlayManager.release();
}