refactor: 修改database层

修改database层语言为kotlin
This commit is contained in:
糕小菜 2024-10-24 22:01:56 +08:00
parent 8919957c37
commit 023fdbb324
15 changed files with 519 additions and 507 deletions

View File

@ -1,42 +1,25 @@
package com.kaixed.kchat;
package com.kaixed.kchat
import android.app.Application;
import android.content.Context;
import android.util.Log;
import com.kaixed.kchat.database.ObjectBox;
import com.tencent.mmkv.MMKV;
import io.objectbox.android.Admin;
import android.app.Application
import android.util.Log
import com.kaixed.kchat.database.ObjectBox
import com.kaixed.kchat.database.ObjectBox.get
import com.kaixed.kchat.database.ObjectBox.init
import com.tencent.mmkv.MMKV
import io.objectbox.android.Admin
/**
* @Author: kaixed
* @Date: 2024/5/4 10:37
* @Date: 2024/10/24 17:04
*/
public class KchatApplication extends Application {
private static KchatApplication instance;
@Override
public void onCreate() {
super.onCreate();
instance = this;
String rootDir = MMKV.initialize(this);
Log.d("mmkv root: ", rootDir);
ObjectBox.init(this);
boolean started = new Admin(ObjectBox.get()).start(this);
Log.i("ObjectBoxAdmin", "Started: " + started);
class KchatApplication : Application() {
override fun onCreate() {
super.onCreate()
val rootDir = MMKV.initialize(this)
Log.d("mmkv root: ", rootDir)
init(this)
val started = Admin(get()).start(this)
Log.i("ObjectBoxAdmin", "Started: $started")
}
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
}
public static KchatApplication getInstance() {
return instance;
}
}
}

View File

