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.app.Application
import android.content.Context; import android.util.Log
import android.util.Log; import com.kaixed.kchat.database.ObjectBox
import com.kaixed.kchat.database.ObjectBox.get
import com.kaixed.kchat.database.ObjectBox; import com.kaixed.kchat.database.ObjectBox.init
import com.tencent.mmkv.MMKV; import com.tencent.mmkv.MMKV
import io.objectbox.android.Admin
import io.objectbox.android.Admin;
/** /**
* @Author: kaixed * @Author: kaixed
* @Date: 2024/5/4 10:37 * @Date: 2024/10/24 17:04
*/ */
public class KchatApplication extends Application { class KchatApplication : Application() {
private static KchatApplication instance; override fun onCreate() {
super.onCreate()
val rootDir = MMKV.initialize(this)
Log.d("mmkv root: ", rootDir)
@Override init(this)
public void onCreate() { val started = Admin(get()).start(this)
super.onCreate(); Log.i("ObjectBoxAdmin", "Started: $started")
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);
}
@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 android.content.Context
import com.kaixed.kchat.database.entity.MyObjectBox
import com.kaixed.kchat.database.entity.MyObjectBox; import io.objectbox.BoxStore
import io.objectbox.BoxStore;
/** /**
* @author hui * @Author: kaixed
* @Date: 2024/10/24 16:57
*/ */
public class ObjectBox { object ObjectBox {
private static BoxStore store; private var store: BoxStore? = null
public static void init(Context context) { fun init(context: Context) {
store = MyObjectBox.builder() store = MyObjectBox.builder()
.androidContext(context) .androidContext(context)
.build(); .build()
} }
public static BoxStore get() { fun get(): BoxStore = store ?: throw IllegalStateException("ObjectBox is not initialized.")
return store;
}
} }

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 { init {
val mmkv = MMKV.mmkvWithID(MMKV_USER_SESSION)
private static final String USERNAME_KEY = "username"; username = mmkv.getString(USERNAME_KEY, "").toString()
private static UserManager instance;
private String username;
private UserManager() {
MMKV mmkv = MMKV.mmkvWithID(MMKV_USER_SESSION);
username = mmkv.getString(USERNAME_KEY, null);
} }
public static synchronized UserManager getInstance() { fun getUsername(): String {
if (instance == null) { return username
instance = new UserManager();
}
return instance;
}
public String getUsername() {
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 * @Author: kaixed
* @Date: 2024/5/23 13:51 * @Date: 2024/10/24 17:07
*/ */
@Entity @Entity
public class ChatLists { data class ChatLists(
@Id @Id
public long id; var id: Long = 0L,
private String talkerId; var talkerId: String,
private String nickname; var nickname: String,
private String avatarUrl; var avatarUrl: String,
private String lastContent; var lastContent: String,
private Long timestamp; var timestamp: Long,
private int unread; var unread: Int,
)
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 +
'}';
}
}

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.Entity
import io.objectbox.annotation.Id; import io.objectbox.annotation.Id
/** /**
* @Author: kaixed * @Author: kaixed
* @Date: 2024/5/23 13:38 * @Date: 2024/10/24 17:08
*/ */
@Entity @Entity
public class Messages { data class Messages(
@Id @Id
public long msgLocalId; var msgLocalId: Long = 0L,
private Long msgSvrId; var msgSvrId: Long,
private String content; var content: String,
private Long timestamp; var timestamp: Long,
private String status; var status: String,
private String senderId; var senderId: String,
private String takerId; var takerId: String,
private String type; var type: String,
private boolean show; var show: Boolean = true,
)
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 +
'}';
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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