fix: 完善修改昵称功能

1.完善修改昵称功能
2.使用viewpager2重构主页4个fragment
3.完善好友列表的昵称备注显示
This commit is contained in:
糕小菜 2024-11-10 11:20:52 +08:00
parent 53bbc029b9
commit 15f9409618
37 changed files with 622 additions and 261 deletions

View File

@ -2,19 +2,7 @@
<project version="4">
<component name="GitCommitMessageStorage">
<option name="messageStorage">
<MessageStorage>
<option name="commitTemplate">
<CommitTemplate>
<option name="body" value="" />
<option name="changes" value="" />
<option name="closes" value="" />
<option name="scope" value="" />
<option name="skipCi" value="" />
<option name="subject" value="" />
<option name="type" value="refactor" />
</CommitTemplate>
</option>
</MessageStorage>
<MessageStorage />
</option>
</component>
</project>

View File

@ -1,4 +1,3 @@
<?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">

View File

@ -61,6 +61,7 @@ dependencies {
implementation(libs.okhttp)
implementation(libs.mmkv)
implementation(libs.gson)
implementation(libs.objectbox.kotlin)
debugImplementation(libs.objectbox.android.objectbrowser)
releaseImplementation(libs.objectbox.android)
implementation(libs.glide)
@ -74,6 +75,7 @@ dependencies {
implementation(libs.pinyin4j)
// implementation(libs.therouter)
// ksp(libs.therouter.ksp)
}

View File

@ -103,7 +103,7 @@
},
{
"id": "4:6179749773128044271",
"lastPropertyId": "9:468674632316468263",
"lastPropertyId": "11:814658585132653810",
"name": "Contact",
"properties": [
{
@ -146,12 +146,66 @@
"id": "9:468674632316468263",
"name": "showHeader",
"type": 1
},
{
"id": "10:7859238878302952317",
"name": "address",
"type": 9
},
{
"id": "11:814658585132653810",
"name": "gender",
"type": 9
}
],
"relations": []
},
{
"id": "5:2885532406154205395",
"lastPropertyId": "7:7996607163318458427",
"name": "UserInfo",
"properties": [
{
"id": "1:5301066467406579649",
"name": "id",
"type": 6,
"flags": 1
},
{
"id": "2:2771882473472315",
"name": "username",
"type": 9
},
{
"id": "3:1747776423604377158",
"name": "nickname",
"type": 9
},
{
"id": "4:1949755077968564625",
"name": "avatarUrl",
"type": 9
},
{
"id": "5:3873569992365810948",
"name": "signature",
"type": 9
},
{
"id": "6:3192923709951727610",
"name": "telephone",
"type": 9
},
{
"id": "7:7996607163318458427",
"name": "status",
"type": 9
}
],
"relations": []
}
],
"lastEntityId": "4:6179749773128044271",
"lastEntityId": "5:2885532406154205395",
"lastIndexId": "0:0",
"lastRelationId": "0:0",
"lastSequenceId": "0:0",

View File

@ -103,7 +103,7 @@
},
{
"id": "4:6179749773128044271",
"lastPropertyId": "8:2701112806559265255",
"lastPropertyId": "9:468674632316468263",
"name": "Contact",
"properties": [
{
@ -143,15 +143,59 @@
"type": 9
},
{
"id": "8:2701112806559265255",
"name": "showHead",
"id": "9:468674632316468263",
"name": "showHeader",
"type": 1
}
],
"relations": []
},
{
"id": "5:2885532406154205395",
"lastPropertyId": "7:7996607163318458427",
"name": "UserInfo",
"properties": [
{
"id": "1:5301066467406579649",
"name": "id",
"type": 6,
"flags": 1
},
{
"id": "2:2771882473472315",
"name": "username",
"type": 9
},
{
"id": "3:1747776423604377158",
"name": "nickname",
"type": 9
},
{
"id": "4:1949755077968564625",
"name": "avatarUrl",
"type": 9
},
{
"id": "5:3873569992365810948",
"name": "signature",
"type": 9
},
{
"id": "6:3192923709951727610",
"name": "telephone",
"type": 9
},
{
"id": "7:7996607163318458427",
"name": "status",
"type": 9
}
],
"relations": []
}
],
"lastEntityId": "4:6179749773128044271",
"lastEntityId": "5:2885532406154205395",
"lastIndexId": "0:0",
"lastRelationId": "0:0",
"lastSequenceId": "0:0",
@ -172,7 +216,8 @@
3687999935415577905,
5421937431301885220,
1118829668259721786,
1490263641107250384
1490263641107250384,
2701112806559265255
],
"retiredRelationUids": [],
"version": 1

View File

@ -1,6 +1,7 @@
package com.kaixed.kchat
import android.app.Application
import android.util.Log
import com.kaixed.kchat.data.objectbox.ObjectBox.get
import com.kaixed.kchat.data.objectbox.ObjectBox.init
import com.tencent.mmkv.MMKV
@ -13,11 +14,9 @@ import io.objectbox.android.Admin
class KchatApplication : Application() {
override fun onCreate() {
super.onCreate()
val rootDir = MMKV.initialize(this)
// Log.d("mmkv root: ", rootDir)
MMKV.initialize(this)
init(this)
val started = Admin(get()).start(this)
// Log.i("ObjectBoxAdmin", "Started: $started")
Admin(get()).start(this)
}
}