@ -1,24 +1,21 @@
package com.kaixed.kchat.database;
package com.kaixed.kchat.database
import android.content.Context;
import com.kaixed.kchat.database.entity.MyObjectBox;
import io.objectbox.BoxStore;
import android.content.Context
import com.kaixed.kchat.database.entity.MyObjectBox
import io.objectbox.BoxStore
/**
* @author hui
* @Author: kaixed
* @Date: 2024/10/24 16:57
*/
public class ObjectBox {
private static BoxStore store;
object ObjectBox {
private var store: BoxStore? = null
public static void init(Context context) {
fun init(context: Context) {
store = MyObjectBox.builder()
.androidContext(context)
.build();
.androidContext(context)
.build()
}
public static BoxStore get() {
return store;
}
fun get(): BoxStore = store ?: throw IllegalStateException("ObjectBox is not initialized.")
}

View File

@ -1,28 +1,22 @@
package com.kaixed.kchat.database;
package com.kaixed.kchat.database
import static com.kaixed.kchat.utils.Constants.MMKV_USER_SESSION;
import com.kaixed.kchat.utils.Constants.MMKV_USER_SESSION
import com.tencent.mmkv.MMKV
import com.tencent.mmkv.MMKV;
/**
* @Author: kaixed
* @Date: 2024/10/24 16:44
*/
object UserManager {
private const val USERNAME_KEY = "username"
private var username: String
public class UserManager {
private static final String USERNAME_KEY = "username";
private static UserManager instance;
private String username;
private UserManager() {
MMKV mmkv = MMKV.mmkvWithID(MMKV_USER_SESSION);
username = mmkv.getString(USERNAME_KEY, null);
init {
val mmkv = MMKV.mmkvWithID(MMKV_USER_SESSION)
username = mmkv.getString(USERNAME_KEY, "").toString()
}
public static synchronized UserManager getInstance() {
if (instance == null) {
instance = new UserManager();
}
return instance;
}
public String getUsername() {
return username;
fun getUsername(): String {
return username
}
}

View File

@ -1,92 +1,21 @@
package com.kaixed.kchat.database.entity;
package com.kaixed.kchat.database.entity
import androidx.annotation.NonNull;
import io.objectbox.annotation.Entity;
import io.objectbox.annotation.Id;
import io.objectbox.annotation.Entity
import io.objectbox.annotation.Id
/**
* @Author: kaixed
* @Date: 2024/5/23 13:51
* @Date: 2024/10/24 17:07
*/
@Entity
public class ChatLists {
data class ChatLists(
@Id
public long id;
private String talkerId;
private String nickname;
private String avatarUrl;
private String lastContent;
private Long timestamp;
private int unread;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getTalkerId() {
return talkerId;
}
public void setTalkerId(String talkerId) {
this.talkerId = talkerId;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public String getAvatarUrl() {
return avatarUrl;
}
public void setAvatarUrl(String avatarUrl) {
this.avatarUrl = avatarUrl;
}
public String getLastContent() {
return lastContent;
}
public void setLastContent(String lastContent) {
this.lastContent = lastContent;
}
public Long getTimestamp() {
return timestamp;
}
public void setTimestamp(Long timestamp) {
this.timestamp = timestamp;
}
public int getUnread() {
return unread;
}
public void setUnread(int unread) {
this.unread = unread;
}
@NonNull
@Override
public String toString() {
return "ChatLists{" +
"id=" + id +
", talkerId='" + talkerId + '\'' +
", nickname='" + nickname + '\'' +
", avatarUrl='" + avatarUrl + '\'' +
", lastContent='" + lastContent + '\'' +
", timestamp=" + timestamp +
", unread=" + unread +
'}';
}
}
var id: Long = 0L,
var talkerId: String,
var nickname: String,
var avatarUrl: String,
var lastContent: String,
var timestamp: Long,
var unread: Int,
)

View File

@ -1,109 +1,23 @@
package com.kaixed.kchat.database.entity;
package com.kaixed.kchat.database.entity
import io.objectbox.annotation.Entity;
import io.objectbox.annotation.Id;
import io.objectbox.annotation.Entity
import io.objectbox.annotation.Id
/**
* @Author: kaixed
* @Date: 2024/5/23 13:38
* @Date: 2024/10/24 17:08
*/
@Entity
public class Messages {
data class Messages(
@Id
public long msgLocalId;
private Long msgSvrId;
private String content;
private Long timestamp;
private String status;
private String senderId;
private String takerId;
private String type;
private boolean show;
public long getMsgLocalId() {
return msgLocalId;
}
public void setMsgLocalId(long msgLocalId) {
this.msgLocalId = msgLocalId;
}
public Long getMsgSvrId() {
return msgSvrId;
}
public void setMsgSvrId(Long msgSvrId) {
this.msgSvrId = msgSvrId;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Long getTimestamp() {
return timestamp;
}
public void setTimestamp(Long timestamp) {
this.timestamp = timestamp;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getSenderId() {
return senderId;
}
public void setSenderId(String senderId) {
this.senderId = senderId;
}
public String getTakerId() {
return takerId;
}
public void setTakerId(String takerId) {
this.takerId = takerId;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public boolean isShow() {
return show;
}
public void setShow(boolean show) {
this.show = show;
}
@Override
public String toString() {
return "Messages{" +
"msgLocalId=" + msgLocalId +
", msgSvrId=" + msgSvrId +
", content='" + content + '\'' +
", timestamp=" + timestamp +
", status='" + status + '\'' +
", senderId='" + senderId + '\'' +
", takerId='" + takerId + '\'' +
", type='" + type + '\'' +
", show=" + show +
'}';
}
}
var msgLocalId: Long = 0L,
var msgSvrId: Long,
var content: String,
var timestamp: Long,
var status: String,
var senderId: String,
var takerId: String,
var type: String,
var show: Boolean = true,
)

View File

@ -96,7 +96,7 @@ class ContactRepo {
fun addContact(contactId: String, message: String): MutableLiveData<ApplyFriend?> {
val mutableLiveData = MutableLiveData<ApplyFriend?>()
val requestBody = FormBody.Builder()
.add("senderId", UserManager.getInstance().username)
.add("senderId", UserManager.getUsername())
.add("receiverId", contactId)
.add("message", message)
.build()

View File

@ -96,8 +96,8 @@ public class WebSocketService extends Service {
public void onCreate() {
super.onCreate();
// 初始化本地存储及其他配置
messagesBox = ObjectBox.get().boxFor(Messages.class);
username = UserManager.getInstance().getUsername();
messagesBox = ObjectBox.INSTANCE.get().boxFor(Messages.class);
username = UserManager.INSTANCE.getUsername();
// 创建并配置心跳执行器
initializeHeartbeatExecutor();

View File

@ -39,7 +39,7 @@ class ApproveContactRequestActivity : BaseActivity() {
private fun setOnClickListener() {
binding.tvFinish.setOnClickListener {
val contactId = intent.getStringExtra("contactId")
contactViewModel.acceptContactRequest(UserManager.getInstance().username, contactId!!)
contactViewModel.acceptContactRequest(UserManager.getUsername(), contactId!!)
.observe(this) { value ->
value?.let {
runOnUiThread {

View File

@ -390,8 +390,8 @@ public class ChatActivity extends AppCompatActivity implements OnItemClickListen
private void initData() {
messagesBox = ObjectBox.get().boxFor(Messages.class);
username = UserManager.getInstance().getUsername();
messagesBox = ObjectBox.INSTANCE.get().boxFor(Messages.class);
username = UserManager.INSTANCE.getUsername();
Intent intent = getIntent();
friendId = intent.getStringExtra("friendId");
softKeyboardHeight = ConstantsUtil.getKeyboardHeight();
@ -480,13 +480,17 @@ public class ChatActivity extends AppCompatActivity implements OnItemClickListen
private void sendMessage() {
String message = binding.etInput.getText().toString().trim();
Messages messages = new Messages();
messages.setTakerId(friendId);
messages.setSenderId(username);
messages.setContent(message);
messages.setStatus("normal");
messages.setType("normal");
messages.setTimestamp(System.currentTimeMillis());
Messages messages = new Messages(
0,
0,
message,
System.currentTimeMillis(),
"normal",
username,
friendId,
"normal",
true
);
addData(messages);
@ -503,7 +507,7 @@ public class ChatActivity extends AppCompatActivity implements OnItemClickListen
} catch (JSONException e) {
throw new RuntimeException(e);
}
webSocketService.sendMessage(jsonObject.toString(),id);
webSocketService.sendMessage(jsonObject.toString(), id);
binding.etInput.setText("");

View File

@ -24,7 +24,7 @@ class FriendListActivity : AppCompatActivity() {
binding = ActivityFriendListBinding.inflate(layoutInflater)
setContentView(binding.root)
getFriendList(UserManager.getInstance().username)
getFriendList(UserManager.getUsername())
if (friendList.isEmpty()) {
binding.tvNothing.visibility = View.VISIBLE

View File

@ -62,8 +62,8 @@ public class MainActivity extends AppCompatActivity implements OnChatListItemCli
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
chatListsBox = ObjectBox.get().boxFor(ChatLists.class);
messagesBox = ObjectBox.get().boxFor(Messages.class);
chatListsBox = ObjectBox.INSTANCE.get().boxFor(ChatLists.class);
messagesBox = ObjectBox.INSTANCE.get().boxFor(Messages.class);
initView();
@ -143,14 +143,15 @@ public class MainActivity extends AppCompatActivity implements OnChatListItemCli
}
private ChatLists createChatList(String talkerId, String content, Long timestamp) {
ChatLists chatList = new ChatLists();
chatList.setAvatarUrl("1");
chatList.setTalkerId(talkerId);
chatList.setNickname(talkerId);
chatList.setTimestamp(timestamp);
chatList.setLastContent(content);
chatList.setUnread(1);
return chatList;
return new ChatLists(
0L,
talkerId,
talkerId,
"1",
content,
timestamp,
1
);
}
private int findChatListIndex(String talkerId) {

View File

@ -35,7 +35,7 @@ class MessageActivity : AppCompatActivity() {
}
private fun getItems() {
contactViewModel.getContactRequestList(UserManager.getInstance().username)
contactViewModel.getContactRequestList(UserManager.getUsername())
.observe(this) { value ->
if (value != null) {
items.addAll(value.data.toMutableList())

View File

@ -1,222 +1,222 @@
package com.kaixed.kchat.view.adapter;
import static com.kaixed.kchat.utils.Constants.TYPE_ITEM_MINE;
import static com.kaixed.kchat.utils.Constants.TYPE_ITEM_OTHER;
import static com.kaixed.kchat.utils.Constants.TYPE_MESSAGE_WITHDRAW;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.drawable.ColorDrawable;
import android.text.SpannableString;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.PopupWindow;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import androidx.window.layout.WindowMetrics;
import androidx.window.layout.WindowMetricsCalculator;
import com.kaixed.kchat.R;
import com.kaixed.kchat.database.entity.Messages;
import com.kaixed.kchat.database.ObjectBox;
import com.kaixed.kchat.database.UserManager;
import com.kaixed.kchat.utils.ImageSpanUtil;
import java.util.LinkedList;
import io.objectbox.Box;
/**
* @Author: kaixed
* @Date: 2024/5/4 22:48
*/
public class ChatAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final LinkedList<Messages> messages;
private final Context mContext;
public ChatAdapter(Context mContext, LinkedList<Messages> messages) {
this.messages = messages;
this.mContext = mContext;
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
RecyclerView.ViewHolder viewHolder;
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
if (viewType == TYPE_ITEM_MINE) {
View view = inflater.inflate(R.layout.chat_recycle_item_mine, parent, false);
viewHolder = new MineViewHolder(view);
} else if (viewType == TYPE_ITEM_OTHER) {
View view = inflater.inflate(R.layout.chat_recycle_item_other, parent, false);
viewHolder = new OtherViewHolder(view);
} else {
View view = inflater.inflate(R.layout.chat_recycle_item_withdraw, parent, false);
viewHolder = new WithdrawViewHolder(view);
}
return viewHolder;
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
Messages singleMessage = messages.get(position);
if (singleMessage == null) {
return;
}
if (holder.getItemViewType() == TYPE_ITEM_MINE) {
MineViewHolder mineViewHolder = (MineViewHolder) holder;
SpannableString spannableString = ImageSpanUtil.setEmojiSpan(mContext, singleMessage.getContent(), (int) mineViewHolder.mTvContent.getTextSize());
mineViewHolder.mTvContent.setText(spannableString);
mineViewHolder.mTvContent.setOnLongClickListener(v -> {
showPopupWindow(v, position);
return true;
});
} else if (holder.getItemViewType() == TYPE_ITEM_OTHER) {
OtherViewHolder otherViewHolder = (OtherViewHolder) holder;
SpannableString spannableString = ImageSpanUtil.setEmojiSpan(mContext, singleMessage.getContent(), (int) otherViewHolder.mTvContent.getTextSize());
otherViewHolder.mTvContent.setText(spannableString);
otherViewHolder.mTvContent.setOnLongClickListener(v -> {
showPopupWindow(v, position);
return true;
});
} else {
WithdrawViewHolder mineViewHolder = (WithdrawViewHolder) holder;
mineViewHolder.bindData(singleMessage.getSenderId());
}
}
/**
* 用来显示消息长按弹窗
*/
private void showPopupWindow(@NonNull View textView, int position) {
@SuppressLint("InflateParams") View popupView = LayoutInflater.from(mContext).inflate(R.layout.popwindows, null);
PopupWindow popupWindow = new PopupWindow(popupView,
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
popupWindow.setFocusable(true);
popupWindow.setBackgroundDrawable(new ColorDrawable());
popupView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
int popupWidth = popupView.getMeasuredWidth();
int popupHeight = popupView.getMeasuredHeight();
int anchorWidth = textView.getWidth();
int anchorHeight = textView.getHeight();
int offsetX = (anchorWidth - popupWidth) / 2;
int offset = 15;
ImageView mIvArrowUp = popupView.findViewById(R.id.iv_arrow_up);
ImageView mIvArrowDown = popupView.findViewById(R.id.iv_arrow_down);
if (getDistanceFromTop(textView) > anchorHeight) {
popupWindow.showAsDropDown(textView, offsetX, -offset - anchorHeight - popupHeight);
mIvArrowUp.setVisibility(View.GONE);
mIvArrowDown.setVisibility(View.VISIBLE);
} else {
popupWindow.showAsDropDown(textView, offsetX, 10);
mIvArrowUp.setVisibility(View.VISIBLE);
mIvArrowDown.setVisibility(View.GONE);
}
ImageView mIvWithdraw = popupView.findViewById(R.id.iv_withdraw);
mIvWithdraw.setOnClickListener(v -> {
messages.get(position).setStatus("withdraw");
updateDb(messages.get(position));
notifyItemChanged(position);
popupWindow.dismiss();
});
}
private void updateDb(Messages messages) {
Box<Messages> messagesBox = ObjectBox.get().boxFor(Messages.class);
messagesBox.put(messages);
}
private int getDistanceFromTop(View view) {
int[] location = new int[2];
view.getLocationOnScreen(location);
//location[0]指的是横向距离
return location[1];
}
private int getDistanceFromBottom(@NonNull View view) {
// 获取控件在屏幕上的位置
int[] location = new int[2];
view.getLocationOnScreen(location);
// 获取屏幕高度
WindowMetrics windowMetrics = WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(mContext);
int screenHeight = windowMetrics.getBounds().height();
// 计算控件底部到屏幕底部的距离
int viewBottom = location[1] + view.getHeight();
return screenHeight - viewBottom;
}
@Override
public int getItemViewType(int position) {
String curUser = UserManager.getInstance().getUsername();
if ("withdraw".equals(messages.get(position).getStatus())) {
return TYPE_MESSAGE_WITHDRAW;
}
return curUser.equals(messages.get(position).getSenderId()) ? TYPE_ITEM_MINE : TYPE_ITEM_OTHER;
}
@Override
public int getItemCount() {
return messages.isEmpty() ? 0 : messages.size();
}
static class MineViewHolder extends RecyclerView.ViewHolder {
private final TextView mTvContent;
public MineViewHolder(@NonNull View itemView) {
super(itemView);
mTvContent = itemView.findViewById(R.id.tv_mine_content);
}
private void bindData(Messages singleMessage) {
mTvContent.setText(singleMessage.getContent());
}
}
static class OtherViewHolder extends RecyclerView.ViewHolder {
private final TextView mTvContent;
public OtherViewHolder(@NonNull View itemView) {
super(itemView);
mTvContent = itemView.findViewById(R.id.tv_other_content);
}
private void bindData(Messages singleMessage) {
mTvContent.setText(singleMessage.getContent());
}
}
static class WithdrawViewHolder extends RecyclerView.ViewHolder {
private final TextView mTvNickname;
public WithdrawViewHolder(@NonNull View itemView) {
super(itemView);
mTvNickname = itemView.findViewById(R.id.tv_user_nickname);
}
private void bindData(String nickname) {
mTvNickname.setText(nickname);
}
}
}
//package com.kaixed.kchat.view.adapter;
//
//import static com.kaixed.kchat.utils.Constants.TYPE_ITEM_MINE;
//import static com.kaixed.kchat.utils.Constants.TYPE_ITEM_OTHER;
//import static com.kaixed.kchat.utils.Constants.TYPE_MESSAGE_WITHDRAW;
//
//import android.annotation.SuppressLint;
//import android.content.Context;
//import android.graphics.drawable.ColorDrawable;
//import android.text.SpannableString;
//import android.view.LayoutInflater;
//import android.view.View;
//import android.view.ViewGroup;
//import android.widget.ImageView;
//import android.widget.PopupWindow;
//import android.widget.TextView;
//
//import androidx.annotation.NonNull;
//import androidx.recyclerview.widget.RecyclerView;
//import androidx.window.layout.WindowMetrics;
//import androidx.window.layout.WindowMetricsCalculator;
//
//import com.kaixed.kchat.R;
//import com.kaixed.kchat.database.entity.Messages;
//import com.kaixed.kchat.database.ObjectBox;
//import com.kaixed.kchat.database.UserManager;
//import com.kaixed.kchat.utils.ImageSpanUtil;
//
//import java.util.LinkedList;
//
//import io.objectbox.Box;
//
///**
// * @Author: kaixed
// * @Date: 2024/5/4 22:48
// */
//public class ChatAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
// private final LinkedList<Messages> messages;
// private final Context mContext;
//
// public ChatAdapter(Context mContext, LinkedList<Messages> messages) {
// this.messages = messages;
// this.mContext = mContext;
// }
//
// @NonNull
// @Override
// public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
// RecyclerView.ViewHolder viewHolder;
// LayoutInflater inflater = LayoutInflater.from(parent.getContext());
// if (viewType == TYPE_ITEM_MINE) {
// View view = inflater.inflate(R.layout.chat_recycle_item_mine, parent, false);
// viewHolder = new MineViewHolder(view);
// } else if (viewType == TYPE_ITEM_OTHER) {
// View view = inflater.inflate(R.layout.chat_recycle_item_other, parent, false);
// viewHolder = new OtherViewHolder(view);
// } else {
// View view = inflater.inflate(R.layout.chat_recycle_item_withdraw, parent, false);
// viewHolder = new WithdrawViewHolder(view);
// }
// return viewHolder;
// }
//
// @Override
// public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
//
// Messages singleMessage = messages.get(position);
// if (singleMessage == null) {
// return;
// }
// if (holder.getItemViewType() == TYPE_ITEM_MINE) {
// MineViewHolder mineViewHolder = (MineViewHolder) holder;
// SpannableString spannableString = ImageSpanUtil.setEmojiSpan(mContext, singleMessage.getContent(), (int) mineViewHolder.mTvContent.getTextSize());
//
// mineViewHolder.mTvContent.setText(spannableString);
//
// mineViewHolder.mTvContent.setOnLongClickListener(v -> {
// showPopupWindow(v, position);
// return true;
// });
//
// } else if (holder.getItemViewType() == TYPE_ITEM_OTHER) {
// OtherViewHolder otherViewHolder = (OtherViewHolder) holder;
//
// SpannableString spannableString = ImageSpanUtil.setEmojiSpan(mContext, singleMessage.getContent(), (int) otherViewHolder.mTvContent.getTextSize());
//
// otherViewHolder.mTvContent.setText(spannableString);
// otherViewHolder.mTvContent.setOnLongClickListener(v -> {
// showPopupWindow(v, position);
// return true;
// });
// } else {
// WithdrawViewHolder mineViewHolder = (WithdrawViewHolder) holder;
// mineViewHolder.bindData(singleMessage.getSenderId());
// }
//
// }
//
// /**
// * 用来显示消息长按弹窗
// */
// private void showPopupWindow(@NonNull View textView, int position) {
// @SuppressLint("InflateParams") View popupView = LayoutInflater.from(mContext).inflate(R.layout.popwindows, null);
//
// PopupWindow popupWindow = new PopupWindow(popupView,
// ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
//
// popupWindow.setFocusable(true);
// popupWindow.setBackgroundDrawable(new ColorDrawable());
//
//
// popupView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
// int popupWidth = popupView.getMeasuredWidth();
// int popupHeight = popupView.getMeasuredHeight();
//
// int anchorWidth = textView.getWidth();
// int anchorHeight = textView.getHeight();
// int offsetX = (anchorWidth - popupWidth) / 2;
//
// int offset = 15;
//
// ImageView mIvArrowUp = popupView.findViewById(R.id.iv_arrow_up);
// ImageView mIvArrowDown = popupView.findViewById(R.id.iv_arrow_down);
//
// if (getDistanceFromTop(textView) > anchorHeight) {
// popupWindow.showAsDropDown(textView, offsetX, -offset - anchorHeight - popupHeight);
// mIvArrowUp.setVisibility(View.GONE);
// mIvArrowDown.setVisibility(View.VISIBLE);
// } else {
// popupWindow.showAsDropDown(textView, offsetX, 10);
// mIvArrowUp.setVisibility(View.VISIBLE);
// mIvArrowDown.setVisibility(View.GONE);
// }
//
// ImageView mIvWithdraw = popupView.findViewById(R.id.iv_withdraw);
//
// mIvWithdraw.setOnClickListener(v -> {
// messages.get(position).setStatus("withdraw");
// updateDb(messages.get(position));
// notifyItemChanged(position);
//
// popupWindow.dismiss();
// });
// }
//
// private void updateDb(Messages messages) {
// Box<Messages> messagesBox = ObjectBox.get().boxFor(Messages.class);
// messagesBox.put(messages);
// }
//
// private int getDistanceFromTop(View view) {
// int[] location = new int[2];
// view.getLocationOnScreen(location);
// //location[0]指的是横向距离
// return location[1];
// }
//
// private int getDistanceFromBottom(@NonNull View view) {
// // 获取控件在屏幕上的位置
// int[] location = new int[2];
// view.getLocationOnScreen(location);
// // 获取屏幕高度
// WindowMetrics windowMetrics = WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(mContext);
// int screenHeight = windowMetrics.getBounds().height();
// // 计算控件底部到屏幕底部的距离
// int viewBottom = location[1] + view.getHeight();
// return screenHeight - viewBottom;
// }
//
// @Override
// public int getItemViewType(int position) {
// String curUser = UserManager.getInstance().getUsername();
// if ("withdraw".equals(messages.get(position).getStatus())) {
// return TYPE_MESSAGE_WITHDRAW;
// }
// return curUser.equals(messages.get(position).getSenderId()) ? TYPE_ITEM_MINE : TYPE_ITEM_OTHER;
// }
//
// @Override
// public int getItemCount() {
// return messages.isEmpty() ? 0 : messages.size();
// }
//
// static class MineViewHolder extends RecyclerView.ViewHolder {
// private final TextView mTvContent;
//
// public MineViewHolder(@NonNull View itemView) {
// super(itemView);
// mTvContent = itemView.findViewById(R.id.tv_mine_content);
// }
//
// private void bindData(Messages singleMessage) {
// mTvContent.setText(singleMessage.getContent());
// }
// }
//
// static class OtherViewHolder extends RecyclerView.ViewHolder {
// private final TextView mTvContent;
//
// public OtherViewHolder(@NonNull View itemView) {
// super(itemView);
// mTvContent = itemView.findViewById(R.id.tv_other_content);
// }
//
// private void bindData(Messages singleMessage) {
// mTvContent.setText(singleMessage.getContent());
// }
// }
//
// static class WithdrawViewHolder extends RecyclerView.ViewHolder {
// private final TextView mTvNickname;
//
// public WithdrawViewHolder(@NonNull View itemView) {
// super(itemView);
// mTvNickname = itemView.findViewById(R.id.tv_user_nickname);
// }
//
// private void bindData(String nickname) {
// mTvNickname.setText(nickname);
// }
// }
//}

View File

@ -0,0 +1,191 @@
package com.kaixed.kchat.view.adapter
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.drawable.ColorDrawable
import android.text.SpannableString
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.PopupWindow
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import androidx.window.layout.WindowMetrics
import androidx.window.layout.WindowMetricsCalculator
import com.kaixed.kchat.R
import com.kaixed.kchat.database.entity.Messages
import com.kaixed.kchat.database.ObjectBox
import com.kaixed.kchat.database.UserManager
import com.kaixed.kchat.utils.Constants.TYPE_ITEM_MINE
import com.kaixed.kchat.utils.Constants.TYPE_ITEM_OTHER
import com.kaixed.kchat.utils.Constants.TYPE_MESSAGE_WITHDRAW
import com.kaixed.kchat.utils.ImageSpanUtil
import com.kaixed.kchat.databinding.ChatRecycleItemMineBinding
import com.kaixed.kchat.databinding.ChatRecycleItemOtherBinding
import com.kaixed.kchat.databinding.ChatRecycleItemWithdrawBinding
import com.kaixed.kchat.databinding.PopwindowsBinding
import java.util.LinkedList
import io.objectbox.Box
class ChatAdapter(
private val context: Context,
private val messages: LinkedList<Messages>
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
TYPE_ITEM_MINE -> {
val binding = ChatRecycleItemMineBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
MineViewHolder(binding)
}
TYPE_ITEM_OTHER -> {
val binding = ChatRecycleItemOtherBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
OtherViewHolder(binding)
}
else -> {
val binding = ChatRecycleItemWithdrawBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
WithdrawViewHolder(binding)
}
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val singleMessage = messages[position]
when (holder) {
is MineViewHolder -> {
val spannableString = ImageSpanUtil.setEmojiSpan(
context,
singleMessage.content,
holder.binding.tvMineContent.textSize.toInt()
)
holder.binding.tvMineContent.text = spannableString
holder.binding.tvMineContent.setOnLongClickListener {
showPopupWindow(it, position)
true
}
}
is OtherViewHolder -> {
val spannableString = ImageSpanUtil.setEmojiSpan(
context,
singleMessage.content,
holder.binding.tvOtherContent.textSize.toInt()
)
holder.binding.tvOtherContent.text = spannableString
holder.binding.tvOtherContent.setOnLongClickListener {
showPopupWindow(it, position)
true
}
}
is WithdrawViewHolder -> {
holder.bindData(singleMessage.senderId)
}
}
}
private fun showPopupWindow(view: View, position: Int) {
val binding: PopwindowsBinding = PopwindowsBinding.inflate(LayoutInflater.from(context))
val popupWindow: PopupWindow = PopupWindow(
binding.root,
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
).apply {
isFocusable = true
isOutsideTouchable = true
setBackgroundDrawable(ColorDrawable())
}
// 动态计算 PopupWindow 的显示位置
val location = IntArray(2)
view.getLocationOnScreen(location)
// 测量 PopupWindow 的宽度和高度
binding.root.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
val popupWidth = binding.root.measuredWidth
val popupHeight = binding.root.measuredHeight
// 计算水平居中的偏移量
val offsetX = (view.width - popupWidth) / 2
// 上下方显示的 padding
val somePadding = (context.resources.displayMetrics.density * 10).toInt()
// 获取 view 顶部和底部在屏幕中的绝对位置
val viewTop = location[1]
val viewBottom = location[1] + view.height
// 判断 PopupWindow 显示在上方是否有足够空间
val isEnoughSpaceAbove = viewTop >= popupHeight + somePadding
val isEnoughSpaceBelow =
(context.resources.displayMetrics.heightPixels - viewBottom) >= popupHeight + somePadding
// 计算 Y 轴的偏移量,根据空间选择上方或下方显示
val offsetY = if (isEnoughSpaceAbove) {
-view.height - popupHeight - somePadding
} else if (isEnoughSpaceBelow) {
view.height + somePadding
} else {
// 如果上下空间都不足,选择最小偏移量的显示(可以根据需求调整)
view.height / 2
}
// 显示 PopupWindow锚点是 view
popupWindow.showAsDropDown(view, offsetX, offsetY)
// 设置点击事件
binding.ivWithdraw.setOnClickListener {
messages[position].status = "withdraw"
updateDb(messages[position])
notifyItemChanged(position)
popupWindow.dismiss()
}
}
private fun updateDb(message: Messages) {
val messagesBox: Box<Messages> = ObjectBox.get().boxFor(Messages::class.java)
messagesBox.put(message)
}
override fun getItemViewType(position: Int): Int {
val curUser = UserManager.getUsername()
return if (messages[position].status == "withdraw") {
TYPE_MESSAGE_WITHDRAW
} else {
if (curUser == messages[position].senderId) TYPE_ITEM_MINE else TYPE_ITEM_OTHER
}
}
override fun getItemCount(): Int = messages.size
class MineViewHolder(val binding: ChatRecycleItemMineBinding) :
RecyclerView.ViewHolder(binding.root)
class OtherViewHolder(val binding: ChatRecycleItemOtherBinding) :
RecyclerView.ViewHolder(binding.root)
class WithdrawViewHolder(val binding: ChatRecycleItemWithdrawBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bindData(nickname: String) {
binding.tvUserNickname.text = nickname
}
}
}

View File

@ -3,7 +3,6 @@ package com.kaixed.kchat.view.adapter;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;