refactor: 优化部分代码

This commit is contained in:
糕小菜 2025-02-12 20:23:38 +08:00
parent da1fc07599
commit 2e9e115c7e
50 changed files with 650 additions and 322 deletions

1
.idea/.name Normal file
View File

@ -0,0 +1 @@
KChat-Android

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="17" />
<bytecodeTargetLevel target="21" />
</component>
</project>

View File

@ -4,6 +4,7 @@
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="CHOOSE_PER_TEST" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
<option name="modules">

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">

View File

@ -5,8 +5,12 @@
<set>
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
<option value="com.intellij.execution.junit.PatternConfigurationProducer" />
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
</set>
</option>
</component>

View File

@ -20,15 +20,15 @@
- [x] **搜索功能**:支持通过关键词在聊天记录中搜索消息,同时支持按文件类型筛选。
- [x] **修改个人信息**:用户可以修改昵称、头像、个性签名等个人信息,提升个性化体验。
- [x] **修改用户密码**:用户可以更新自己的密码。
- [x] **离线消息**:即使用户离线,服务器也会缓存消息,并在用户上线后自动同步到设备。
- [ ] **查看好友动态**:支持查看好友分享的动态内容,形式包括文字、图片和视频,用户可以点赞或评论。
- [ ] **支持多媒体消息**:支持发送音频、视频、文件等多种格式,满足用户不同的交流需求。
- [ ] **群聊支持**:用户可以创建群组,与多人实时互动,并支持群管理功能(如设置群管理员、踢人)。
- [ ] **消息通知**:支持推送通知,提醒用户有新的消息,未读消息可在通知栏中展示摘要。
- [ ] **离线消息**:即使用户离线,服务器也会缓存消息,并在用户上线后自动同步到设备。
### 其他特性
- [ ] **消息加密**:所有用户消息在传输过程中都进行端到端加密,确保通信安全,防止信息泄露。
- [x] **消息加密**:所有用户消息在传输过程中都进行端到端加密,确保通信安全,防止信息泄露。
- [ ] **个性化设置**:支持用户自定义头像、昵称、聊天背景,提供多种预设主题和自定义选项。
- [ ] **深色/浅色模式**:支持自动或手动切换界面主题,适配不同的光线环境和用户偏好。

View File

@ -0,0 +1,19 @@
package com.kaixed.kchat.extensions
import android.content.Intent
import android.os.Build
import android.os.Parcelable
/**
* @Author: kaixed
* @Date: 2025/1/26 19:41
*/
// 扩展函数,用于兼容不同版本的 Intent.getParcelableExtra 方法
inline fun <reified T : Parcelable> Intent.getParcelableExtraCompat(key: String): T? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
getParcelableExtra(key, T::class.java)
} else {
@Suppress("DEPRECATION")
getParcelableExtra(key)
}
}

View File

@ -13,22 +13,43 @@ object NetworkInterface {
const val SERVER_URL = "http://$URL"
const val WEBSOCKET_SERVER_URL = "ws://$URL"
const val WEBSOCKET = "/websocket/single/"
const val USER_INFO = "/users/info/"
const val USER_LOGIN_BY_USERNAME = "/users/login/username"
const val USER_LOGIN_BY_TELEPHONE = "/users/login/telephone"
const val USER_REGISTER = "/users/register"
const val USER_MESSAGES_COUNT = "/users/%s/%s/msgCounts"
const val USER_MESSAGES = "/users/%s/%s/messages"
const val MESSAGE_WITHDRAW = "/messages/"
// 获取access-token
const val ACCESS_TOKEN = "auth/access-token"
// 获取refresh-token
const val REFRESH_TOKEN = "auth/refresh-token"
// 登录接口(根据用户名登录)
const val LOGIN_BY_USERNAME = "auth/login/username"
// 登录接口(根据电话登录)
const val LOGIN_BY_TELEPHONE = "auth/login/telephone"
// 注册接口
const val REGISTER = "auth/register"
const val USER_LIST = "/users/list/"
// 上传文件
const val UPLOAD_FILE = "file/upload"
const val ADD_FRIEND = "/friend/request"
const val FRIEND_LIST = "/friend/list"
const val FRIEND_REQUEST_LIST = "/friend/request/list"
const val ACCEPT_CONTACT_REQUEST = "/friend/accept"
// 更新用户密码
const val UPDATE_PASSWORD = "user/password/update"
// 获取用户信息
const val GET_USER_INFO = "user/info/fetch"
// 更新用户信息
const val UPDATE_USER_INFO = "user/info/update"
// 上传用户头像
const val UPLOAD_AVATAR = "user/upload-avatar"
const val UPDATE_USER_INFO = "/users/info"
const val UPLOAD_AVATAR = "/users/avatar"
// 发送好友请求
const val SEND_FRIEND_REQUEST = "friends/requests/send"
// 接受好友请求
const val ACCEPT_FRIEND_REQUEST = "friends/requests/accept"
// 获取好友列表
const val GET_FRIENDS = "friends/list"
// 获取好友请求列表
const val GET_FRIEND_LIST = "friends/requests/fetch"
// 删除好友
const val DELETE_FRIEND = "friends/delete-friend"
// 设置好友备注
const val SET_FRIEND_REMARK = "friends/set-remark"
//撤回消息
const val RECALL_MESSAGE = "message/withdraw"
}

View File

