package com.netease.nim.uikit.session.viewholder;
import android.content.Context;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.netease.nim.uikit.NimUIKit;
import com.netease.nim.uikit.R;
import com.netease.nim.uikit.cache.TeamDataCache;
import com.netease.nim.uikit.common.ui.imageview.HeadImageView;
import com.netease.nim.uikit.common.ui.recyclerview.adapter.BaseMultiItemFetchLoadAdapter;
import com.netease.nim.uikit.common.ui.recyclerview.holder.BaseViewHolder;
import com.netease.nim.uikit.common.ui.recyclerview.holder.RecyclerViewHolder;
import com.netease.nim.uikit.common.util.sys.TimeUtil;
import com.netease.nim.uikit.session.module.list.MsgAdapter;
import com.netease.nimlib.sdk.NIMClient;
import com.netease.nimlib.sdk.msg.MsgService;
import com.netease.nimlib.sdk.msg.attachment.FileAttachment;
import com.netease.nimlib.sdk.msg.constant.MsgDirectionEnum;
import com.netease.nimlib.sdk.msg.constant.MsgStatusEnum;
import com.netease.nimlib.sdk.msg.constant.SessionTypeEnum;
import com.netease.nimlib.sdk.msg.model.IMMessage;
/**
* 会话窗口消息列表项的ViewHolder基类,负责每个消息项的外层框架,包括头像,昵称,发送/接收进度条,重发按钮等。<br>
* 具体的消息展示项可继承该基类,然后完成具体消息内容展示即可。
*/
public abstract class MsgViewHolderBase extends RecyclerViewHolder<BaseMultiItemFetchLoadAdapter, BaseViewHolder, IMMessage> {
public MsgViewHolderBase(BaseMultiItemFetchLoadAdapter adapter) {
super(adapter);
this.adapter = adapter;
}
// basic
protected View view;
protected Context context;
protected BaseMultiItemFetchLoadAdapter adapter;
// data
protected IMMessage message;
// view
protected View alertButton;
protected TextView timeTextView;
protected ProgressBar progressBar;
protected TextView nameTextView;
protected FrameLayout contentContainer;
protected LinearLayout nameContainer;
protected TextView readReceiptTextView;
private HeadImageView avatarLeft;
private HeadImageView avatarRight;
public ImageView nameIconView;
// contentContainerView的默认长按事件。如果子类需要不同的处理,可覆盖onItemLongClick方法
// 但如果某些子控件会拦截触摸消息,导致contentContainer收不到长按事件,子控件也可在inflate时重新设置
protected View.OnLongClickListener longClickListener;
/// -- 以下接口可由子类覆盖或实现
// 返回具体消息类型内容展示区域的layout res id
abstract protected int getContentResId();
// 在该接口中根据layout对各控件成员变量赋值
abstract protected void inflateContentView();
// 将消息数据项与内容的view进行绑定
abstract protected void bindContentView();
// 内容区域点击事件响应处理。
protected void onItemClick() {
}
// 内容区域长按事件响应处理。该接口的优先级比adapter中有长按事件的处理监听高,当该接口返回为true时,adapter的长按事件监听不会被调用到。
protected boolean onItemLongClick() {
return false;
}
// 当是接收到的消息时,内容区域背景的drawable id
protected int leftBackground() {
return R.drawable.nim_message_item_left_selector;
}
// 当是发送出去的消息时,内容区域背景的drawable id
protected int rightBackground() {
return R.drawable.nim_message_item_right_selector;
}
// 返回该消息是不是居中显示
protected boolean isMiddleItem() {
return false;
}
// 是否显示头像,默认为显示
protected boolean isShowHeadImage() {
return true;
}
// 是否显示气泡背景,默认为显示
protected boolean isShowBubble() {
return true;
}
/// -- 以下接口可由子类调用
protected final MsgAdapter getMsgAdapter() {
return (MsgAdapter) adapter;
}
/**
* 下载附件/缩略图
*/
protected void downloadAttachment() {
if (message.getAttachment() != null && message.getAttachment() instanceof FileAttachment)
NIMClient.getService(MsgService.class).downloadAttachment(message, true);
}
// 设置FrameLayout子控件的gravity参数
protected final void setGravity(View view, int gravity) {
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) view.getLayoutParams();
params.gravity = gravity;
}
// 设置控件的长宽
protected void setLayoutParams(int width, int height, View... views) {
for (View view : views) {
ViewGroup.LayoutParams maskParams = view.getLayoutParams();
maskParams.width = width;
maskParams.height = height;
view.setLayoutParams(maskParams);
}
}
// 根据layout id查找对应的控件
protected <T extends View> T findViewById(int id) {
return (T) view.findViewById(id);
}
// 判断消息方向,是否是接收到的消息
protected boolean isReceivedMessage() {
return message.getDirect() == MsgDirectionEnum.In;
}
/// -- 以下是基类实现代码
@Override
public void convert(BaseViewHolder holder, IMMessage data, int position, boolean isScrolling) {
view = holder.getConvertView();
context = holder.getContext();
message = data;
inflate();
refresh();
}
protected final void inflate() {
timeTextView = findViewById(R.id.message_item_time);
avatarLeft = findViewById(R.id.message_item_portrait_left);
avatarRight = findViewById(R.id.message_item_portrait_right);
alertButton = findViewById(R.id.message_item_alert);
progressBar = findViewById(R.id.message_item_progress);
nameTextView = findViewById(R.id.message_item_nickname);
contentContainer = findViewById(R.id.message_item_content);
nameIconView = findViewById(R.id.message_item_name_icon);
nameContainer = findViewById(R.id.message_item_name_layout);
readReceiptTextView = findViewById(R.id.textViewAlreadyRead);
// 这里只要inflate出来后加入一次即可
if(contentContainer.getChildCount() == 0) {
View.inflate(view.getContext(), getContentResId(), contentContainer);
}
inflateContentView();
}
protected final void refresh() {
setHeadImageView();
setNameTextView();
setTimeTextView();
setStatus();
setOnClickListener();
setLongClickListener();
setContent();
setReadReceipt();
bindContentView();
}
public void refreshCurrentItem() {
if (message != null) {
refresh();
}
}
/**
* 设置时间显示
*/
private void setTimeTextView() {
if (getMsgAdapter().needShowTime(message)) {
timeTextView.setVisibility(View.VISIBLE);
} else {
timeTextView.setVisibility(View.GONE);
return;
}
String text = TimeUtil.getTimeShowString(message.getTime(), false);
timeTextView.setText(text);
}
/**
* 设置消息发送状态
*/
private void setStatus() {
MsgStatusEnum status = message.getStatus();
switch (status) {
case fail:
progressBar.setVisibility(View.GONE);
alertButton.setVisibility(View.VISIBLE);
break;
case sending:
progressBar.setVisibility(View.VISIBLE);
alertButton.setVisibility(View.GONE);
break;
default:
progressBar.setVisibility(View.GONE);
alertButton.setVisibility(View.GONE);
break;
}
}
private void setHeadImageView() {
HeadImageView show = isReceivedMessage() ? avatarLeft : avatarRight;
HeadImageView hide = isReceivedMessage() ? avatarRight : avatarLeft;
hide.setVisibility(View.GONE);
if (!isShowHeadImage()) {
show.setVisibility(View.GONE);
return;
}
if (isMiddleItem()) {
show.setVisibility(View.GONE);
} else {
show.setVisibility(View.VISIBLE);
show.loadBuddyAvatar(message.getFromAccount());
}
}
private void setOnClickListener() {
// 重发/重收按钮响应事件
if (getMsgAdapter().getEventListener() != null) {
alertButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
getMsgAdapter().getEventListener().onFailedBtnClick(message);
}
});
}
// 内容区域点击事件响应, 相当于点击了整项
contentContainer.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onItemClick();
}
});
// 头像点击事件响应
if (NimUIKit.getSessionListener() != null) {
View.OnClickListener portraitListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
NimUIKit.getSessionListener().onAvatarClicked(context, message);
}
};
avatarLeft.setOnClickListener(portraitListener);
avatarRight.setOnClickListener(portraitListener);
}
}
/**
* item长按事件监听
*/
private void setLongClickListener() {
longClickListener = new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
// 优先派发给自己处理,
if (!onItemLongClick()) {
if (getMsgAdapter().getEventListener() != null) {
getMsgAdapter().getEventListener().onViewHolderLongClick(contentContainer, view, message);
return true;
}
}
return false;
}
};
// 消息长按事件响应处理
contentContainer.setOnLongClickListener(longClickListener);
// 头像长按事件响应处理
if (NimUIKit.getSessionListener() != null) {
View.OnLongClickListener longClickListener = new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
NimUIKit.getSessionListener().onAvatarLongClicked(context, message);
return true;
}
};
avatarLeft.setOnLongClickListener(longClickListener);
avatarRight.setOnLongClickListener(longClickListener);
}
}
public void setNameTextView() {
if (message.getSessionType() == SessionTypeEnum.Team && isReceivedMessage() && !isMiddleItem()) {
nameTextView.setVisibility(View.VISIBLE);
nameTextView.setText(TeamDataCache.getInstance().getTeamMemberDisplayName(message.getSessionId(), message
.getFromAccount()));
} else {
nameTextView.setVisibility(View.GONE);
}
}
private void setContent() {
if (!isShowBubble() && !isMiddleItem()) {
return;
}
LinearLayout bodyContainer = (LinearLayout) view.findViewById(R.id.message_item_body);
// 调整container的位置
int index = isReceivedMessage() ? 0 : 3;
if (bodyContainer.getChildAt(index) != contentContainer) {
bodyContainer.removeView(contentContainer);
bodyContainer.addView(contentContainer, index);
}
if (isMiddleItem()) {
setGravity(bodyContainer, Gravity.CENTER);
} else {
if (isReceivedMessage()) {
setGravity(bodyContainer, Gravity.LEFT);
contentContainer.setBackgroundResource(leftBackground());
} else {
setGravity(bodyContainer, Gravity.RIGHT);
contentContainer.setBackgroundResource(rightBackground());
}
}
}
private void setReadReceipt() {
if (!TextUtils.isEmpty(getMsgAdapter().getUuid()) && message.getUuid().equals(getMsgAdapter().getUuid())) {
readReceiptTextView.setVisibility(View.VISIBLE);
} else {
readReceiptTextView.setVisibility(View.GONE);
}
}
}