View File

@ -17,5 +17,7 @@ data class Contact(
var signature: String?,
var remark: String?,
var quanpin: String?,
var showHeader: Boolean?
var showHeader: Boolean?,
var address: String,
var gender: String
)

View File

@ -0,0 +1,23 @@
package com.kaixed.kchat.data.objectbox.entity
import io.objectbox.annotation.Entity
import io.objectbox.annotation.Id
import kotlinx.serialization.Serializable
/**
* @Author: kaixed
* @Date: 2024/11/6 19:24
*/
@Entity
@Serializable
data class UserInfo(
@Id
var id: Long = 0,
var username: String,
var nickname: String,
var avatarUrl: String,
var signature: String,
var telephone: String,
var status: String? = null
)

View File

@ -7,7 +7,7 @@ package com.kaixed.kchat.model.item
data class FriendItem(
val nickname: String,
val username: String,
val remark: String?,
val remark: String? = "",
var avatarUrl: String? = "",
var firstInSection: Boolean = false,
var pinyin: String? = ""

View File

@ -0,0 +1,16 @@
package com.kaixed.kchat.model.request
import kotlinx.serialization.Serializable
/**
* @Author: kaixed
* @Date: 2024/11/7 17:45
*/
@Serializable
data class UserRequest(
val username: String,
val password: String? = null,
val telephone: String? = null,
val nickname: String? = null,
val signature: String? = null,
)

View File

@ -4,9 +4,11 @@ import kotlinx.serialization.Serializable
@Serializable
data class Data(
var id: Long,
val avatarUrl: String,
val nickname: String,
val signature: String,
var telephone: String,
val status: String?,
val username: String,
)

View File

@ -1,10 +1,11 @@
package com.kaixed.kchat.model.response.login
import com.kaixed.kchat.data.objectbox.entity.UserInfo
import kotlinx.serialization.Serializable
@Serializable
data class Login(
val code: String,
val `data`: Data,
val `data`: UserInfo,
val msg: String,
)

View File

@ -0,0 +1,10 @@
package com.kaixed.kchat.model.response.user
import kotlinx.serialization.Serializable
@Serializable
data class ChangeNickname(
val code: String,
val `data`: String?,
val msg: String
)

View File

@ -0,0 +1,11 @@
package com.kaixed.kchat.network
/**
* @Author: kaixed
* @Date: 2024/11/6 22:42
*/
sealed class NetResult<out T> {
data class Loading<out T>(val message: String = "加载中...") : NetResult<T>()
data class Success<out T>(val data: T) : NetResult<T>()
data class Error<out T>(val exception: Throwable) : NetResult<T>()
}

View File

@ -0,0 +1,60 @@
package com.kaixed.kchat.network
import androidx.lifecycle.MutableLiveData
import kotlinx.serialization.KSerializer
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import okhttp3.Call
import okhttp3.Callback
import okhttp3.Response
import okio.IOException
/**
* @Author: kaixed
* @Date: 2024/11/6 22:54
*/
object NetworkHelper {
fun <T> makeRequest(
url: String,
requestBody: Any? = null,
method: String = "POST",
serializer: KSerializer<T>
): MutableLiveData<NetResult<T>> {
val netResultLiveData = MutableLiveData<NetResult<T>>()
netResultLiveData.postValue(NetResult.Loading())
val callback = object : Callback {
override fun onFailure(call: Call, e: IOException) {
netResultLiveData.postValue(NetResult.Error(e))
}
override fun onResponse(call: Call, response: Response) {
response.body?.string()?.let { responseBody ->
try {
val responseData = Json.decodeFromString(serializer, responseBody)
netResultLiveData.postValue(NetResult.Success(responseData))
} catch (e: Exception) {
netResultLiveData.postValue(NetResult.Error(e))
}
}
}
}
when (method) {
"POST" -> {
val json = Json.encodeToString(requestBody)
NetworkRequest().postAsync(url, json, callback)
}
"GET" -> {
NetworkRequest().getAsync(url, callback)
}
else -> {
netResultLiveData.postValue(NetResult.Error(IllegalArgumentException("不支持的请求方法")))
}
}
return netResultLiveData
}
}

View File

@ -25,4 +25,6 @@ object NetworkInterface {
const val FRIEND_LIST = "/friend/list"
const val FRIEND_REQUEST_LIST = "/friend/request/list"
const val ACCEPT_CONTACT_REQUEST = "/friend/accept"
const val UPDATE_USER_INFO = "/users/info"
}

View File

@ -1,6 +1,5 @@
package com.kaixed.kchat.network
import android.util.Log
import com.kaixed.kchat.network.OkhttpHelper.getInstance
import okhttp3.Callback
import okhttp3.MediaType.Companion.toMediaType

View File

@ -2,10 +2,13 @@ package com.kaixed.kchat.repository
import androidx.lifecycle.MutableLiveData
import com.kaixed.kchat.model.request.RegisterRequest
import com.kaixed.kchat.model.request.UserRequest
import com.kaixed.kchat.model.response.login.Login
import com.kaixed.kchat.model.response.register.Register
import com.kaixed.kchat.model.response.search.UserList
import com.kaixed.kchat.model.response.user.ChangeNickname
import com.kaixed.kchat.network.NetworkInterface.SERVER_URL
import com.kaixed.kchat.network.NetworkInterface.UPDATE_USER_INFO
import com.kaixed.kchat.network.NetworkInterface.USER_LIST
import com.kaixed.kchat.network.NetworkInterface.USER_LOGIN_BY_TELEPHONE
import com.kaixed.kchat.network.NetworkInterface.USER_LOGIN_BY_USERNAME
@ -119,4 +122,38 @@ class UserRepo {
)
return listMutableLiveData
}
fun changeNickname(username: String, nickname: String): MutableLiveData<Boolean> {
val listMutableLiveData = MutableLiveData<Boolean>()
val userRequest = UserRequest(
username = username,
nickname = nickname,
)
val json =
Json.encodeToString(UserRequest.serializer(), userRequest)
NetworkRequest().postAsync(
"$SERVER_URL$UPDATE_USER_INFO",
json,
object : Callback {
override fun onFailure(call: Call, e: IOException) {
listMutableLiveData.postValue(false)
}
override fun onResponse(call: Call, response: Response) {
response.body?.string()?.let { responseBody ->
val changeNickname = Json.decodeFromString<ChangeNickname>(responseBody)
when (changeNickname.code) {
"A0203" -> listMutableLiveData.postValue(true)
else -> listMutableLiveData.postValue(false)
}
}
}
}
)
return listMutableLiveData
}
}