@ -4,6 +4,11 @@ import com.kaixed.kchat.data.local.entity.UserInfo
import com.kaixed.kchat.data.model.request.RegisterRequest
import com.kaixed.kchat.data.model.response.register.Register
import com.kaixed.kchat.network.ApiResponse
import com.kaixed.kchat.network.NetworkInterface.ACCESS_TOKEN
import com.kaixed.kchat.network.NetworkInterface.LOGIN_BY_TELEPHONE
import com.kaixed.kchat.network.NetworkInterface.LOGIN_BY_USERNAME
import com.kaixed.kchat.network.NetworkInterface.REFRESH_TOKEN
import com.kaixed.kchat.network.NetworkInterface.REGISTER
import retrofit2.http.Body
import retrofit2.http.Header
import retrofit2.http.POST
@ -15,32 +20,32 @@ import retrofit2.http.Query
*/
interface AuthApiService {
@POST("auth/access-token")
@POST(ACCESS_TOKEN)
suspend fun auth(
@Header("Authorization") token: String,
@Query("username") username: String
): ApiResponse<String>
@POST("auth/refresh-token")
@POST(REFRESH_TOKEN)
suspend fun refresh(
@Header("Authorization") token: String,
@Query("username") username: String
): ApiResponse<String>
// 登录接口(根据用户名登录)
@POST("auth/login/username")
@POST(LOGIN_BY_USERNAME)
suspend fun loginByUsername(
@Body requestParams: Map<String, String>,
): ApiResponse<UserInfo?>
// 登录接口(根据电话登录)
@POST("auth/login/telephone")
@POST(LOGIN_BY_TELEPHONE)
suspend fun loginByTelephone(
@Body requestParams: Map<String, String>,
): ApiResponse<UserInfo>
// 注册接口
@POST("auth/register")
@POST(REGISTER)
suspend fun register(
@Body registerRequest: RegisterRequest
): ApiResponse<Register>

View File

@ -1,6 +1,7 @@
package com.kaixed.kchat.network.service
import com.kaixed.kchat.network.ApiResponse
import com.kaixed.kchat.network.NetworkInterface.UPLOAD_FILE
import okhttp3.MultipartBody
import retrofit2.http.Multipart
import retrofit2.http.POST
@ -13,7 +14,7 @@ import retrofit2.http.Part
interface FileApiService {
@Multipart
@POST("file/upload")
@POST(UPLOAD_FILE)
suspend fun uploadFile(
@Part file: MultipartBody.Part
): ApiResponse<String>

View File

@ -4,6 +4,11 @@ import com.kaixed.kchat.data.local.entity.Contact
import com.kaixed.kchat.data.model.friend.FriendRequestItem
import com.kaixed.kchat.data.model.search.SearchUser
import com.kaixed.kchat.network.ApiResponse
import com.kaixed.kchat.network.NetworkInterface.ACCEPT_FRIEND_REQUEST
import com.kaixed.kchat.network.NetworkInterface.DELETE_FRIEND
import com.kaixed.kchat.network.NetworkInterface.GET_FRIEND_LIST
import com.kaixed.kchat.network.NetworkInterface.SEND_FRIEND_REQUEST
import com.kaixed.kchat.network.NetworkInterface.SET_FRIEND_REMARK
import retrofit2.http.Body
import retrofit2.http.Field
import retrofit2.http.FormUrlEncoded
@ -16,16 +21,16 @@ import retrofit2.http.Path
* @Date: 2025/1/25 16:38
*/
interface FriendApiService {
// 获取联系人请求列表
// 获取好友请求列表
@FormUrlEncoded
@POST("friend/request/list")
@POST(GET_FRIEND_LIST)
suspend fun getContactRequestList(
@Field("userId") username: String
): ApiResponse<List<FriendRequestItem>?>
// 接受联系人请求
// 接受好友请求
@FormUrlEncoded
@POST("friend/accept")
@POST(ACCEPT_FRIEND_REQUEST)
suspend fun acceptContactRequest(
@Field("requestId") contactId: String,
@Field("receiverId") username: String,
@ -34,7 +39,7 @@ interface FriendApiService {
// 添加联系人
@FormUrlEncoded
@POST("friend/request")
@POST(SEND_FRIEND_REQUEST)
suspend fun addContact(
@Field("senderId") senderId: String,
@Field("receiverId") receiverId: String,
@ -54,7 +59,7 @@ interface FriendApiService {
): ApiResponse<List<Contact>?>
// 删除联系人
@POST("friends/delete")
@POST(DELETE_FRIEND)
suspend fun deleteContact(
@Field("userId") username: String,
@Field("contactId") contactId: String,
@ -62,7 +67,7 @@ interface FriendApiService {
// 设置好友备注
@FormUrlEncoded
@POST("friend/remark")
@POST(SET_FRIEND_REMARK)
suspend fun setRemark(
@Field("userId") userId: String,
@Field("contactId") contactId: String,

View File

@ -5,6 +5,11 @@ import com.kaixed.kchat.data.model.request.UserRequest
import com.kaixed.kchat.data.model.response.search.User
import com.kaixed.kchat.data.model.search.SearchUser
import com.kaixed.kchat.network.ApiResponse
import com.kaixed.kchat.network.NetworkInterface.GET_FRIENDS
import com.kaixed.kchat.network.NetworkInterface.GET_USER_INFO
import com.kaixed.kchat.network.NetworkInterface.UPDATE_PASSWORD
import com.kaixed.kchat.network.NetworkInterface.UPDATE_USER_INFO
import com.kaixed.kchat.network.NetworkInterface.UPLOAD_AVATAR
import okhttp3.MultipartBody
import retrofit2.http.Body
import retrofit2.http.Multipart
@ -18,33 +23,33 @@ import retrofit2.http.Part
interface UserApiService {
// 获取用户列表
@POST("friends/list")
@POST(GET_FRIENDS)
suspend fun fetchUserList(
@Body requestParams: Map<String, String>
): ApiResponse<List<User>>
// 获取用户信息
@POST("users/info/fetch")
@POST(GET_USER_INFO)
suspend fun getUserInfo(
@Body requestParams: Map<String, String>
): ApiResponse<SearchUser>
// 更改昵称接口
@POST("users/info")
@POST(UPDATE_USER_INFO)
suspend fun changeNickname(
@Body userRequest: UserRequest
): ApiResponse<Boolean?>
// 上传头像接口
@Multipart
@POST("users/avatar")
@POST(UPLOAD_AVATAR)
suspend fun uploadAvatar(
@Part("username") username: String,
@Part file: MultipartBody.Part
): ApiResponse<String>
// 更改密码
@POST("users/password")
@POST(UPDATE_PASSWORD)
suspend fun updatePassword(
@Body updatePasswordRequest: UpdatePasswordRequest
): ApiResponse<String>

View File

@ -7,6 +7,13 @@ import androidx.activity.enableEdgeToEdge
import com.kaixed.kchat.databinding.ActivityAddFriendsBinding
import com.kaixed.kchat.ui.base.BaseActivity
/**
* 该文件包含用于兼容不同 Android 版本的 Intent 获取 Parcelable 数据的扩展函数
* 通过扩展函数 getParcelableExtraCompat 解决了低版本和高版本 Android 系统间的差异
*
* @author kaixed
* @since 2025年1月26日
*/
class AddFriendsActivity : BaseActivity<ActivityAddFriendsBinding>() {
private val context: Context by lazy { this }
@ -18,15 +25,21 @@ class AddFriendsActivity : BaseActivity<ActivityAddFriendsBinding>() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
initView()
}
override fun initData() {
}
private fun initView() {
override fun observeData() {
}
override fun setupListeners() {
}
override fun initView() {
binding.ivBack.setOnClickListener {
finish()
}

View File

@ -21,11 +21,13 @@ class ApplyAddFriendActivity : BaseActivity<ActivityApplyAddFriendBinding>() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setListener()
observeData()
}
private fun observeData() {
override fun initView() {
}
override fun observeData() {
contactViewModel.addContactResult
.observe(this) { result ->
result.onSuccess {
@ -36,7 +38,7 @@ class ApplyAddFriendActivity : BaseActivity<ActivityApplyAddFriendBinding>() {
}
}
private fun setListener() {
override fun setupListeners() {
binding.tvSendApply.setOnClickListener {
sendContactRequest(contactId)
}

View File

@ -12,26 +12,14 @@ import com.kaixed.kchat.ui.base.BaseActivity
class ApplyFriendsDetailActivity : BaseActivity<ActivityApplyFriendsDetailBinding>() {
override fun inflateBinding(): ActivityApplyFriendsDetailBinding {
return ActivityApplyFriendsDetailBinding.inflate(layoutInflater)
}
private var user: SearchUser? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent()
setOnClick()
}
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
override fun initData() {
user = intent.getParcelableExtra("user", SearchUser::class.java)
}
private fun setContent() {
override fun initView() {
val nickname = user?.nickname
val signature = user?.signature
val avatarUrl = user?.avatarUrl
@ -43,7 +31,12 @@ class ApplyFriendsDetailActivity : BaseActivity<ActivityApplyFriendsDetailBindin
Glide.with(this).load(avatarUrl).into(binding.ifvAvatar)
}
private fun setOnClick() {
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
override fun initData() {
user = intent.getParcelableExtra("user", SearchUser::class.java)
}
override fun setupListeners() {
binding.tvAddContact.setOnClickListener {
val contactId = user?.username
contactId?.let {
@ -60,4 +53,12 @@ class ApplyFriendsDetailActivity : BaseActivity<ActivityApplyFriendsDetailBindin
startActivity(intent)
}
}
override fun observeData() {
}
override fun inflateBinding(): ActivityApplyFriendsDetailBinding {
return ActivityApplyFriendsDetailBinding.inflate(layoutInflater)
}
}

View File

@ -28,11 +28,6 @@ class ApproveContactRequestActivity : BaseActivity<ActivityApproveContactRequest
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent()
setOnClickListener()
}
override fun initData() {
@ -41,18 +36,22 @@ class ApproveContactRequestActivity : BaseActivity<ActivityApproveContactRequest
contactId = intent.getStringExtra("contactId").toString()
}
@SuppressLint("SetTextI18n")
private fun setContent() {
binding.etRemark.setText(nickname)
binding.tvMessage.text = "$message"
override fun observeData() {
}
private fun setOnClickListener() {
override fun setupListeners() {
binding.tvFinish.setOnClickListener {
acceptContactRequest(contactId!!, nickname!!)
acceptContactRequest(contactId, nickname)
}
}
@SuppressLint("SetTextI18n")
override fun initView() {
binding.etRemark.setText(nickname)
binding.tvMessage.text = "$message"
}
private fun acceptContactRequest(contactId: String, nickname: String) {
contactViewModel.acceptContactRequestResult
.observe(this) { result ->

View File

@ -1,63 +1,45 @@
package com.kaixed.kchat.ui.activity
import android.content.Intent
import android.os.Build
import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.annotation.RequiresApi
import com.bumptech.glide.Glide
import com.kaixed.kchat.databinding.ActivityApproveDetailBinding
import com.kaixed.kchat.data.model.friend.FriendRequestItem
import com.kaixed.kchat.databinding.ActivityApproveDetailBinding
import com.kaixed.kchat.extensions.getParcelableExtraCompat
import com.kaixed.kchat.ui.base.BaseActivity
class ApproveDetailActivity : BaseActivity<ActivityApproveDetailBinding>() {
private var request: FriendRequestItem? = null
override fun inflateBinding(): ActivityApproveDetailBinding {
return ActivityApproveDetailBinding.inflate(layoutInflater)
}
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
handleIntent(intent)
setContent()
setOnClick()
}
override fun initData() {
request = intent.getParcelableExtraCompat("request")
}
override fun observeData() {
}
private fun handleIntent(intent: Intent) {
request = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
intent.getParcelableExtra("request", FriendRequestItem::class.java)
} else {
@Suppress("DEPRECATION")
intent.getParcelableExtra("request") as? FriendRequestItem
override fun initView() {
request?.let {
val nickname = it.nickname
val signature = it.signature
val avatarUrl = it.avatarUrl
binding.tvContactName.text = nickname
binding.tvContactSignature.text = signature
binding.tvSignatureContent.text = signature
Glide.with(this).load(avatarUrl).into(binding.ifvAvatar)
}
}
private fun setContent() {
if (request == null) {
return
}
val nickname = request?.nickname
val signature = request?.signature
val avatarUrl = request?.avatarUrl
binding.tvContactName.text = nickname
binding.tvContactSignature.text = signature
binding.tvSignatureContent.text = signature
Glide.with(this).load(avatarUrl).into(binding.ifvAvatar)
}
private fun setOnClick() {
override fun setupListeners() {
binding.ctl.setOnSettingClickListener {
val intent = Intent(this, DataSettingActivity::class.java)
startActivity(intent)
@ -73,4 +55,8 @@ class ApproveDetailActivity : BaseActivity<ActivityApproveDetailBinding>() {
)
}
}
override fun inflateBinding(): ActivityApproveDetailBinding {
return ActivityApproveDetailBinding.inflate(layoutInflater)
}
}

View File

@ -116,13 +116,9 @@ class ChatActivity : BaseActivity<ActivityChatBinding>(), OnItemClickListener,
super.onCreate(savedInstanceState)
enableEdgeToEdge()
EventBus.getDefault().register(this)
observeLiveData()
firstLoadData()
initView()
setListener()
bindWebSocketService()
setPanelChange()
observeStateFlow()
if (isSearchHistory) {
val size = MessagesManager.queryHistory(msgLocalId)
binding.recycleChatList.smoothScrollToPosition(size - 1)
@ -136,17 +132,6 @@ class ChatActivity : BaseActivity<ActivityChatBinding>(), OnItemClickListener,
}
}
private fun observeStateFlow() {
lifecycleScope.launch {
MessagesManager.messages.collect {
chatAdapter?.submitList(it)
binding.recycleChatList.post {
binding.recycleChatList.smoothScrollToPosition(0)
}
}
}
}
private val connection: ServiceConnection = object : ServiceConnection {
override fun onServiceConnected(className: ComponentName, service: IBinder) {
val binder = service as LocalBinder
@ -266,7 +251,114 @@ class ChatActivity : BaseActivity<ActivityChatBinding>(), OnItemClickListener,
}
}
private fun setListener() {
private fun setupFunctionPanel() {
strings?.add("[委屈]")
val mEmojiAdapter = EmojiAdapter(strings)
mEmojiAdapter.setOnItemClickListener(this)
binding.rvEmoji.adapter = mEmojiAdapter
val gridLayoutManager = GridLayoutManager(this, 7)
binding.rvEmoji.layoutManager = gridLayoutManager
val map: MutableMap<String, Int> = mutableMapOf()
map["相册"] = R.drawable.ic_filled_picture
map["拍摄"] = R.drawable.ic_filled_camera
map["位置"] = R.drawable.ic_filled_location
map["名片"] = R.drawable.ic_filled_personal
map["转账"] = R.drawable.ic_filled_transfer
map["文件"] = R.drawable.ic_folder_filled
map["语音输入"] = R.drawable.ic_filled_voiceinput
map["我的收藏"] = R.drawable.ic_filled_favorites
val functionItems = mutableListOf<FunctionItem>()
for (i in map.keys) {
functionItems.add(FunctionItem(i, map[i]!!))
}
val functionPanelAdapter = FunctionPanelAdapter(functionItems, this)
functionPanelAdapter.setOnItemClickListener(this)
binding.gvFunctionPanel.adapter = functionPanelAdapter
}
override fun onItemClick(position: Int) {
runOnUiThread {
val editable = binding.etInput.text
// 获取当前光标位置
val index = binding.etInput.selectionStart
// 获取将要插入的表情符号
val emoji = strings!![position]
// 使用 ImageSpanUtil 插入表情符号
insertEmoji(
context, editable, binding.etInput.textSize.toInt(), emoji, index
)
// 设置新的光标位置
binding.etInput.setSelection(index + emoji.length)
}
}
override fun initView() {
binding.ctb.setUnReadCount(ConversationManager.unReadMsgCount)
setupFunctionPanel()
setRecycleView()
contactNickname?.let {
binding.ctb.setTitleName(contactNickname!!)
}
setPanel()
}
private fun setPanel() {
val layoutParams = binding.clBottomPanel.layoutParams
layoutParams.height = softKeyboardHeight
binding.clBottomPanel.layoutParams = layoutParams
}
override fun initData() {
contactId = intent.getStringExtra("contactId").toString()
contactNickname = intent.getStringExtra("contactNickname")
isSearchHistory = intent.getBooleanExtra("isSearchHistory", false) == true
msgLocalId = intent.getLongExtra("msgLocalId", 0)
binding.ctb.setTitleName(contactNickname!!)
softKeyboardHeight = getKeyboardHeight()
mmkv.putString(CURRENT_CONTACT_ID, contactId)
}
private fun setRecycleView() {
val layoutManager = LinearLayoutManager(this)
layoutManager.reverseLayout = true
binding.recycleChatList.layoutManager = layoutManager
chatAdapter = ChatAdapter(this)
binding.recycleChatList.adapter = chatAdapter
}
private fun firstLoadData() {
MessagesManager.setContactId(contactId)
MessagesManager.firstLoadMessages()
}
private fun loadMoreMessages() {
MessagesManager.loadMoreMessages()
}
override fun observeData() {
webSocketService!!.messageLivedata.observe(this) {
it?.let {
handleMsg(it)
}
}
lifecycleScope.launch {
MessagesManager.messages.collect {
chatAdapter?.submitList(it)
binding.recycleChatList.post {
binding.recycleChatList.smoothScrollToPosition(0)
}
}
}
}
override fun setupListeners() {
binding.etInput.setOnClickListener {
if (hasSoftInput()){
MMKV.defaultMMKV().encode(KEYBOARD_HEIGHT, max(getSoftInputHeight(), 300))
@ -324,108 +416,6 @@ class ChatActivity : BaseActivity<ActivityChatBinding>(), OnItemClickListener,
}
}
private fun setupFunctionPanel() {
strings?.add("[委屈]")
val mEmojiAdapter = EmojiAdapter(strings)
mEmojiAdapter.setOnItemClickListener(this)
binding.rvEmoji.adapter = mEmojiAdapter
val gridLayoutManager = GridLayoutManager(this, 7)
binding.rvEmoji.layoutManager = gridLayoutManager
val map: MutableMap<String, Int> = mutableMapOf()
map["相册"] = R.drawable.ic_filled_picture
map["拍摄"] = R.drawable.ic_filled_camera
map["位置"] = R.drawable.ic_filled_location
map["名片"] = R.drawable.ic_filled_personal
map["转账"] = R.drawable.ic_filled_transfer
map["文件"] = R.drawable.ic_folder_filled
map["语音输入"] = R.drawable.ic_filled_voiceinput
map["我的收藏"] = R.drawable.ic_filled_favorites
val functionItems = mutableListOf<FunctionItem>()
for (i in map.keys) {
functionItems.add(FunctionItem(i, map[i]!!))
}
val functionPanelAdapter = FunctionPanelAdapter(functionItems, this)
functionPanelAdapter.setOnItemClickListener(this)
binding.gvFunctionPanel.adapter = functionPanelAdapter
}
override fun onItemClick(position: Int) {
runOnUiThread {
val editable = binding.etInput.text
// 获取当前光标位置
val index = binding.etInput.selectionStart
// 获取将要插入的表情符号
val emoji = strings!![position]
// 使用 ImageSpanUtil 插入表情符号
insertEmoji(
context, editable, binding.etInput.textSize.toInt(), emoji, index
)
// 设置新的光标位置
binding.etInput.setSelection(index + emoji.length)
}
}
private fun initView() {
binding.ctb.setUnReadCount(ConversationManager.unReadMsgCount)
setupFunctionPanel()
setRecycleView()
contactNickname?.let {
binding.ctb.setTitleName(contactNickname!!)
}
setPanel()
}
private fun setPanel() {
val layoutParams = binding.clBottomPanel.layoutParams
layoutParams.height = softKeyboardHeight
binding.clBottomPanel.layoutParams = layoutParams
}
override fun initData() {
contactId = intent.getStringExtra("contactId").toString()
contactNickname = intent.getStringExtra("contactNickname")
isSearchHistory = intent.getBooleanExtra("isSearchHistory", false) == true
msgLocalId = intent.getLongExtra("msgLocalId", 0)
binding.ctb.setTitleName(contactNickname!!)
softKeyboardHeight = getKeyboardHeight()
mmkv.putString(CURRENT_CONTACT_ID, contactId)
}
private fun setRecycleView() {
val layoutManager = LinearLayoutManager(this)
layoutManager.reverseLayout = true
binding.recycleChatList.layoutManager = layoutManager
chatAdapter = ChatAdapter(this)
binding.recycleChatList.adapter = chatAdapter
}
private fun firstLoadData() {
MessagesManager.setContactId(contactId)
MessagesManager.firstLoadMessages()
}
private fun loadMoreMessages() {
MessagesManager.loadMoreMessages()
}
private fun observeLiveData() {
if (webSocketService == null) {
return
}
webSocketService!!.messageLivedata.observe(this) {
it?.let {
handleMsg(it)
}
}
}
private fun handleMsg(messages: Messages) {
MessagesManager.receiveMessage(messages)
binding.recycleChatList.smoothScrollToPosition(0)

View File

@ -25,13 +25,9 @@ class ChatDetailActivity : BaseActivity<ActivityChatDetailBinding>() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
initView()
setListener()
}
private fun initView() {
override fun initView() {
Glide.with(this).load(contact?.avatarUrl).into(binding.ifvAvatar)
binding.tvContactName.text = contact?.remark ?: contact?.nickname
}
@ -41,7 +37,11 @@ class ChatDetailActivity : BaseActivity<ActivityChatDetailBinding>() {
binding.ciSetDisturb.setSwitchChecked(contact?.doNotDisturb ?: false)
}
private fun setListener() {
override fun observeData() {
}
override fun setupListeners() {
binding.ciSetDisturb.sbSwitch.setOnCheckedChangeListener(object :
SwitchButton.OnCheckedChangeListener {
override fun onCheckedChanged(button: SwitchButton, isChecked: Boolean) {

View File

@ -21,7 +21,14 @@ class ContactPermissionActivity : BaseActivity<ActivityContactPermissionBinding>
override fun initData() {
}
private fun initView() {
override fun observeData() {
}
override fun setupListeners() {
}
override fun initView() {
binding.ciChat.setYesSelected(false)
}
}

View File

@ -13,7 +13,6 @@ import com.kaixed.kchat.viewmodel.ContactViewModel
class ContactRequestListActivity : BaseActivity<ActivityContactRequestListBinding>() {
private var items = mutableListOf<FriendRequestItem>()
private val contactViewModel: ContactViewModel by viewModels()
@ -38,10 +37,19 @@ class ContactRequestListActivity : BaseActivity<ActivityContactRequestListBindin
binding.recycleFriendRequestList.adapter = contactRequestListAdapter
}
override fun initView() {
}
override fun initData() {
}
override fun observeData() {
}
override fun setupListeners() {
}
private fun getItems() {
contactViewModel.contactRequestListResult
.observe(this) { result ->

View File

@ -5,17 +5,18 @@ import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.constraintlayout.widget.ConstraintSet
import com.kaixed.kchat.databinding.ActivityContactUpdatesBinding
import com.kaixed.kchat.ui.base.BaseActivity
import com.kaixed.kchat.utils.ScreenUtils
class ContactUpdatesActivity : AppCompatActivity() {
class ContactUpdatesActivity : BaseActivity<ActivityContactUpdatesBinding>() {
private lateinit var binding: ActivityContactUpdatesBinding
override fun inflateBinding(): ActivityContactUpdatesBinding {
return ActivityContactUpdatesBinding.inflate(layoutInflater)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
binding = ActivityContactUpdatesBinding.inflate(layoutInflater)
setContentView(binding.root)
val constraintSet = ConstraintSet()
constraintSet.clone(binding.main)
@ -37,4 +38,16 @@ class ContactUpdatesActivity : AppCompatActivity() {
)
constraintSet.applyTo(binding.main)
}
override fun initView() {
}
override fun initData() {
}
override fun observeData() {
}
override fun setupListeners() {
}
}

View File

@ -36,16 +36,49 @@ class ContactsDetailActivity : BaseActivity<ActivityContactsDetailBinding>() {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setOnListener()
updateContent()
}
override fun initView() {
}
override fun initData() {
contactId = intent.getStringExtra("contactId")
isMine = intent.getBooleanExtra("isMine", false)
}
override fun observeData() {
}
override fun setupListeners() {
binding.tvSendMessage.setOnClickListener {
startActivity(Intent(this, ChatActivity::class.java).apply {
putExtra("contactId", contactId)
putExtra("contactNickname", contactId)
})
}
binding.ciSetRemarkAndLabel.setOnClickListener {
startActivity(Intent(this, SetRemarkAndLabelActivity::class.java).apply {
putExtra("contactId", contactId)
putExtra("contactNickname", contactNickname)
})
}
binding.ctb.setOnSettingClickListener {
startActivity(Intent(this, DataSettingActivity::class.java).apply {
putExtra("contactId", contactId)
})
}
binding.ciPermission.setOnClickListener {
startActivity(
Intent(this, ContactPermissionActivity::class.java)
)
}
}
@SuppressLint("SetTextI18n")
private fun updateContent() {
if (isMine) {
@ -83,34 +116,6 @@ class ContactsDetailActivity : BaseActivity<ActivityContactsDetailBinding>() {
updateContent()
}
private fun setOnListener() {
binding.tvSendMessage.setOnClickListener {
startActivity(Intent(this, ChatActivity::class.java).apply {
putExtra("contactId", contactId)
putExtra("contactNickname", contactId)
})
}
binding.ciSetRemarkAndLabel.setOnClickListener {
startActivity(Intent(this, SetRemarkAndLabelActivity::class.java).apply {
putExtra("contactId", contactId)
putExtra("contactNickname", contactNickname)
})
}
binding.ctb.setOnSettingClickListener {
startActivity(Intent(this, DataSettingActivity::class.java).apply {
putExtra("contactId", contactId)
})
}
binding.ciPermission.setOnClickListener {
startActivity(
Intent(this, ContactPermissionActivity::class.java)
)
}
}
private fun getContactInfo(contactId: String): Contact {
return DataBase.contactBox
.query(Contact_.username.equal(contactId))

View File

@ -33,10 +33,20 @@ class DataSettingActivity : BaseActivity<ActivityDataSettingBinding>(),
handleIntent(intent)
}
override fun initView() {
}
override fun initData() {
}
override fun observeData() {
}
override fun setupListeners() {
}
private fun handleIntent(intent: Intent) {
contactId = intent.getStringExtra("contactId") ?: ""
}

View File

@ -38,21 +38,21 @@ class FriendCircleActivity : BaseActivity<ActivityFriendCircleBinding>() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
initView()
setListener()
}
private fun setListener() {
binding.ivBack.setOnClickListener {
finish()
}
}
override fun initData() {
getItems()
}
override fun observeData() {
}
override fun setupListeners() {
binding.ivBack.setOnClickListener {
finish()
}
}
private fun getItems() {
val img = listOf(
R.drawable.ic_1,
@ -90,7 +90,7 @@ class FriendCircleActivity : BaseActivity<ActivityFriendCircleBinding>() {
@SuppressLint("NewApi")
private fun initView() {
override fun initView() {
setContent()
binding.clTool.apply {
updateLayoutParams {

View File

@ -35,10 +35,19 @@ class LaunchActivity : BaseActivity<ActivityLaunchBinding>() {
setOnClickListener()
}
override fun initView() {
}
override fun initData() {
}
override fun observeData() {
}
override fun setupListeners() {
}
@SuppressLint("DiscouragedApi", "InternalInsetResource")
private fun getStatusBarHeight(): Int {
var result = 0

View File

@ -58,7 +58,7 @@ class LoginActivity : BaseActivity<ActivityLoginBinding>() {
}
}
private fun initView() {
override fun initView() {
setupLoginView()
}
@ -210,6 +210,11 @@ class LoginActivity : BaseActivity<ActivityLoginBinding>() {
}
override fun initData() {}
override fun observeData() {
}
override fun setupListeners() {
}
override fun inflateBinding(): ActivityLoginBinding {
return ActivityLoginBinding.inflate(layoutInflater)

View File

@ -180,6 +180,12 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
}
}
override fun observeData() {
}
override fun setupListeners() {
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onMessageEvent(unreadEvent: UnreadEvent) {
val unreadCount = unreadEvent.unreadCount
@ -191,7 +197,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
}
private fun initView() {
override fun initView() {
navItems.forEachIndexed { index, navItem ->
navItem.container.setOnClickListener {
updateSelection(index)

View File

@ -65,6 +65,9 @@ class ProfileDetailActivity : BaseActivity<ActivityProfileDetailBinding>() {
updateContent(username)
}
override fun initView() {
}
override fun initData() {
getImageLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
@ -79,6 +82,12 @@ class ProfileDetailActivity : BaseActivity<ActivityProfileDetailBinding>() {
}
}
override fun observeData() {
}
override fun setupListeners() {
}
private fun updateAvatar(uri: Uri) {
val filePath = getPathFromUri(uri)

View File

@ -31,6 +31,9 @@ class QrCodeActivity : BaseActivity<ActivityQrCodeBinding>() {
}
}
override fun initView() {
}
override fun initData() {
setupQrcode()
binding.tvNickname.text = getNickName()
@ -38,6 +41,12 @@ class QrCodeActivity : BaseActivity<ActivityQrCodeBinding>() {
.into(binding.ifvAvatar)
}
override fun observeData() {
}
override fun setupListeners() {
}
private fun setupQrcode() {
val content = "kchat@${getUsername()}"
val type = HmsScan.QRCODE_SCAN_TYPE

View File

@ -33,10 +33,6 @@ class RegisterActivity : BaseActivity<ActivityRegisterBinding>() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
initView()
setOnClickListener()
}
override fun initData() {
@ -53,7 +49,10 @@ class RegisterActivity : BaseActivity<ActivityRegisterBinding>() {
}
}
private fun setOnClickListener() {
override fun observeData() {
}
override fun setupListeners() {
binding.ivClose.setOnClickListener {
finish()
}
@ -95,7 +94,7 @@ class RegisterActivity : BaseActivity<ActivityRegisterBinding>() {
}
private fun initView() {
override fun initView() {
setupTip()
setOnEtChangeListener()
}

View File

@ -8,8 +8,8 @@ import androidx.activity.viewModels
import androidx.core.widget.addTextChangedListener
import com.kaixed.kchat.data.local.box.ObjectBox.getBoxStore
import com.kaixed.kchat.data.local.entity.UserInfo
import com.kaixed.kchat.databinding.ActivityRenameBinding
import com.kaixed.kchat.data.model.request.UserRequest
import com.kaixed.kchat.databinding.ActivityRenameBinding
import com.kaixed.kchat.ui.base.BaseActivity
import com.kaixed.kchat.ui.widget.LoadingDialogFragment
import com.kaixed.kchat.utils.ConstantsUtils.getNickName
@ -19,8 +19,6 @@ import io.objectbox.Box
class RenameActivity : BaseActivity<ActivityRenameBinding>() {
private val userInfoBox: Box<UserInfo> by lazy { getBoxStore().boxFor(UserInfo::class.java) }
private val userViewModel: UserViewModel by viewModels()
private var oldNickname: String = ""
@ -56,10 +54,19 @@ class RenameActivity : BaseActivity<ActivityRenameBinding>() {
}
}
override fun initView() {
}
override fun initData() {
oldNickname = getNickName()
}
override fun observeData() {
}
override fun setupListeners() {
}
private fun updateNicknamesByCondition(newNickname: String, username: String) {
binding.ctb.setBtnEnable(false)
val dialog: LoadingDialogFragment = LoadingDialogFragment.newInstance("正在保存")

View File

@ -86,6 +86,15 @@ class ScanActivity : BaseActivity<ActivityScanBinding>() {
}
override fun initView() {
}
override fun setupListeners() {
}
override fun observeData() {
}
private fun initPermission() {
ActivityCompat.requestPermissions(
this,

View File

@ -40,13 +40,22 @@ class SearchActivity : BaseActivity<ActivitySearchBinding>() {
}
override fun initView() {
}
override fun setupListeners() {
}
override fun observeData() {
}
private fun setupViewVisibility(length: Int) {
binding.rvSearchResult.visibility = if (length > 0) View.VISIBLE else View.INVISIBLE
binding.tvPageSetting.visibility = if (length > 0) View.INVISIBLE else View.VISIBLE
}
private fun setupRecycleView() {
binding!!.rvSearchResult.edgeEffectFactory = object : EdgeEffectFactory() {
binding.rvSearchResult.edgeEffectFactory = object : EdgeEffectFactory() {
override fun createEdgeEffect(view: RecyclerView, direction: Int): EdgeEffect {
return object : EdgeEffect(view.context) {
override fun onPull(deltaDistance: Float, displacement: Float) {

View File

@ -140,4 +140,13 @@ class SearchChatHistory : BaseActivity<ActivitySearchChatHistoryBinding>() {
contactAvatarUrl = intent?.getStringExtra("contactAvatarUrl") ?: ""
contactId = intent?.getStringExtra("contactId") ?: ""
}
override fun initView() {
}
override fun setupListeners() {
}
override fun observeData() {
}
}

View File

@ -132,11 +132,17 @@ class SearchFriendsActivity : BaseActivity<ActivitySearchFriendsBinding>() {
override fun initData() {
}
private fun initView() {
override fun initView() {
binding.clFriends.visibility = View.INVISIBLE
binding.tvTitle.visibility = View.INVISIBLE
}
override fun setupListeners() {
}
override fun observeData() {
}
private fun searchUser(username: String) {
contactViewModel.searchContact(username)
}

View File

@ -4,13 +4,28 @@ import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import com.kaixed.kchat.databinding.ActivityServiceDetailBinding
import com.kaixed.kchat.ui.base.BaseActivity
class ServiceDetailActivity : BaseActivity<ActivityServiceDetailBinding>() {
class ServiceDetailActivity : AppCompatActivity() {
private lateinit var binding: ActivityServiceDetailBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
binding = ActivityServiceDetailBinding.inflate(layoutInflater)
setContentView(binding.root)
}
override fun initData() {
}
override fun initView() {
}
override fun setupListeners() {
}
override fun observeData() {
}
override fun inflateBinding(): ActivityServiceDetailBinding {
return ActivityServiceDetailBinding.inflate(layoutInflater)
}
}

View File

@ -4,19 +4,28 @@ import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import com.kaixed.kchat.databinding.ActivitySetRemarkBinding
import com.kaixed.kchat.ui.base.BaseActivity
class SetRemarkActivity : AppCompatActivity() {
private lateinit var binding: ActivitySetRemarkBinding
class SetRemarkActivity : BaseActivity<ActivitySetRemarkBinding>() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
binding = ActivitySetRemarkBinding.inflate(layoutInflater)
setContentView(binding.root)
setOnClickListener()
}
private fun setOnClickListener() {
override fun initData() {
}
override fun initView() {
}
override fun setupListeners() {
binding.ivBack.setOnClickListener { finish() }
}
override fun observeData() {
}
override fun inflateBinding(): ActivitySetRemarkBinding {
return ActivitySetRemarkBinding.inflate(layoutInflater)
}
}

View File

@ -25,10 +25,6 @@ import kotlinx.coroutines.launch
class SetRemarkAndLabelActivity : BaseActivity<ActivitySetRemarkAndLabelBinding>() {
private val contactBox by lazy {
getBoxStore().boxFor(Contact::class.java)
}
private var contactId: String? = null
private var hasFocus = false
@ -59,6 +55,15 @@ class SetRemarkAndLabelActivity : BaseActivity<ActivitySetRemarkAndLabelBinding>
remark = contact?.remark ?: contact?.nickname ?: ""
}
override fun initView() {
}
override fun setupListeners() {
}
override fun observeData() {
}
override fun onResume() {
super.onResume()
updateContent()

View File

@ -140,4 +140,13 @@ class SettingsActivity : BaseActivity<ActivitySettingBinding>(), View.OnClickLis
override fun initData() {
}
override fun initView() {
}
override fun setupListeners() {
}
override fun observeData() {
}
}

View File

@ -4,14 +4,8 @@ import android.os.Bundle
import android.text.Spannable
import android.text.SpannableString
import android.text.style.ClickableSpan
import android.text.util.Linkify
import android.util.Patterns
import android.view.View
import android.webkit.WebChromeClient
import android.webkit.WebResourceRequest
import android.webkit.WebView
import android.webkit.WebViewClient
import android.widget.ProgressBar
import android.widget.TextView
import androidx.activity.enableEdgeToEdge
import com.kaixed.kchat.databinding.ActivityTestBinding
@ -70,4 +64,13 @@ class TestActivity : BaseActivity<ActivityTestBinding>() {
}
override fun initView() {
}
override fun setupListeners() {
}
override fun observeData() {
}
}

View File

@ -57,6 +57,15 @@ class WebViewActivity : BaseActivity<ActivityWebViewBinding>() {
url = intent.getStringExtra("url") ?: ""
}
override fun initView() {
}
override fun setupListeners() {
}
override fun observeData() {
}
private fun setupWebView() {
// 启用 JavaScript
webView.settings.javaScriptEnabled = true

View File

@ -28,4 +28,13 @@ class AboutActivity : BaseActivity<ActivityAboutBinding>() {
loadingDialog.showLoading(supportFragmentManager)
}
}
override fun initView() {
}
override fun setupListeners() {
}
override fun observeData() {
}
}

View File

@ -16,16 +16,6 @@ class AccountSecurityActivity : BaseActivity<ActivityAccountSecurityBinding>() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setListener()
}
private fun setListener() {
binding.ciUpdatePassword.setOnClickListener {
startActivity(
Intent(this, UpdatePasswordActivity::class.java)
)
}
}
override fun initData() {
@ -36,4 +26,18 @@ class AccountSecurityActivity : BaseActivity<ActivityAccountSecurityBinding>() {
binding.ciTelephone.setItemDesc(telephone)
}
}
override fun initView() {
}
override fun setupListeners() {
binding.ciUpdatePassword.setOnClickListener {
startActivity(
Intent(this, UpdatePasswordActivity::class.java)
)
}
}
override fun observeData() {
}
}

View File

@ -17,10 +17,18 @@ class UpdateKidActivity : BaseActivity<ActivityUpdateKidBinding>() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.activity_update_kid)
}
override fun initData() {
}
override fun initView() {
}
override fun setupListeners() {
}
override fun observeData() {
}
}

View File

@ -83,6 +83,15 @@ class UpdatePasswordActivity : BaseActivity<ActivityUpdatePasswordBinding>() {
addEdittextFilter()
}
override fun initView() {
}
override fun setupListeners() {
}
override fun observeData() {
}
private val textWatch = object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}

View File

@ -22,4 +22,16 @@ class UpdateTelephoneActivity : BaseActivity<ActivityUpdateTelephoneBinding>() {
override fun initData() {
}
override fun initView() {
TODO("Not yet implemented")
}
override fun setupListeners() {
TODO("Not yet implemented")
}
override fun observeData() {
TODO("Not yet implemented")
}
}

View File

@ -14,13 +14,13 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
protected lateinit var binding: VB
abstract fun inflateBinding(): VB
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = inflateBinding()
setContentView(binding.root)
initData()
initView()
setupListeners()
AppManager.instance.addActivity(this)
}
@ -31,6 +31,12 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
abstract fun initData()
abstract fun initView()
abstract fun setupListeners()
abstract fun observeData()
fun toast(msg: String) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
}
@ -38,4 +44,6 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
fun toast() {
Toast.makeText(this, "暂未开发", Toast.LENGTH_SHORT).show()
}
abstract fun inflateBinding(): VB
}

View File

@ -10,6 +10,7 @@ import androidx.constraintlayout.widget.ConstraintLayout
import com.bumptech.glide.Glide
import com.kaixed.kchat.R
import com.kaixed.kchat.databinding.ItemCustomBinding
import com.kaixed.kchat.utils.ImageLoader
/**
* @Author: kaixed
@ -123,8 +124,7 @@ class CustomItem @JvmOverloads constructor(
if (itemIconRound > 0) {
binding.ivItemIcon.roundPercent = itemIconRound
Glide.with(context).load(itemIcon)
.into(binding.ivItemIcon)
ImageLoader.loadImage(context, itemIcon, binding.ivItemIcon)
}
} else {
binding.ivItemIcon.visibility = View.GONE

View File

@ -0,0 +1,54 @@
package com.kaixed.kchat.utils
import android.content.Context
import android.widget.ImageView
import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
object ImageLoader {
fun loadImage(context: Context, url: String, imageView: ImageView) {
Glide.with(context)
.load(url)
.into(imageView)
}
fun loadImage(context: Context, image: Int, imageView: ImageView) {
Glide.with(context)
.load(image)
.into(imageView)
}
fun loadImageWithPlaceholder(context: Context, url: String, imageView: ImageView, placeholderResId: Int) {
Glide.with(context)
.load(url)
.apply(getDefaultOptions().placeholder(placeholderResId))
.into(imageView)
}
fun loadImageWithError(context: Context, url: String, imageView: ImageView, errorResId: Int) {
Glide.with(context)
.load(url)
.apply(getDefaultOptions().error(errorResId))
.into(imageView)
}
fun loadImageCircle(context: Context, url: String, imageView: ImageView) {
Glide.with(context)
.load(url)
.apply(getCircleCropOptions())
.into(imageView)
}
private fun getDefaultOptions(): RequestOptions {
return RequestOptions()
.centerCrop() // 默认居中裁剪
.dontAnimate() // 禁止动画(如果不需要)
}
private fun getCircleCropOptions(): RequestOptions {
return RequestOptions()
.circleCrop() // 圆形裁剪
.dontAnimate() // 禁止动画
}
}