View File

@ -2,15 +2,17 @@ package com.kaixed.kchat.ui.activity
import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import com.kaixed.kchat.databinding.ActivityContactsDetailBinding
import com.kaixed.kchat.ui.base.BaseActivity
class ContactsDetailActivity : BaseActivity<ActivityContactsDetailBinding>() {
override fun inflateBinding(): ActivityContactsDetailBinding {
return ActivityContactsDetailBinding.inflate(layoutInflater)
}
class ContactsDetailActivity : AppCompatActivity() {
private lateinit var binding: ActivityContactsDetailBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
binding = ActivityContactsDetailBinding.inflate(layoutInflater)
setContentView(binding.root)
}
}

View File

@ -6,11 +6,13 @@ import android.graphics.Rect
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.widget.Toast
import androidx.activity.enableEdgeToEdge
import androidx.activity.viewModels
import androidx.core.content.ContextCompat
import com.kaixed.kchat.R
import com.kaixed.kchat.data.objectbox.ObjectBox.get
import com.kaixed.kchat.data.objectbox.entity.UserInfo
import com.kaixed.kchat.data.objectbox.entity.UserInfo_
import com.kaixed.kchat.databinding.ActivityLoginBinding
import com.kaixed.kchat.ui.base.BaseActivity
import com.kaixed.kchat.utils.Constants.KEYBOARD_HEIGHT_RATIO
@ -20,15 +22,19 @@ import com.kaixed.kchat.utils.DensityUtil.dpToPx
import com.kaixed.kchat.utils.DrawableUtil.createDrawable
import com.kaixed.kchat.viewmodel.LoginViewModel
import com.tencent.mmkv.MMKV
import io.objectbox.Box
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class LoginActivity : BaseActivity<ActivityLoginBinding>() {
override fun inflateBinding(): ActivityLoginBinding {
return ActivityLoginBinding.inflate(layoutInflater)
}
private val mViewModel: LoginViewModel by viewModels()
private val userInfoBox: Box<UserInfo> by lazy { get().boxFor(UserInfo::class.java) }
private val mmkv by lazy { MMKV.mmkvWithID(MMKV_USER_SESSION) }
private var previousKeyboardHeight = 0
@ -48,28 +54,53 @@ class LoginActivity : BaseActivity<ActivityLoginBinding>() {
val username = binding.etUsername.text.toString().trim()
val password = binding.etPassword.text.toString().trim()
if (username.isEmpty() || password.isEmpty()) {
Toast.makeText(this, "请输入用户名或密码", Toast.LENGTH_SHORT).show()
}
mViewModel.login(username, password, loginByUsername).observe(this) { loginResult ->
loginResult?.let {
if (it.code == "200") {
login(username, password)
}
getKeyboardHeight()
}
private fun login(username: String, password: String) {
if (!loginByUsername) {
binding.etUsername.text.toString().length != 11
toast("请输入正确的手机号码")
return
}
if (username.isEmpty() || password.isEmpty()) {
toast("请输入${if (loginByUsername) "用户名" else "手机号"}或密码")
}
mViewModel.login(username, password, loginByUsername).observe(this) { loginResult ->
loginResult?.let {
when (it.code) {
"A0201" -> toast("用户账户不存在")
else -> {
mmkv.apply {
encode("username", loginResult.data.username)
encode("nickname", loginResult.data.nickname)
encode("avatarUrl", loginResult.data.avatarUrl)
}
MMKV.defaultMMKV().encode("userLoginStatus", true)
Toast.makeText(this, "登录成功", Toast.LENGTH_SHORT).show()
updateDb(loginResult.data)
navigateToMain()
} else {
Toast.makeText(this, "登录异常", Toast.LENGTH_SHORT).show()
}
} ?: Toast.makeText(this, "登录异常", Toast.LENGTH_SHORT).show()
}
} ?: toast("登录异常")
}
}
private fun updateDb(userInfo: UserInfo) {
CoroutineScope(Dispatchers.IO).launch {
val user = userInfoBox
.query(UserInfo_.username.equal(userInfo.username))
.build()
.findFirst()
if (user == null) {
userInfoBox.put(userInfo)
}
withContext(Dispatchers.Main) {
toast("登录成功")
}
}
getKeyboardHeight()
}
private fun initView() {
@ -157,4 +188,8 @@ class LoginActivity : BaseActivity<ActivityLoginBinding>() {
startActivity(intent)
finish()
}
override fun inflateBinding(): ActivityLoginBinding {
return ActivityLoginBinding.inflate(layoutInflater)
}
}

View File

@ -6,6 +6,8 @@ import androidx.activity.enableEdgeToEdge
import androidx.core.content.ContextCompat
import androidx.core.view.WindowCompat
import androidx.fragment.app.Fragment
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import com.kaixed.kchat.R
import com.kaixed.kchat.databinding.ActivityMainBinding
import com.kaixed.kchat.ui.base.BaseActivity
@ -15,17 +17,25 @@ import com.kaixed.kchat.ui.fragment.HomeFragment
import com.kaixed.kchat.ui.fragment.MineFragment
class MainActivity : BaseActivity<ActivityMainBinding>(), View.OnClickListener {
private var colorMain = 0
private var colorBlack = 0
companion object {
private const val KEY_HOME_FRAGMENT = 10001
private const val KEY_CONTACT_FRAGMENT = 10002
private const val KEY_DISCOVERY_FRAGMENT = 10003
private const val KEY_MINE_FRAGMENT = 10004
private const val KEY_HOME_FRAGMENT = 0
private const val KEY_CONTACT_FRAGMENT = 1
private const val KEY_DISCOVERY_FRAGMENT = 2
private const val KEY_MINE_FRAGMENT = 3
}
private val fragments = mutableMapOf<Int, Fragment>()
private val fragments = listOf<Fragment>(
HomeFragment(),
ContactFragment(),
DiscoveryFragment(),
MineFragment()
)
private lateinit var viewPagerAdapter: FragmentStateAdapter
override fun inflateBinding(): ActivityMainBinding {
return ActivityMainBinding.inflate(layoutInflater)
@ -35,46 +45,16 @@ class MainActivity : BaseActivity<ActivityMainBinding>(), View.OnClickListener {
WindowCompat.setDecorFitsSystemWindows(window, false)
super.onCreate(savedInstanceState)
enableEdgeToEdge()
// 设置透明状态栏
colorMain = ContextCompat.getColor(this, R.color.green)
colorBlack = ContextCompat.getColor(this, R.color.black)
initView()
initFragment()
initViewPager()
updateSelection(KEY_HOME_FRAGMENT)
}
private fun initFragment() {
fragments[KEY_HOME_FRAGMENT] = HomeFragment()
fragments[KEY_CONTACT_FRAGMENT] = ContactFragment()
fragments[KEY_DISCOVERY_FRAGMENT] = DiscoveryFragment()
fragments[KEY_MINE_FRAGMENT] = MineFragment()
supportFragmentManager.beginTransaction().apply {
fragments.values.forEach { fragment ->
add(R.id.fl_main, fragment, fragment.javaClass.simpleName)
hide(fragment)
}
commit()
}
showFragment(KEY_HOME_FRAGMENT)
}
private fun showFragment(key: Int) {
supportFragmentManager.beginTransaction().apply {
fragments.values.forEach { fragment ->
hide(fragment)
if (fragments[key] == fragment) {
show(fragment)
}
}
commit()
}
}
private fun initView() {
binding.clHome.setOnClickListener(this)
binding.clHome.tag = KEY_HOME_FRAGMENT
@ -89,9 +69,32 @@ class MainActivity : BaseActivity<ActivityMainBinding>(), View.OnClickListener {
binding.clMine.tag = KEY_MINE_FRAGMENT
}
private fun initViewPager() {
// 使用 FragmentStateAdapter 构造函数
viewPagerAdapter = object : FragmentStateAdapter(this) {
override fun getItemCount(): Int {
return fragments.size
}
override fun createFragment(position: Int): Fragment {
return fragments[position]
}
}
binding.viewPager.adapter = viewPagerAdapter
// binding.viewPager.isUserInputEnabled = false // 禁用滑动切换
binding.viewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
updateSelection(position)
}
})
}
override fun onClick(view: View) {
val key = view.tag as Int
showFragment(key)
binding.viewPager.currentItem = key
updateSelection(key)
}

View File

@ -22,8 +22,8 @@ class ProfileDetailActivity : BaseActivity<ActivityProfileDetailBinding>() {
}
setListener()
updateContent()
binding.ciNickname.setItemDesc(getNickName())
}
private fun setListener() {
@ -33,4 +33,13 @@ class ProfileDetailActivity : BaseActivity<ActivityProfileDetailBinding>() {
)
}
}
override fun onResume() {
super.onResume()
updateContent()
}
private fun updateContent() {
binding.ciNickname.setItemDesc(getNickName())
}
}

View File

@ -8,30 +8,33 @@ import android.text.SpannableString
import android.text.TextPaint
import android.text.method.LinkMovementMethod
import android.text.style.ClickableSpan
import android.util.Log
import android.view.View
import android.widget.Toast
import androidx.activity.enableEdgeToEdge
import androidx.activity.viewModels
import androidx.core.content.ContextCompat
import androidx.core.widget.addTextChangedListener
import com.kaixed.kchat.R
import com.kaixed.kchat.data.objectbox.ObjectBox.get
import com.kaixed.kchat.data.objectbox.entity.UserInfo
import com.kaixed.kchat.databinding.ActivityRegisterBinding
import com.kaixed.kchat.ui.base.BaseActivity
import com.kaixed.kchat.utils.DensityUtil.dpToPx
import com.kaixed.kchat.utils.DrawableUtil.createDrawable
import com.kaixed.kchat.viewmodel.UserViewModel
import io.objectbox.Box
class RegisterActivity : BaseActivity<ActivityRegisterBinding>() {
override fun inflateBinding(): ActivityRegisterBinding {
return ActivityRegisterBinding.inflate(layoutInflater)
}
private var tvContinueEnable: Boolean = false
private val mViewModel: UserViewModel by viewModels()
private val userInfoBox: Box<UserInfo> by lazy { get().boxFor(UserInfo::class.java) }
override fun inflateBinding(): ActivityRegisterBinding {
return ActivityRegisterBinding.inflate(layoutInflater)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
@ -47,48 +50,66 @@ class RegisterActivity : BaseActivity<ActivityRegisterBinding>() {
}
binding.tvContinue.setOnClickListener {
if (tvContinueEnable) {
val telephone = binding.etUsername.text.toString().trim()
val nickname = binding.etNickname.text.toString().trim()
val password = binding.etPassword.text.toString().trim()
val avatarUrl = ""
val signature = ""
onContinueClick()
mViewModel.register(nickname, password, avatarUrl, signature, telephone)
.observe(this) { registerResult ->
Log.d("haha", registerResult.toString())
when (registerResult?.code) {
"A0111" -> {
Toast.makeText(this, "用户名已存在,请重新注册", Toast.LENGTH_SHORT)
.show()
}
"200" -> {
Toast.makeText(
this,
"注册成功,请前往登录界面登录",
Toast.LENGTH_SHORT
)
.show()
startActivity(Intent(this, LoginActivity::class.java))
finish()
}
else -> {
Toast.makeText(
this,
"系统服务器异常",
Toast.LENGTH_SHORT
)
.show()
}
}
}
}
}
}
private fun onContinueClick() {
if (!tvContinueEnable) return
val telephone = binding.etUsername.text.toString().trim()
if (telephone.length != 11) {
toast("请输入正确的手机号")
return
}
val nickname = binding.etNickname.text.toString().trim()
val password = binding.etPassword.text.toString().trim()
val avatarUrl = ""
val signature = ""
registerAccount(nickname, password, avatarUrl, signature, telephone)
}
private fun registerAccount(
nickname: String,
password: String,
avatarUrl: String,
signature: String,
telephone: String
) {
mViewModel.register(nickname, password, avatarUrl, signature, telephone)
.observe(this) { registerResult ->
when (registerResult?.code) {
"A0111" -> toast("用户名已存在,请重新注册")
"200" -> registerResult.data?.username?.let {
onRegisterSuccess(
it,
nickname,
telephone
)
}
else -> toast("系统服务器异常")
}
}
}
private fun onRegisterSuccess(username: String, nickname: String, telephone: String) {
val userInfo = UserInfo(
username = username,
nickname = nickname,
avatarUrl = "",
signature = "",
telephone = telephone
)
userInfoBox.put(userInfo)
toast("注册成功,请前往登录界面登录")
startActivity(Intent(this, LoginActivity::class.java))
finish()
}
private fun initView() {
setupTip()
setOnEtChangeListener()
@ -124,14 +145,14 @@ class RegisterActivity : BaseActivity<ActivityRegisterBinding>() {
private fun setContinueButtonState(textColor: Int, backgroundColor: Int) {
binding.tvContinue.setTextColor(textColor)
binding.tvContinue.background = createDrawable(
backgroundColor, dpToPx(this@RegisterActivity, 8)
backgroundColor, dpToPx(this, 8)
)
}
private fun setupTip() {
val clickableSpan: ClickableSpan = object : ClickableSpan() {
override fun onClick(widget: View) {
Toast.makeText(widget.context, "暂未编写", Toast.LENGTH_SHORT).show()
toast("暂未编写")
}
override fun updateDrawState(ds: TextPaint) {

View File

@ -1,15 +1,36 @@
package com.kaixed.kchat.ui.activity
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import androidx.activity.enableEdgeToEdge
import androidx.activity.viewModels
import androidx.core.widget.addTextChangedListener
import com.kaixed.kchat.data.objectbox.ObjectBox.get
import com.kaixed.kchat.data.objectbox.entity.UserInfo
import com.kaixed.kchat.data.objectbox.entity.UserInfo_
import com.kaixed.kchat.databinding.ActivityRenameBinding
import com.kaixed.kchat.ui.base.BaseActivity
import com.kaixed.kchat.utils.Constants.MMKV_USER_SESSION
import com.kaixed.kchat.utils.Constants.NICKNAME_KEY
import com.kaixed.kchat.utils.ConstantsUtil.getNickName
import com.kaixed.kchat.utils.ConstantsUtil.getUsername
import com.kaixed.kchat.utils.WidgetUtil
import com.kaixed.kchat.viewmodel.UserViewModel
import com.tencent.mmkv.MMKV
import io.objectbox.Box
class RenameActivity : BaseActivity<ActivityRenameBinding>() {
private val userInfoBox: Box<UserInfo> by lazy { get().boxFor(UserInfo::class.java) }
private val userViewModel: UserViewModel by viewModels()
private var oldNickname: String = ""
private var updateSucceed = false
override fun inflateBinding(): ActivityRenameBinding {
return ActivityRenameBinding.inflate(layoutInflater)
}
@ -18,23 +39,62 @@ class RenameActivity : BaseActivity<ActivityRenameBinding>() {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
val nickname = getNickName()
oldNickname = getNickName()
val nickname = oldNickname
var enable = false
binding.etNickname.setText(nickname)
binding.etNickname.addTextChangedListener(
afterTextChanged = { et ->
enable = et.toString() != nickname
binding.ctb.setBtnEnable(et.toString() != nickname)
val nowNickname = getNickName()
enable = et.toString() != nowNickname
binding.ctb.setBtnEnable(et.toString() != nowNickname)
}
)
binding.ctb.setOnBtnClickListener {
if (enable) {
toast("保存成功")
WidgetUtil.showLoadingDialog(this, "正在保存")
updateNicknamesByCondition(binding.etNickname.text.toString(), getUsername())
}
}
}
private fun updateNicknamesByCondition(newNickname: String, username: String) {
binding.ctb.setBtnEnable(false)
val dialog = WidgetUtil.showLoadingDialog(this, "正在保存")
dialog.show()
userViewModel.changeNickname(username, newNickname).observe(this) { result ->
if (result) {
val user = userInfoBox
.query(UserInfo_.username.equal(username))
.build()
.findFirst()
MMKV.mmkvWithID(MMKV_USER_SESSION).putString(NICKNAME_KEY, newNickname)
user?.nickname = newNickname
if (user != null) {
userInfoBox.put(user)
}
binding.etNickname.setText(newNickname)
updateSucceed = true
} else {
updateSucceed = false
binding.etNickname.setText(oldNickname)
}
binding.etNickname.setSelection(newNickname.length)
}
Handler(Looper.getMainLooper()).postDelayed({
dialog.dismiss()
toast(if (updateSucceed) "更新成功" else "更新失败")
}, 800)
}
override fun onResume() {
super.onResume()
binding.etNickname.setText(getNickName())
}
}

View File

@ -4,8 +4,8 @@ import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.bumptech.glide.Glide
@ -32,20 +32,6 @@ class FriendListAdapter(private var items: MutableList<FriendItem>, private val
private const val TAG = "FriendListAdapter"
}
fun updateItems(newItems: List<FriendItem>) {
val diffResult = DiffUtil.calculateDiff(object : DiffUtil.Callback() {
override fun getOldListSize() = items.size
override fun getNewListSize() = newItems.size
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) =
items[oldItemPosition].username == newItems[newItemPosition].username
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) =
items[oldItemPosition] == newItems[newItemPosition]
})
items = newItems.toMutableList()
diffResult.dispatchUpdatesTo(this)
}
private val defaultItems = listOf(
com.kaixed.kchat.R.drawable.ic_contact_new_friend,
com.kaixed.kchat.R.drawable.ic_contact_group,
@ -85,8 +71,8 @@ class FriendListAdapter(private var items: MutableList<FriendItem>, private val
is ItemViewHolder -> {
val item = items[position]
// Log.d(TAG, "${item.nickname} ${item.firstInSection}")
holder.binding.tvNickname.text = item.nickname
holder.binding.tvNickname.text =
item.remark?.ifEmpty { item.nickname }
if (item.firstInSection) {
holder.binding.tvLetter.text =
@ -99,6 +85,10 @@ class FriendListAdapter(private var items: MutableList<FriendItem>, private val
if (position <= defaultItems.size - 1) {
holder.binding.ifvAvatar.setImageResource(defaultItems[position])
holder.binding.ifvAvatar.setBackgroundColor(context.getColor(com.kaixed.kchat.R.color.gray))
if (position == defaultItems.size - 1) {
holder.binding.viewBottom.visibility =
if (items.size == 5) View.VISIBLE else View.INVISIBLE
}
} else {
Glide.with(context).load(item.avatarUrl).into(holder.binding.ifvAvatar)
holder.binding.root.setOnClickListener {

View File

@ -47,17 +47,17 @@ class ContactFragment : BaseFragment<FragmentContactBinding>() {
friendListViewModel.friendListLiveData.observe(viewLifecycleOwner) { friends ->
friends?.let {
friendList.addAll(friends)
friendAdapter.updateItems(friendList)
friendAdapter.notifyDataSetChanged()
}
}
friendListViewModel.loadFriendList(getUsername())
}
private fun getDefaultItem() {
friendList.add(FriendItem("新的朋友", "", "新的朋友", ""))
friendList.add(FriendItem("群聊", "", "群聊", ""))
friendList.add(FriendItem("标签", "", "标签", ""))
friendList.add(FriendItem("公众号", "", "公众号", ""))
friendList.add(FriendItem("服务号", "", "服务号", ""))
friendList.add(FriendItem("新的朋友", "", remark = "hahah"))
friendList.add(FriendItem("群聊", ""))
friendList.add(FriendItem("标签", ""))
friendList.add(FriendItem("公众号", ""))
friendList.add(FriendItem("服务号", ""))
}
}

View File

@ -1,5 +1,6 @@
package com.kaixed.kchat.ui.fragment
import android.annotation.SuppressLint
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
@ -8,6 +9,7 @@ import android.view.ViewGroup
import com.kaixed.kchat.databinding.FragmentMineBinding
import com.kaixed.kchat.ui.activity.ProfileDetailActivity
import com.kaixed.kchat.ui.base.BaseFragment
import com.kaixed.kchat.utils.ConstantsUtil
class MineFragment : BaseFragment<FragmentMineBinding>() {
@ -35,5 +37,13 @@ class MineFragment : BaseFragment<FragmentMineBinding>() {
}
binding.ciSetting.setRedTipVisibility(true)
}
@SuppressLint("SetTextI18n")
override fun onResume() {
super.onResume()
val nickname = ConstantsUtil.getNickName()
binding.tvId.text = "kid: $nickname"
}
}

View File

@ -14,6 +14,7 @@ import com.kaixed.kchat.data.objectbox.entity.Messages
import com.kaixed.kchat.databinding.BottomSheetLayoutBinding
import com.kaixed.kchat.ui.activity.LoginActivity
import com.kaixed.kchat.utils.Constants.MMKV_USER_SESSION
import com.kaixed.kchat.utils.Constants.USER_LOGIN_STATUS
import com.tencent.mmkv.MMKV
import io.objectbox.BoxStore
@ -71,6 +72,9 @@ class MyBottomSheetFragment : BottomSheetDialogFragment() {
val mmkv = MMKV.mmkvWithID(MMKV_USER_SESSION)
mmkv.clearAll()
MMKV.defaultMMKV().putBoolean(USER_LOGIN_STATUS, false)
val boxStore: BoxStore = get()
boxStore.boxFor(ChatLists::class.java).removeAll()

View File

@ -1,6 +1,5 @@
package com.kaixed.kchat.viewmodel
import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
@ -31,16 +30,13 @@ class FriendListViewModel : ViewModel() {
Pinyin4jUtil.compare(f1.nickname, f2.nickname)
}?.toMutableList() // 确保是可变列表,以便后续更新 firstInSection
val startTime = System.currentTimeMillis()
uiFriendList?.forEachIndexed { index, item ->
item.firstInSection =
(index == 0 || item.pinyin?.get(index) != uiFriendList[index - 1].pinyin?.get(
index - 1
))
}
val duration = System.currentTimeMillis() - startTime
Log.d(TAG, "Execution time: ${duration}ms")
// Log.d(TAG, "loadFriendList: $uiFriendList")
_friendListLiveData.value = uiFriendList
}
}

View File

@ -23,6 +23,9 @@ class UserViewModel : ViewModel() {
signature: String,
telephone: String
): MutableLiveData<Register?> =
userRepo.register( password, nickname, avatarUrl, signature, telephone)
userRepo.register(password, nickname, avatarUrl, signature, telephone)
fun changeNickname(username: String, nickname: String): MutableLiveData<Boolean> =
userRepo.changeNickname(username, nickname)
}

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/green" android:state_checked="true" />
<item android:color="@color/black" />
</selector>

View File

@ -76,73 +76,29 @@
android:layout_marginStart="15dp"
android:background="#E5E5E5" />
<RelativeLayout
android:id="@+id/rl_setDetails"
<com.kaixed.kchat.ui.widget.CustomItem
android:layout_width="match_parent"
android:layout_height="50dp"
android:paddingStart="15dp">
android:layout_height="wrap_content"
app:isTopDividerVisible="true"
app:itemName="设置备注" />
<com.kaixed.kchat.ui.widget.CustomItem
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:isTopDividerVisible="true"
app:itemName="朋友权限" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="设置备注和标签"
android:textColor="@color/black"
android:textSize="16sp" />
<ImageView
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginEnd="15dp"
android:src="@drawable/icon_arrow_right" />
</RelativeLayout>
<View
android:layout_width="match_parent"
android:layout_height="0.7dp"
android:layout_marginStart="15dp"
android:background="#E5E5E5" />
<RelativeLayout
android:id="@+id/rl_contact_permission"
android:layout_width="match_parent"
android:layout_height="50dp"
android:paddingStart="15dp"
tools:ignore="RtlSymmetry">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="朋友权限"
android:textColor="@color/black"
android:textSize="16sp" />
<ImageView
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginEnd="15dp"
android:src="@drawable/icon_arrow_right" />
</RelativeLayout>
<View
android:layout_width="match_parent"
android:layout_height="8dp"
android:layout_height="@dimen/margin"
android:background="#E5E5E5" />
<RelativeLayout
android:id="@+id/rl_contact_circle"
android:layout_width="match_parent"
android:layout_height="80dp"
android:background="@color/white"
android:paddingStart="15dp"
tools:ignore="RtlSymmetry">
@ -166,35 +122,12 @@
</RelativeLayout>
<View
<com.kaixed.kchat.ui.widget.CustomItem
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginStart="15dp"
android:background="#E5E5E5" />
android:layout_height="wrap_content"
app:isTopDividerVisible="true"
app:itemName="更多信息" />
<RelativeLayout
android:id="@+id/rl_contact_more"
android:layout_width="match_parent"
android:layout_height="50dp"
android:paddingStart="15dp"
tools:ignore="RtlSymmetry">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="更多信息"
android:textColor="@color/black"
android:textSize="16sp" />
<ImageView
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginEnd="15dp"
android:src="@drawable/icon_arrow_right" />
</RelativeLayout>
<View
android:layout_width="match_parent"
@ -214,7 +147,7 @@
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="发消息"
android:textColor="@color/black"
android:textColor="@color/normal"
android:textSize="16sp" />
@ -222,8 +155,8 @@
<View
android:layout_width="match_parent"
android:layout_height="0.7dp"
android:background="#E5E5E5" />
android:layout_height="@dimen/divider_height"
android:background="@color/gray" />
<RelativeLayout
@ -239,10 +172,8 @@
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="音视频通话"
android:textColor="@color/black"
android:textColor="@color/normal"
android:textSize="16sp" />
</RelativeLayout>
<View

View File

@ -6,12 +6,18 @@
android:background="@color/white"
android:orientation="vertical">
<FrameLayout
android:id="@+id/fl_main"
<!-- <FrameLayout-->
<!-- android:id="@+id/fl_main"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="0dp"-->
<!-- android:layout_weight="1.0"-->
<!-- android:background="@color/white" />-->
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1.0"
android:background="@color/white" />
android:layout_weight="1.0" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/bottomNavContainer"
@ -23,7 +29,7 @@
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#E6F0F0"
android:background="@color/gray"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.ConstraintLayout

View File

@ -98,6 +98,7 @@
<com.kaixed.kchat.ui.widget.CustomItem
android:id="@+id/ci_service"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:itemLeftIcon="@drawable/ic_mine_pay"
@ -115,6 +116,7 @@
android:background="#D5D5D5" />
<com.kaixed.kchat.ui.widget.CustomItem
android:id="@+id/ci_favorites"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:itemLeftIcon="@drawable/ic_mine_favorites"
@ -122,6 +124,7 @@
<com.kaixed.kchat.ui.widget.CustomItem
android:id="@+id/ci_friend_circle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:isTopDividerVisible="true"
@ -130,6 +133,7 @@
<com.kaixed.kchat.ui.widget.CustomItem
android:id="@+id/ci_card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:isTopDividerVisible="true"
@ -138,6 +142,7 @@
<com.kaixed.kchat.ui.widget.CustomItem
android:id="@+id/ci_emoji"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:isTopDividerVisible="true"

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/navigation_home"
android:icon="@drawable/ic_main"
android:title="首页"
app:titleTextAppearance="@style/BottomNavTextAppearance"
/>
<item
android:id="@+id/navigation_contact"
android:icon="@drawable/ic_message"
app:titleTextAppearance="@style/BottomNavTextAppearance"
android:title="联系人" />
<item
android:id="@+id/navigation_discovery"
app:titleTextAppearance="@style/BottomNavTextAppearance"
android:icon="@drawable/ic_discovery"
android:title="发现" />
<item
android:id="@+id/navigation_profile"
app:titleTextAppearance="@style/BottomNavTextAppearance"
android:icon="@drawable/ic_mine"
android:title="我的" />
</menu>

View File

@ -16,4 +16,9 @@
<item name="android:background">@android:color/transparent</item>
<item name="android:backgroundDimEnabled">true</item>
</style>
<style name="BottomNavTextAppearance" parent="TextAppearance.AppCompat.Caption">
<item name="android:textSize">11sp</item>
</style>
</resources>

View File

@ -35,6 +35,7 @@ shapedrawable = { module = "com.github.getActivity:ShapeDrawable", version.ref =
shapeview = { module = "com.github.getActivity:ShapeView", version.ref = "shapeview" }
therouter-ksp = { module = "cn.therouter:apt", version.ref = "therouter" }
objectbox-android-objectbrowser = { group = "io.objectbox", name = "objectbox-android-objectbrowser", version.ref = "objectbox" }
objectbox-kotlin = { group = "io.objectbox", name = "objectbox-kotlin", version.ref = "objectbox" }
objectbox-android = { group = "io.objectbox", name = "objectbox-android", version.ref = "objectbox" }
emoji2 = { module = "androidx.emoji2:emoji2", version.ref = "emoji2" }
glide = { module = "com.github.bumptech.glide:glide", version.ref = "glide" }