From d4dc9a3b24093897b66704ea195912b74a9ab598 Mon Sep 17 00:00:00 2001 From: kaixed Date: Fri, 1 Nov 2024 19:14:16 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84=E4=B8=BB?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1.将主页面更改为4个页面 2.完善自定义CustomItem --- app/src/main/AndroidManifest.xml | 3 - .../kaixed/kchat/ui/activity/ChatActivity.kt | 12 +- .../kchat/ui/activity/FriendListActivity.kt | 2 +- .../kchat/ui/activity/LaunchActivity.kt | 26 +- .../kaixed/kchat/ui/activity/MainActivity.kt | 397 ++++-------------- .../kaixed/kchat/ui/activity/TestActivity.kt | 141 ------- .../kchat/ui/adapter/FriendListAdapter.kt | 92 +++- .../com/kaixed/kchat/ui/base/BaseFragment.kt | 47 +++ .../kchat/ui/fragment/ContactFragment.kt | 102 +++++ .../kchat/ui/fragment/DiscoveryFragment.kt | 23 + .../kaixed/kchat/ui/fragment/HomeFragment.kt | 371 ++++++++++++++++ .../kaixed/kchat/ui/fragment/MineFragment.kt | 39 ++ .../com/kaixed/kchat/ui/widget/CustomItem.kt | 31 +- .../java/com/kaixed/kchat/utils/Constants.kt | 4 + .../com/kaixed/kchat/utils/ConstantsUtil.kt | 8 +- .../main/res/drawable-hdpi/ic_discovery.xml | 5 + .../res/drawable-hdpi/ic_discovery_normal.xml | 12 + .../drawable-hdpi/ic_discovery_selected.xml | 12 + .../main/res/drawable-hdpi/ic_location.xml | 9 + app/src/main/res/drawable-hdpi/ic_main.xml | 5 + .../main/res/drawable-hdpi/ic_main_normal.xml | 12 + .../res/drawable-hdpi/ic_main_selected.xml | 12 + app/src/main/res/drawable-hdpi/ic_message.xml | 5 + .../res/drawable-hdpi/ic_message_normal.xml | 12 + .../res/drawable-hdpi/ic_message_selected.xml | 9 + app/src/main/res/drawable-hdpi/ic_mine.xml | 5 + .../main/res/drawable-hdpi/ic_mine_normal.xml | 15 + .../res/drawable-hdpi/ic_mine_selected.xml | 12 + .../main/res/drawable/ic_contact_group.xml | 9 + .../main/res/drawable/ic_contact_label.xml | 7 + .../res/drawable/ic_contact_new_friend.xml | 11 + .../drawable/ic_contact_public_account.xml | 7 + .../drawable/ic_contact_service_account.xml | 7 + .../main/res/drawable/ic_default_avatar.xml | 18 + .../main/res/drawable/ic_discovery_music.xml | 9 + .../main/res/drawable/ic_discovery_scan.xml | 12 + .../main/res/drawable/ic_friend_circle.xml | 54 +++ app/src/main/res/drawable/ic_mine_album.xml | 12 + app/src/main/res/drawable/ic_mine_card.xml | 15 + .../main/res/drawable/ic_mine_collection.xml | 24 ++ app/src/main/res/drawable/ic_mine_emoji.xml | 24 ++ .../main/res/drawable/ic_mine_favorites.xml | 12 + app/src/main/res/drawable/ic_mine_pay.xml | 9 + app/src/main/res/drawable/ic_mine_setting.xml | 12 + .../main/res/drawable/mine_icon_wallet.xml | 6 +- app/src/main/res/layout/activity_main.xml | 232 ++++++---- app/src/main/res/layout/activity_test.xml | 154 +------ .../layout/contact_recycle_footer_item.xml | 19 + app/src/main/res/layout/fragment_contact.xml | 27 ++ .../main/res/layout/fragment_discovery.xml | 41 ++ app/src/main/res/layout/fragment_home.xml | 117 ++++++ app/src/main/res/layout/fragment_mine.xml | 134 ++++++ .../main/res/layout/friend_recycle_item.xml | 54 +-- app/src/main/res/layout/item_custom.xml | 29 +- app/src/main/res/layout/view_test.xml | 20 + app/src/main/res/values/attr.xml | 2 + app/src/main/res/values/strings.xml | 2 + 57 files changed, 1730 insertions(+), 772 deletions(-) delete mode 100644 app/src/main/java/com/kaixed/kchat/ui/activity/TestActivity.kt create mode 100644 app/src/main/java/com/kaixed/kchat/ui/base/BaseFragment.kt create mode 100644 app/src/main/java/com/kaixed/kchat/ui/fragment/ContactFragment.kt create mode 100644 app/src/main/java/com/kaixed/kchat/ui/fragment/DiscoveryFragment.kt create mode 100644 app/src/main/java/com/kaixed/kchat/ui/fragment/HomeFragment.kt create mode 100644 app/src/main/java/com/kaixed/kchat/ui/fragment/MineFragment.kt create mode 100644 app/src/main/res/drawable-hdpi/ic_discovery.xml create mode 100644 app/src/main/res/drawable-hdpi/ic_discovery_normal.xml create mode 100644 app/src/main/res/drawable-hdpi/ic_discovery_selected.xml create mode 100644 app/src/main/res/drawable-hdpi/ic_location.xml create mode 100644 app/src/main/res/drawable-hdpi/ic_main.xml create mode 100644 app/src/main/res/drawable-hdpi/ic_main_normal.xml create mode 100644 app/src/main/res/drawable-hdpi/ic_main_selected.xml create mode 100644 app/src/main/res/drawable-hdpi/ic_message.xml create mode 100644 app/src/main/res/drawable-hdpi/ic_message_normal.xml create mode 100644 app/src/main/res/drawable-hdpi/ic_message_selected.xml create mode 100644 app/src/main/res/drawable-hdpi/ic_mine.xml create mode 100644 app/src/main/res/drawable-hdpi/ic_mine_normal.xml create mode 100644 app/src/main/res/drawable-hdpi/ic_mine_selected.xml create mode 100644 app/src/main/res/drawable/ic_contact_group.xml create mode 100644 app/src/main/res/drawable/ic_contact_label.xml create mode 100644 app/src/main/res/drawable/ic_contact_new_friend.xml create mode 100644 app/src/main/res/drawable/ic_contact_public_account.xml create mode 100644 app/src/main/res/drawable/ic_contact_service_account.xml create mode 100644 app/src/main/res/drawable/ic_default_avatar.xml create mode 100644 app/src/main/res/drawable/ic_discovery_music.xml create mode 100644 app/src/main/res/drawable/ic_discovery_scan.xml create mode 100644 app/src/main/res/drawable/ic_friend_circle.xml create mode 100644 app/src/main/res/drawable/ic_mine_album.xml create mode 100644 app/src/main/res/drawable/ic_mine_card.xml create mode 100644 app/src/main/res/drawable/ic_mine_collection.xml create mode 100644 app/src/main/res/drawable/ic_mine_emoji.xml create mode 100644 app/src/main/res/drawable/ic_mine_favorites.xml create mode 100644 app/src/main/res/drawable/ic_mine_pay.xml create mode 100644 app/src/main/res/drawable/ic_mine_setting.xml create mode 100644 app/src/main/res/layout/contact_recycle_footer_item.xml create mode 100644 app/src/main/res/layout/fragment_contact.xml create mode 100644 app/src/main/res/layout/fragment_discovery.xml create mode 100644 app/src/main/res/layout/fragment_home.xml create mode 100644 app/src/main/res/layout/fragment_mine.xml create mode 100644 app/src/main/res/layout/view_test.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 809e6d4..a0e9d1e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -94,9 +94,6 @@ - diff --git a/app/src/main/java/com/kaixed/kchat/ui/activity/ChatActivity.kt b/app/src/main/java/com/kaixed/kchat/ui/activity/ChatActivity.kt index a3eddfc..0d852a5 100644 --- a/app/src/main/java/com/kaixed/kchat/ui/activity/ChatActivity.kt +++ b/app/src/main/java/com/kaixed/kchat/ui/activity/ChatActivity.kt @@ -257,9 +257,6 @@ class ChatActivity : BaseActivity(), OnItemClickListener { private fun setListener() { binding.tvContactName.setOnLongClickListener { - val intent = - Intent(context, TestActivity::class.java) - startActivity(intent) false } @@ -273,11 +270,14 @@ class ChatActivity : BaseActivity(), OnItemClickListener { binding.recycleChatList.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { super.onScrolled(recyclerView, dx, dy) - val layoutManager = checkNotNull(recyclerView.layoutManager as LinearLayoutManager?) - val firstVisiblePosition = layoutManager.findLastVisibleItemPosition() - if (tempIndex + 1 == messagesList[firstVisiblePosition].msgLocalId && hasHistory && !loading) { + if (recyclerView.canScrollVertically(-1) && hasHistory && !loading) { loadMoreMessages() } +// val layoutManager = checkNotNull(recyclerView.layoutManager as LinearLayoutManager?) +// val firstVisiblePosition = layoutManager.findLastVisibleItemPosition() +// if (tempIndex + 1 == messagesList[firstVisiblePosition].msgLocalId && hasHistory && !loading) { +// loadMoreMessages() +// } } }) binding.ivBack.setOnClickListener { finish() } diff --git a/app/src/main/java/com/kaixed/kchat/ui/activity/FriendListActivity.kt b/app/src/main/java/com/kaixed/kchat/ui/activity/FriendListActivity.kt index 549ccbf..74b7981 100644 --- a/app/src/main/java/com/kaixed/kchat/ui/activity/FriendListActivity.kt +++ b/app/src/main/java/com/kaixed/kchat/ui/activity/FriendListActivity.kt @@ -12,7 +12,7 @@ import com.kaixed.kchat.ui.adapter.FriendListAdapter import com.kaixed.kchat.utils.ConstantsUtil.getUsername import com.kaixed.kchat.viewmodel.FriendListViewModel -class FriendListActivity : AppCompatActivity() { +class FriendListActivity : BaseActivity() { private lateinit var binding: ActivityFriendListBinding private val friendListViewModel: FriendListViewModel by viewModels() private var friendList: MutableList = mutableListOf() diff --git a/app/src/main/java/com/kaixed/kchat/ui/activity/LaunchActivity.kt b/app/src/main/java/com/kaixed/kchat/ui/activity/LaunchActivity.kt index 2f820fe..107d599 100644 --- a/app/src/main/java/com/kaixed/kchat/ui/activity/LaunchActivity.kt +++ b/app/src/main/java/com/kaixed/kchat/ui/activity/LaunchActivity.kt @@ -1,15 +1,18 @@ package com.kaixed.kchat.ui.activity +import android.annotation.SuppressLint import android.content.Intent import android.os.Bundle import androidx.activity.enableEdgeToEdge -import androidx.appcompat.app.AppCompatActivity import com.kaixed.kchat.databinding.ActivityLaunchBinding +import com.kaixed.kchat.utils.Constants.MMKV_COMMON_DATA import com.kaixed.kchat.utils.Constants.MMKV_USER_SESSION +import com.kaixed.kchat.utils.Constants.STATUS_BAR_HEIGHT import com.kaixed.kchat.utils.Constants.USER_LOGIN_STATUS import com.tencent.mmkv.MMKV -class LaunchActivity : AppCompatActivity() { + +class LaunchActivity : BaseActivity() { private lateinit var binding: ActivityLaunchBinding @@ -18,7 +21,12 @@ class LaunchActivity : AppCompatActivity() { enableEdgeToEdge() binding = ActivityLaunchBinding.inflate(layoutInflater) setContentView(binding.root) + + val mmkvData: MMKV = MMKV.mmkvWithID(MMKV_COMMON_DATA) + mmkvData.putInt(STATUS_BAR_HEIGHT, getStatusBarHeight()) + val mmkv = MMKV.mmkvWithID(MMKV_USER_SESSION) + if (mmkv.decodeBool(USER_LOGIN_STATUS)) { navigateToMain() } @@ -26,10 +34,24 @@ class LaunchActivity : AppCompatActivity() { setOnClickListener() } + + @SuppressLint("DiscouragedApi", "InternalInsetResource") + private fun getStatusBarHeight(): Int { + var result = 0 + val resourceId: Int = + resources.getIdentifier("status_bar_height", "dimen", "android") + if (resourceId > 0) { + result = resources.getDimensionPixelSize(resourceId) + } + return result + } + + private fun navigateToMain() { val intent = Intent(this, MainActivity::class.java) startActivity(intent) finish() + return } private fun setOnClickListener() { diff --git a/app/src/main/java/com/kaixed/kchat/ui/activity/MainActivity.kt b/app/src/main/java/com/kaixed/kchat/ui/activity/MainActivity.kt index 2e3dad0..d681efe 100644 --- a/app/src/main/java/com/kaixed/kchat/ui/activity/MainActivity.kt +++ b/app/src/main/java/com/kaixed/kchat/ui/activity/MainActivity.kt @@ -1,355 +1,134 @@ package com.kaixed.kchat.ui.activity -import android.animation.Animator -import android.animation.AnimatorListenerAdapter -import android.animation.ObjectAnimator -import android.annotation.SuppressLint -import android.content.ComponentName -import android.content.Context -import android.content.Intent -import android.content.ServiceConnection -import android.graphics.Color -import android.graphics.drawable.ColorDrawable import android.os.Bundle -import android.os.IBinder import android.view.View import androidx.activity.enableEdgeToEdge -import androidx.appcompat.app.AppCompatActivity -import androidx.recyclerview.widget.LinearLayoutManager +import androidx.core.content.ContextCompat +import androidx.core.view.WindowCompat +import androidx.fragment.app.Fragment import com.kaixed.kchat.R -import com.kaixed.kchat.data.objectbox.ObjectBox.get -import com.kaixed.kchat.data.objectbox.entity.ChatLists -import com.kaixed.kchat.data.objectbox.entity.ChatLists_ -import com.kaixed.kchat.data.objectbox.entity.Messages import com.kaixed.kchat.databinding.ActivityMainBinding -import com.kaixed.kchat.model.HomeItem -import com.kaixed.kchat.service.WebSocketService -import com.kaixed.kchat.service.WebSocketService.LocalBinder -import com.kaixed.kchat.ui.adapter.ChatListAdapter -import com.kaixed.kchat.ui.adapter.MyGridAdapter -import com.kaixed.kchat.ui.i.OnChatListItemClickListener -import com.kaixed.kchat.utils.Constants.MMKV_COMMON_DATA -import com.tencent.mmkv.MMKV -import io.objectbox.Box +import com.kaixed.kchat.ui.fragment.ContactFragment +import com.kaixed.kchat.ui.fragment.DiscoveryFragment +import com.kaixed.kchat.ui.fragment.HomeFragment +import com.kaixed.kchat.ui.fragment.MineFragment -/** - * @Author: kaixed - * @Date: 2024/5/26 10:02 - */ -class MainActivity : AppCompatActivity(), OnChatListItemClickListener { - private var binding: ActivityMainBinding? = null - private var chatLists: MutableList = ArrayList() - private var chatListAdapter: ChatListAdapter? = null - private var chatListsBox: Box? = null - private var messagesBox: Box? = null - private var updateUsername: String? = null - private val items: MutableList = ArrayList() - private var webSocketService: WebSocketService? = null - private var isFlipped = false - private var isBound = false - private val mContext: Context = this +class MainActivity : BaseActivity(), View.OnClickListener { + private lateinit var binding: ActivityMainBinding + 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 val fragments = mutableMapOf() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - this.enableEdgeToEdge() + enableEdgeToEdge() + // 设置透明状态栏 + WindowCompat.setDecorFitsSystemWindows(window, false) binding = ActivityMainBinding.inflate(layoutInflater) - setContentView(binding!!.root) + setContentView(binding.root) - chatListsBox = get().boxFor(ChatLists::class.java) - messagesBox = get().boxFor(Messages::class.java) + colorMain = ContextCompat.getColor(this, R.color.green) + colorBlack = ContextCompat.getColor(this, R.color.black) initView() + initFragment() - startService() - - setOnClick() + updateSelection(KEY_HOME_FRAGMENT) } - private fun setOnClick() { - binding!!.ivMessage.setOnClickListener { - val intent = Intent( - this, - MessageActivity::class.java - ) - startActivity(intent) - } - binding!!.ivSearch.setOnClickListener { - val intent = - Intent(mContext, SearchActivity::class.java) - startActivity(intent) - } + private fun initFragment() { + fragments[KEY_HOME_FRAGMENT] = HomeFragment() + fragments[KEY_CONTACT_FRAGMENT] = ContactFragment() + fragments[KEY_DISCOVERY_FRAGMENT] = DiscoveryFragment() + fragments[KEY_MINE_FRAGMENT] = MineFragment() - binding!!.ifvAvatar.setOnClickListener { - val intent = - Intent(mContext, ProfileActivity::class.java) - startActivity(intent) - } - } - - private fun flipImage(v: View) { - val flipAnimation = if (isFlipped) { - ObjectAnimator.ofFloat(v, "rotationX", 180f, 0f) - } else { - ObjectAnimator.ofFloat(v, "rotationX", 0f, 180f) - } - flipAnimation.setDuration(0) - flipAnimation.start() - isFlipped = !isFlipped - } - - @SuppressLint("NotifyDataSetChanged") - fun notifyData() { - chatListAdapter!!.notifyDataSetChanged() - } - - private fun startService() { - val intent = Intent( - this@MainActivity, - WebSocketService::class.java - ) - startService(intent) - } - - private fun observeLiveData() { - if (webSocketService == null) { - return - } - webSocketService!!.liveData.observe( - this - ) { messages: Messages -> - if ("0" == messages.type) { - processMessage(messages) + supportFragmentManager.beginTransaction().apply { + fragments.values.forEach { fragment -> + add(R.id.fl_main, fragment, fragment.javaClass.simpleName) + hide(fragment) } - } - } - - private fun processMessage(messages: Messages) { - val talkerId = messages.takerId - val content = messages.content - val timestamp = messages.timestamp - - val chatList = createChatList(talkerId, content, timestamp) - - val index = findChatListIndex(talkerId) - if (index == -1) { - chatLists.add(chatList) - } else { - updateChatList(chatLists[index], content, timestamp) + commit() } - runOnUiThread { this.notifyData() } - isInDb(chatList) + showFragment(KEY_HOME_FRAGMENT) } - private fun createChatList(talkerId: String, content: String, timestamp: Long): ChatLists { - return ChatLists( - 0L, - talkerId, - talkerId, - "1", - content, - timestamp, - 1 - ) - } - - private fun findChatListIndex(talkerId: String): Int { - for (i in chatLists.indices) { - if (chatLists[i].talkerId == talkerId) { - return i + private fun showFragment(key: Int) { + supportFragmentManager.beginTransaction().apply { + fragments.values.forEach { fragment -> + hide(fragment) + if (fragments[key] == fragment) { + show(fragment) + } } - } - return -1 - } - - private fun updateChatList(chatList: ChatLists, content: String, timestamp: Long) { - chatList.lastContent = content - chatList.timestamp = timestamp - } - - private fun isInDb(chatList: ChatLists) { - // 查询数据库中是否已经存在该talkerId的记录 - val existingChatList = chatListsBox - ?.query(ChatLists_.talkerId.equal(chatList.talkerId)) - ?.build() - ?.findFirst() - - if (existingChatList != null) { - // 如果存在,更新内容和时间戳 - existingChatList.lastContent = chatList.lastContent - existingChatList.timestamp = chatList.timestamp - chatListsBox!!.put(existingChatList) - } else { - // 如果不存在,添加新的记录 - chatListsBox!!.put(chatList) + commit() } } - private fun updateChatLists() { - val kv = MMKV.mmkvWithID(MMKV_COMMON_DATA) - val msgLocalId = kv.getLong("msgLocalId", -1L) - if (msgLocalId != -1L) { - val messages = messagesBox!![msgLocalId] - processMessage(messages) - kv.remove("msgLocalId") - } - } - - private var connection: ServiceConnection = object : ServiceConnection { - override fun onServiceConnected(className: ComponentName, service: IBinder) { - val binder = service as LocalBinder - webSocketService = binder.getService() - isBound = true - observeLiveData() - } - - override fun onServiceDisconnected(arg0: ComponentName) { - isBound = false - } - } private fun initView() { - setupGridView() - setupRecycleView() - setupMoreOptionsToggle() + binding.clHome.setOnClickListener(this) + binding.clHome.tag = KEY_HOME_FRAGMENT + + binding.clContact.setOnClickListener(this) + binding.clContact.tag = KEY_CONTACT_FRAGMENT + + binding.clDiscovery.setOnClickListener(this) + binding.clDiscovery.tag = KEY_DISCOVERY_FRAGMENT + + binding.clMine.setOnClickListener(this) + binding.clMine.tag = KEY_MINE_FRAGMENT } - private fun setupMoreOptionsToggle() { - binding!!.ivMore.setOnClickListener { v -> - flipImage(v) - val gridViewHeight = binding!!.gridView.height - val startAt: Int - val endAt: Int - if (isFlipped) { - binding!!.gridView.visibility = View.VISIBLE - startAt = 0 - endAt = gridViewHeight - } else { - startAt = gridViewHeight - endAt = 0 - } - createAnimator(startAt, endAt).start() - } + + override fun onClick(view: View) { + val key = view.tag as Int + showFragment(key) + updateSelection(key) } - private fun createAnimator(start: Int, end: Int): ObjectAnimator { - val animator = ObjectAnimator.ofInt(binding!!.linearlayout, "top", start, end) - val duration: Long = 500 - animator.setDuration(duration) - - animator.addListener(object : AnimatorListenerAdapter() { - override fun onAnimationStart(animation: Animator) { - super.onAnimationStart(animation) - binding!!.ivMore.isEnabled = false + private fun updateSelection(selectedKey: Int) { + clearSelection() + when (selectedKey) { + KEY_HOME_FRAGMENT -> { + binding.ivHome.isSelected = true + binding.tvHome.setTextColor(colorMain) } - override fun onAnimationEnd(animation: Animator) { - super.onAnimationEnd(animation) - if (!isFlipped) { - binding!!.gridView.visibility = View.INVISIBLE - } - binding!!.ivMore.isEnabled = true + KEY_CONTACT_FRAGMENT -> { + binding.ivContact.isSelected = true + binding.tvContact.setTextColor(colorMain) } - }) - return animator - } - private fun setupRecycleView() { - if (chatListsBox!!.all != null) { - chatLists = chatListsBox!!.all - } - chatListAdapter = ChatListAdapter(chatLists, this) - binding!!.recycleChatList.layoutManager = - LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false) - binding!!.recycleChatList.adapter = chatListAdapter - chatListAdapter!!.setItemListener(this) - } + KEY_DISCOVERY_FRAGMENT -> { + binding.ivDiscovery.isSelected = true + binding.tvDiscovery.setTextColor(colorMain) + } - private fun setupGridView() { - initMenuData() - val myGridAdapter = MyGridAdapter(this, items) - binding!!.gridView.adapter = myGridAdapter - - binding!!.gridView.selector = ColorDrawable(Color.TRANSPARENT) - - binding!!.gridView.setOnItemClickListener { _, _, position, _ -> - when (position) { - 0 -> {} - 2 -> { - val intent = - Intent( - this@MainActivity, - AddFriendsActivity::class.java - ) - startActivity(intent) - } - - 4 -> { - val intent2 = - Intent( - this@MainActivity, - ContactUpdatesActivity::class.java - ) - startActivity(intent2) - } - - 6 -> { - val intent1 = - Intent( - this@MainActivity, - FriendListActivity::class.java - ) - startActivity(intent1) - } - - else -> finish() + KEY_MINE_FRAGMENT -> { + binding.ivMine.isSelected = true + binding.tvMine.setTextColor(colorMain) } } } - private fun initMenuData() { - items.add(HomeItem("更换背景", true, R.drawable.ic_switch)) - items.add(HomeItem("创建群聊", false, R.drawable.ic_troop)) - items.add(HomeItem("新朋友", true, R.drawable.ic_friend)) - items.add(HomeItem("夜间模式", true, R.drawable.ic_night)) - items.add(HomeItem("好友动态", false, R.drawable.ic_qzone)) - items.add(HomeItem("扫一扫", false, R.drawable.ic_scan)) - items.add(HomeItem("通讯录", true, R.drawable.ic_clock_in)) - items.add(HomeItem("关闭应用", false, R.drawable.ic_exit)) - } + private fun clearSelection() { + binding.ivHome.isSelected = false + binding.ivContact.isSelected = false + binding.ivDiscovery.isSelected = false + binding.ivMine.isSelected = false - override fun onRestart() { - super.onRestart() - updateChatLists() - } - - override fun onStart() { - super.onStart() - val intent = Intent( - this, - WebSocketService::class.java - ) - bindService(intent, connection, BIND_AUTO_CREATE) - } - - override fun onStop() { - super.onStop() - if (isBound) { - unbindService(connection) - isBound = false - } - } - - override fun onDestroy() { - super.onDestroy() - val intent = Intent( - this@MainActivity, - WebSocketService::class.java - ) - stopService(intent) - } - - override fun onItemClick(username: String?) { - this.updateUsername = username + binding.tvHome.setTextColor(colorBlack) + binding.tvContact.setTextColor(colorBlack) + binding.tvDiscovery.setTextColor(colorBlack) + binding.tvMine.setTextColor(colorBlack) } } diff --git a/app/src/main/java/com/kaixed/kchat/ui/activity/TestActivity.kt b/app/src/main/java/com/kaixed/kchat/ui/activity/TestActivity.kt deleted file mode 100644 index 0989d99..0000000 --- a/app/src/main/java/com/kaixed/kchat/ui/activity/TestActivity.kt +++ /dev/null @@ -1,141 +0,0 @@ -package com.kaixed.kchat.ui.activity - -import android.annotation.SuppressLint -import android.os.Bundle -import android.view.MotionEvent -import android.view.View -import android.view.inputmethod.InputMethodManager -import android.widget.LinearLayout -import androidx.activity.enableEdgeToEdge -import androidx.appcompat.app.AppCompatActivity -import com.kaixed.kchat.databinding.ActivityTestBinding -import com.kaixed.kchat.utils.Constants -import com.tencent.mmkv.MMKV - -class TestActivity : AppCompatActivity() { - - private lateinit var binding: ActivityTestBinding - private lateinit var inputMethodManager: InputMethodManager - - var keyboard: Int = MMKV.mmkvWithID(Constants.MMKV_COMMON_DATA) - .getInt("keyboardHeight", 200) - - - @SuppressLint("ClickableViewAccessibility") - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - binding = ActivityTestBinding.inflate(layoutInflater) - enableEdgeToEdge() - setContentView(binding.root) - - inputMethodManager = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager - - // 处理 EditText 的触摸事件 - binding.etInput.setOnTouchListener { _, event -> - if (event.action == MotionEvent.ACTION_UP && binding.clFunctionPanel.isShown) { - lockContentViewHeight() - hideEmojiPanel(true) - unlockContentViewHeight() - } - false - } - - // 处理 RecyclerView 的触摸事件 -// binding.recycleChatList.setOnTouchListener { _, motionEvent -> -// if (motionEvent.action == MotionEvent.ACTION_UP) { -// if (binding.clFunctionPanel.isShown) { -// hideEmojiPanel(false) -// } else if (isSoftKeyboardShown()) { -// hideSoftKeyboard() -// } -// } -// false -// } - - // 表情面板切换按钮点击事件 - binding.ivEmoji.setOnClickListener { - if (binding.clFunctionPanel.isShown) { - lockContentViewHeight() - hideEmojiPanel(true) - unlockContentViewHeight() - } else { - if (isSoftKeyboardShown()) { - lockContentViewHeight() - showEmojiPanel() - unlockContentViewHeight() - } else { - showEmojiPanel() - } - } - } - } - - private fun lockContentViewHeight() { - val layoutParams = binding.recycleChatList.layoutParams as LinearLayout.LayoutParams - layoutParams.height = binding.recycleChatList.height - layoutParams.weight = 0f - binding.recycleChatList.layoutParams = layoutParams - } - - private fun unlockContentViewHeight() { - binding.recycleChatList.postDelayed({ - val layoutParams = binding.recycleChatList.layoutParams as LinearLayout.LayoutParams - layoutParams.weight = 1f - binding.recycleChatList.layoutParams = layoutParams - }, 200) - } - - private fun getSoftKeyboardHeight(): Int { - return keyboard - } - - private fun getSoftKeyboardHeightLocalValue(): Int { - return keyboard - } - - private fun isSoftKeyboardShown(): Boolean { - return getSoftKeyboardHeight() != 0 - } - - private fun showSoftKeyboard(saveSoftKeyboardHeight: Boolean) { - binding.etInput.requestFocus() - inputMethodManager.showSoftInput(binding.etInput, 0) - if (saveSoftKeyboardHeight) { - binding.etInput.postDelayed({ - getSoftKeyboardHeight() - }, 200) - } - } - - private fun hideSoftKeyboard() { - inputMethodManager.hideSoftInputFromWindow(binding.etInput.windowToken, 0) - } - - private fun showEmojiPanel() { - var softKeyboardHeight = getSoftKeyboardHeight() - if (softKeyboardHeight == 0) { - softKeyboardHeight = getSoftKeyboardHeightLocalValue() - } else { - hideSoftKeyboard() - } - binding.clFunctionPanel.layoutParams.height = softKeyboardHeight - binding.clFunctionPanel.visibility = View.VISIBLE - } - - private fun hideEmojiPanel(showSoftKeyboard: Boolean) { - if (binding.clFunctionPanel.isShown) { - binding.clFunctionPanel.visibility = View.GONE - if (showSoftKeyboard) { - showSoftKeyboard(false) - } - } - } - - override fun onBackPressed() { - if (binding.clFunctionPanel.isShown) { - hideEmojiPanel(false) - } else { - super.onBackPressed() - } - } -} diff --git a/app/src/main/java/com/kaixed/kchat/ui/adapter/FriendListAdapter.kt b/app/src/main/java/com/kaixed/kchat/ui/adapter/FriendListAdapter.kt index ae6aba0..6242366 100644 --- a/app/src/main/java/com/kaixed/kchat/ui/adapter/FriendListAdapter.kt +++ b/app/src/main/java/com/kaixed/kchat/ui/adapter/FriendListAdapter.kt @@ -1,5 +1,6 @@ package com.kaixed.kchat.ui.adapter +import android.annotation.SuppressLint import android.content.Context import android.content.Intent import android.view.LayoutInflater @@ -7,6 +8,7 @@ import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView.ViewHolder import com.bumptech.glide.Glide +import com.kaixed.kchat.databinding.ContactRecycleFooterItemBinding import com.kaixed.kchat.databinding.FriendRecycleItemBinding import com.kaixed.kchat.model.friend.FriendItem import com.kaixed.kchat.ui.activity.ChatActivity @@ -16,30 +18,80 @@ import com.kaixed.kchat.ui.activity.ChatActivity * @Date: 2024/10/14 14:47 */ class FriendListAdapter(private val items: MutableList, private val context: Context) : - RecyclerView.Adapter() { - class MyViewHolder(val binding: FriendRecycleItemBinding) : ViewHolder(binding.root) + RecyclerView.Adapter() { - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { - val binding = FriendRecycleItemBinding.inflate( - LayoutInflater.from(parent.context), - parent, - false - ) - return MyViewHolder(binding) + class ItemViewHolder(val binding: FriendRecycleItemBinding) : ViewHolder(binding.root) + + class FooterViewHolder(val binding: ContactRecycleFooterItemBinding) : ViewHolder(binding.root) + + companion object { + const val TYPE_ITEM: Int = 0 + const val TYPE_FOOTER: Int = 1 } - override fun onBindViewHolder(holder: MyViewHolder, position: Int) { - val item = items[position] - holder.binding.tvSendMessage.setOnClickListener { - val intent = Intent(context, ChatActivity::class.java) - intent.putExtra("friendId", item.username) - context.startActivity(intent) + private val defaultItems = listOf( + com.kaixed.kchat.R.drawable.ic_contact_new_friend, + com.kaixed.kchat.R.drawable.ic_contact_group, + com.kaixed.kchat.R.drawable.ic_contact_label, + com.kaixed.kchat.R.drawable.ic_contact_public_account, + com.kaixed.kchat.R.drawable.ic_contact_service_account + ) + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + return when (viewType) { + TYPE_FOOTER -> { + val binding = ContactRecycleFooterItemBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + FooterViewHolder(binding) + } + + else -> { + val binding = FriendRecycleItemBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ItemViewHolder(binding) + } } - - holder.binding.tvNickname.text = item.nickname - holder.binding.tvSignature.text = item.signature - Glide.with(context).load(item.avatarUrl).into(holder.binding.ifvAvatar) } - override fun getItemCount(): Int = items.size + @SuppressLint("SetTextI18n") + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + when (holder) { + is FooterViewHolder -> { + holder.binding.tvTip.text = "${itemCount - defaultItems.size - 1} 个朋友" + } + + is ItemViewHolder -> { + val item = items[position] + holder.binding.tvNickname.text = item.nickname + + if (position <= defaultItems.size - 1) { + holder.binding.ifvAvatar.setImageResource(defaultItems[position]) + holder.binding.ifvAvatar.setBackgroundColor(context.getColor(com.kaixed.kchat.R.color.gray)) + } else { + Glide.with(context).load(item.avatarUrl).into(holder.binding.ifvAvatar) + holder.binding.root.setOnClickListener { + val intent = Intent(context, ChatActivity::class.java) + intent.putExtra("friendId", item.username) + context.startActivity(intent) + } + } + } + } + } + + override fun getItemCount(): Int = items.size + 1 + + override fun getItemViewType(position: Int): Int { + return if (position + 1 == getItemCount()) { + TYPE_FOOTER + } else { + TYPE_ITEM + } + } } diff --git a/app/src/main/java/com/kaixed/kchat/ui/base/BaseFragment.kt b/app/src/main/java/com/kaixed/kchat/ui/base/BaseFragment.kt new file mode 100644 index 0000000..cea78fc --- /dev/null +++ b/app/src/main/java/com/kaixed/kchat/ui/base/BaseFragment.kt @@ -0,0 +1,47 @@ +package com.kaixed.kchat.ui.base + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.view.updatePadding +import androidx.fragment.app.Fragment +import androidx.viewbinding.ViewBinding +import com.kaixed.kchat.model.friend.FriendItem +import com.kaixed.kchat.utils.ConstantsUtil.getStatusBarHeight + +/** + * @Author: kaixed + * @Date: 2024/10/31 11:04 + */ +abstract class BaseFragment : Fragment() { + + private var _binding: VB? = null + protected val binding get() = _binding!! + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + _binding = inflateBinding(inflater, container) + return binding.root + } + + abstract fun inflateBinding(inflater: LayoutInflater, container: ViewGroup?): VB + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setupStatusBarPadding(binding.root) + } + + private fun setupStatusBarPadding(rootView: View) { + val statusBarHeight = getStatusBarHeight() + rootView.updatePadding(top = statusBarHeight) + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } +} diff --git a/app/src/main/java/com/kaixed/kchat/ui/fragment/ContactFragment.kt b/app/src/main/java/com/kaixed/kchat/ui/fragment/ContactFragment.kt new file mode 100644 index 0000000..60dbea3 --- /dev/null +++ b/app/src/main/java/com/kaixed/kchat/ui/fragment/ContactFragment.kt @@ -0,0 +1,102 @@ +package com.kaixed.kchat.ui.fragment + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.viewModels +import androidx.recyclerview.widget.LinearLayoutManager +import com.kaixed.kchat.R +import com.kaixed.kchat.databinding.FragmentContactBinding +import com.kaixed.kchat.model.friend.FriendItem +import com.kaixed.kchat.ui.adapter.FriendListAdapter +import com.kaixed.kchat.ui.base.BaseFragment +import com.kaixed.kchat.utils.ConstantsUtil.getUsername +import com.kaixed.kchat.viewmodel.FriendListViewModel + + +class ContactFragment : BaseFragment() { + + private val friendListViewModel: FriendListViewModel by viewModels() + + private var friendList: MutableList = mutableListOf() + + companion object { + private const val TAG = "ContactFragment" + } + + override fun inflateBinding( + inflater: LayoutInflater, + container: ViewGroup? + ): FragmentContactBinding { + return FragmentContactBinding.inflate(inflater, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + getDefaultItem() + getFriendList(getUsername()) + + binding.recycleFriendList.layoutManager = + LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false) + binding.recycleFriendList.adapter = FriendListAdapter(friendList, requireContext()) + } + + + private fun getFriendList(username: String) { + friendListViewModel.getFriendList(username).observe(requireActivity()) { value -> + value?.let { + friendList.addAll(value) + binding.recycleFriendList.adapter?.notifyDataSetChanged() + } + } + } + + private fun getDefaultItem() { + friendList.add( + FriendItem( + username = "", + avatarUrl = "", + nickname = "新的朋友", + remark = "", + signature = "" + ) + ) + friendList.add( + FriendItem( + username = "", + avatarUrl = "", + nickname = "群聊", + remark = "", + signature = "" + ) + ) + friendList.add( + FriendItem( + username = "", + avatarUrl = "", + nickname = "标签", + remark = "", + signature = "" + ) + ) + friendList.add( + FriendItem( + username = "", + avatarUrl = "", + nickname = "公众号", + remark = "", + signature = "" + ) + ) + friendList.add( + FriendItem( + username = "", + avatarUrl = "", + nickname = "服务号", + remark = "", + signature = "" + ) + ) + } +} diff --git a/app/src/main/java/com/kaixed/kchat/ui/fragment/DiscoveryFragment.kt b/app/src/main/java/com/kaixed/kchat/ui/fragment/DiscoveryFragment.kt new file mode 100644 index 0000000..a93087e --- /dev/null +++ b/app/src/main/java/com/kaixed/kchat/ui/fragment/DiscoveryFragment.kt @@ -0,0 +1,23 @@ +package com.kaixed.kchat.ui.fragment + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.kaixed.kchat.databinding.FragmentDiscoveryBinding +import com.kaixed.kchat.ui.base.BaseFragment + +class DiscoveryFragment : BaseFragment() { + + override fun inflateBinding( + inflater: LayoutInflater, + container: ViewGroup? + ): FragmentDiscoveryBinding { + return FragmentDiscoveryBinding.inflate(inflater, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + } + +} diff --git a/app/src/main/java/com/kaixed/kchat/ui/fragment/HomeFragment.kt b/app/src/main/java/com/kaixed/kchat/ui/fragment/HomeFragment.kt new file mode 100644 index 0000000..891db5b --- /dev/null +++ b/app/src/main/java/com/kaixed/kchat/ui/fragment/HomeFragment.kt @@ -0,0 +1,371 @@ +package com.kaixed.kchat.ui.fragment + +import android.animation.Animator +import android.animation.AnimatorListenerAdapter +import android.animation.ObjectAnimator +import android.annotation.SuppressLint +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.content.ServiceConnection +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.os.Bundle +import android.os.IBinder +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.app.AppCompatActivity.BIND_AUTO_CREATE +import androidx.recyclerview.widget.LinearLayoutManager +import com.kaixed.kchat.R +import com.kaixed.kchat.data.objectbox.ObjectBox.get +import com.kaixed.kchat.data.objectbox.entity.ChatLists +import com.kaixed.kchat.data.objectbox.entity.ChatLists_ +import com.kaixed.kchat.data.objectbox.entity.Messages +import com.kaixed.kchat.databinding.FragmentHomeBinding +import com.kaixed.kchat.model.HomeItem +import com.kaixed.kchat.service.WebSocketService +import com.kaixed.kchat.service.WebSocketService.LocalBinder +import com.kaixed.kchat.ui.activity.AddFriendsActivity +import com.kaixed.kchat.ui.activity.ContactUpdatesActivity +import com.kaixed.kchat.ui.activity.FriendListActivity +import com.kaixed.kchat.ui.activity.MessageActivity +import com.kaixed.kchat.ui.activity.ProfileActivity +import com.kaixed.kchat.ui.activity.SearchActivity +import com.kaixed.kchat.ui.adapter.ChatListAdapter +import com.kaixed.kchat.ui.adapter.MyGridAdapter +import com.kaixed.kchat.ui.base.BaseFragment +import com.kaixed.kchat.ui.i.OnChatListItemClickListener +import com.kaixed.kchat.utils.Constants.MMKV_COMMON_DATA +import com.tencent.mmkv.MMKV +import io.objectbox.Box + +class HomeFragment : BaseFragment(), OnChatListItemClickListener { + + private var chatLists: MutableList = ArrayList() + + private var chatListAdapter: ChatListAdapter? = null + + private var chatListsBox: Box? = null + + private var messagesBox: Box? = null + + private var updateUsername: String? = null + + private val items: MutableList = ArrayList() + + private var webSocketService: WebSocketService? = null + + private var flipped = false + + private var bound = false + + private lateinit var context: Context + + override fun inflateBinding( + inflater: LayoutInflater, + container: ViewGroup? + ): FragmentHomeBinding { + return FragmentHomeBinding.inflate(inflater, container, false) + } + + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + context = requireContext() + chatListsBox = get().boxFor(ChatLists::class.java) + messagesBox = get().boxFor(Messages::class.java) + initView() + startService() + setOnClick() + } + + private fun setOnClick() { + binding.ivMessage.setOnClickListener { + val intent = Intent( + context, + MessageActivity::class.java + ) + startActivity(intent) + } + binding.ivSearch.setOnClickListener { + val intent = + Intent(context, SearchActivity::class.java) + startActivity(intent) + } + + binding.ifvAvatar.setOnClickListener { + val intent = + Intent(context, ProfileActivity::class.java) + startActivity(intent) + } + } + + private fun flipImage(v: View) { + val flipAnimation = if (flipped) { + ObjectAnimator.ofFloat(v, "rotationX", 180f, 0f) + } else { + ObjectAnimator.ofFloat(v, "rotationX", 0f, 180f) + } + flipAnimation.setDuration(0) + flipAnimation.start() + flipped = !flipped + } + + @SuppressLint("NotifyDataSetChanged") + fun notifyData() { + chatListAdapter!!.notifyDataSetChanged() + } + + private fun startService() { + val intent = Intent( + context, + WebSocketService::class.java + ) + context.startService(intent) + } + + private fun observeLiveData() { + if (webSocketService == null) { + return + } + webSocketService!!.liveData.observe( + this + ) { messages: Messages -> + if ("0" == messages.type) { + processMessage(messages) + } + } + } + + private fun processMessage(messages: Messages) { + val talkerId = messages.takerId + val content = messages.content + val timestamp = messages.timestamp + + val chatList = createChatList(talkerId, content, timestamp) + + val index = findChatListIndex(talkerId) + if (index == -1) { + chatLists.add(chatList) + } else { + updateChatList(chatLists[index], content, timestamp) + } + + notifyData() + requireActivity().runOnUiThread { this.notifyData() } + isInDb(chatList) + } + + private fun createChatList(talkerId: String, content: String, timestamp: Long): ChatLists { + return ChatLists( + 0L, + talkerId, + talkerId, + "1", + content, + timestamp, + 1 + ) + } + + private fun findChatListIndex(talkerId: String): Int { + for (i in chatLists.indices) { + if (chatLists[i].talkerId == talkerId) { + return i + } + } + return -1 + } + + private fun updateChatList(chatList: ChatLists, content: String, timestamp: Long) { + chatList.lastContent = content + chatList.timestamp = timestamp + } + + private fun isInDb(chatList: ChatLists) { + // 查询数据库中是否已经存在该talkerId的记录 + val existingChatList = chatListsBox + ?.query(ChatLists_.talkerId.equal(chatList.talkerId)) + ?.build() + ?.findFirst() + + if (existingChatList != null) { + // 如果存在,更新内容和时间戳 + existingChatList.lastContent = chatList.lastContent + existingChatList.timestamp = chatList.timestamp + chatListsBox!!.put(existingChatList) + } else { + // 如果不存在,添加新的记录 + chatListsBox!!.put(chatList) + } + } + + private fun updateChatLists() { + val kv = MMKV.mmkvWithID(MMKV_COMMON_DATA) + val msgLocalId = kv.getLong("msgLocalId", -1L) + if (msgLocalId != -1L) { + val messages = messagesBox!![msgLocalId] + processMessage(messages) + kv.remove("msgLocalId") + } + } + + private var connection: ServiceConnection = object : ServiceConnection { + override fun onServiceConnected(className: ComponentName, service: IBinder) { + val binder = service as LocalBinder + webSocketService = binder.getService() + bound = true + observeLiveData() + } + + override fun onServiceDisconnected(arg0: ComponentName) { + bound = false + } + } + + private fun initView() { + setupGridView() + setupRecycleView() + setupMoreOptionsToggle() + } + + private fun setupMoreOptionsToggle() { + binding.ivMore.setOnClickListener { v -> + flipImage(v) + val gridViewHeight = binding.gridView.height + val startAt: Int + val endAt: Int + if (flipped) { + binding.gridView.visibility = View.VISIBLE + startAt = 0 + endAt = gridViewHeight + } else { + startAt = gridViewHeight + endAt = 0 + } + createAnimator(startAt, endAt).start() + } + } + + private fun createAnimator(start: Int, end: Int): ObjectAnimator { + val animator = ObjectAnimator.ofInt(binding!!.linearlayout, "top", start, end) + val duration: Long = 500 + animator.setDuration(duration) + + animator.addListener(object : AnimatorListenerAdapter() { + override fun onAnimationStart(animation: Animator) { + super.onAnimationStart(animation) + binding.ivMore.isEnabled = false + } + + override fun onAnimationEnd(animation: Animator) { + super.onAnimationEnd(animation) + if (!flipped) { + binding.gridView.visibility = View.INVISIBLE + } + binding.ivMore.isEnabled = true + } + }) + return animator + } + + private fun setupRecycleView() { + chatListsBox!!.all?.let { + chatLists = chatListsBox!!.all + } + chatListAdapter = ChatListAdapter(chatLists, context) + binding.recycleChatList.layoutManager = + LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) + binding.recycleChatList.adapter = chatListAdapter + chatListAdapter!!.setItemListener(this) + } + + private fun setupGridView() { + initMenuData() + val myGridAdapter = MyGridAdapter(context, items) + binding.gridView.adapter = myGridAdapter + + binding.gridView.selector = ColorDrawable(Color.TRANSPARENT) + + binding.gridView.setOnItemClickListener { _, _, position, _ -> + when (position) { + 0 -> {} + 2 -> { + val intent = + Intent( + context, + AddFriendsActivity::class.java + ) + startActivity(intent) + } + + 4 -> { + val intent2 = + Intent( + context, + ContactUpdatesActivity::class.java + ) + startActivity(intent2) + } + + 6 -> { + val intent1 = + Intent( + context, + FriendListActivity::class.java + ) + startActivity(intent1) + } + } + } + } + + private fun initMenuData() { + items.add(HomeItem("更换背景", true, R.drawable.ic_switch)) + items.add(HomeItem("创建群聊", false, R.drawable.ic_troop)) + items.add(HomeItem("新朋友", true, R.drawable.ic_friend)) + items.add(HomeItem("夜间模式", true, R.drawable.ic_night)) + items.add(HomeItem("好友动态", false, R.drawable.ic_qzone)) + items.add(HomeItem("扫一扫", false, R.drawable.ic_discovery_scan)) + items.add(HomeItem("通讯录", true, R.drawable.ic_clock_in)) + items.add(HomeItem("关闭应用", false, R.drawable.ic_exit)) + } + + + override fun onResume() { + super.onResume() + updateChatLists() + } + + override fun onStart() { + super.onStart() + val intent = Intent( + context, + WebSocketService::class.java + ) + requireActivity().bindService(intent, connection, BIND_AUTO_CREATE) + } + + override fun onStop() { + super.onStop() + if (bound) { + context.unbindService(connection) + bound = false + } + } + + override fun onDestroy() { + super.onDestroy() + val intent = Intent( + context, + WebSocketService::class.java + ) + context.stopService(intent) + } + + override fun onItemClick(username: String?) { + this.updateUsername = username + } + +} diff --git a/app/src/main/java/com/kaixed/kchat/ui/fragment/MineFragment.kt b/app/src/main/java/com/kaixed/kchat/ui/fragment/MineFragment.kt new file mode 100644 index 0000000..45d3bee --- /dev/null +++ b/app/src/main/java/com/kaixed/kchat/ui/fragment/MineFragment.kt @@ -0,0 +1,39 @@ +package com.kaixed.kchat.ui.fragment + +import android.content.Intent +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.kaixed.kchat.databinding.FragmentMineBinding +import com.kaixed.kchat.ui.activity.ProfileDetailActivity +import com.kaixed.kchat.ui.base.BaseFragment + + +class MineFragment : BaseFragment() { + + override fun inflateBinding( + inflater: LayoutInflater, + container: ViewGroup? + ): FragmentMineBinding { + return FragmentMineBinding.inflate(inflater, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + binding.clProfile.setOnClickListener { + startActivity(Intent(requireContext(), ProfileDetailActivity::class.java)) + } + + binding.ciSetting.setOnClickListener { + val bottomSheetFragment = MyBottomSheetFragment() + bottomSheetFragment.show( + requireActivity().supportFragmentManager, + bottomSheetFragment.tag + ) + } + + binding.ciSetting.setRedTipVisibility(true) + } +} diff --git a/app/src/main/java/com/kaixed/kchat/ui/widget/CustomItem.kt b/app/src/main/java/com/kaixed/kchat/ui/widget/CustomItem.kt index d77d17b..fb46eb2 100644 --- a/app/src/main/java/com/kaixed/kchat/ui/widget/CustomItem.kt +++ b/app/src/main/java/com/kaixed/kchat/ui/widget/CustomItem.kt @@ -31,14 +31,18 @@ class CustomItem @JvmOverloads constructor( val itemName = typedArray.getString(R.styleable.CustomItem_itemName) ?: "" val itemIconSize = typedArray.getDimension(R.styleable.CustomItem_iconSize, 0f) val itemIcon = typedArray.getResourceId(R.styleable.CustomItem_itemIcon, -1) + val itemLeftIcon = typedArray.getResourceId(R.styleable.CustomItem_itemLeftIcon, -1) val itemIconRound = typedArray.getDimension(R.styleable.CustomItem_iconRound, 0f) val itemDesc = typedArray.getString(R.styleable.CustomItem_itemDesc) ?: "" + val itemRedTip = typedArray.getBoolean(R.styleable.CustomItem_itemRedTip, false) val isShowTopDivider = typedArray.getBoolean(R.styleable.CustomItem_isTopDividerVisible, false) val isShowBottomDivider = typedArray.getBoolean(R.styleable.CustomItem_isBottomDividerVisible, false) + binding.viewRedTip.visibility = if (itemRedTip) View.VISIBLE else View.GONE + if (itemDesc.isNotEmpty()) { binding.tvItemDesc.visibility = View.VISIBLE binding.tvItemDesc.text = itemDesc @@ -46,6 +50,13 @@ class CustomItem @JvmOverloads constructor( binding.tvItemDesc.visibility = View.GONE } + if (itemLeftIcon != -1) { + binding.ivItemLeftIcon.visibility = View.VISIBLE + binding.ivItemLeftIcon.setImageResource(itemLeftIcon) + } else { + binding.ivItemIcon.visibility = View.GONE + } + if (itemIcon != -1) { binding.ivItemIcon.visibility = View.VISIBLE binding.ivItemIcon.setImageResource(itemIcon) @@ -61,7 +72,6 @@ class CustomItem @JvmOverloads constructor( } if (itemIconSize.toInt() != 0) { - binding.ivItemIcon.layoutParams.width = (itemIconSize * displayMetrics.density).toInt() binding.ivItemIcon.layoutParams.height = @@ -77,17 +87,12 @@ class CustomItem @JvmOverloads constructor( binding.tvItemName.text = itemName } - binding.decorationTop.visibility = if (isShowTopDivider) { - View.VISIBLE - } else { - View.GONE - } + binding.decorationTop.visibility = + if (isShowTopDivider) View.VISIBLE else View.GONE + + binding.decorationBottom.visibility = + if (isShowBottomDivider) View.VISIBLE else View.GONE - binding.decorationBottom.visibility = if (isShowBottomDivider) { - View.VISIBLE - } else { - View.GONE - } typedArray.recycle() } } @@ -95,4 +100,8 @@ class CustomItem @JvmOverloads constructor( fun setOnItemClickListener(listener: OnClickListener) { binding.root.setOnClickListener(listener) } + + fun setRedTipVisibility(visible: Boolean) { + binding.viewRedTip.visibility = if (visible) View.VISIBLE else View.GONE + } } diff --git a/app/src/main/java/com/kaixed/kchat/utils/Constants.kt b/app/src/main/java/com/kaixed/kchat/utils/Constants.kt index 65b0378..904894b 100644 --- a/app/src/main/java/com/kaixed/kchat/utils/Constants.kt +++ b/app/src/main/java/com/kaixed/kchat/utils/Constants.kt @@ -16,6 +16,10 @@ object Constants { const val MESSAGE_TYPE_FUNCTION: Int = 2 const val MESSAGE_TYPE_TITLE: Int = 2 + const val STATUS_BAR_HEIGHT = "status_bar_height" + const val KEYBOARD_HEIGHT = "keyboardHeight" + const val KEYBOARD_HEIGHT_RATIO = 0.15F const val KEYBOARD_DEFAULT_HEIGHT = 200 + const val STATUS_BAR_DEFAULT_HEIGHT: Int = 10 } diff --git a/app/src/main/java/com/kaixed/kchat/utils/ConstantsUtil.kt b/app/src/main/java/com/kaixed/kchat/utils/ConstantsUtil.kt index d9abeaa..80634be 100644 --- a/app/src/main/java/com/kaixed/kchat/utils/ConstantsUtil.kt +++ b/app/src/main/java/com/kaixed/kchat/utils/ConstantsUtil.kt @@ -1,8 +1,11 @@ package com.kaixed.kchat.utils import com.kaixed.kchat.utils.Constants.KEYBOARD_DEFAULT_HEIGHT +import com.kaixed.kchat.utils.Constants.KEYBOARD_HEIGHT import com.kaixed.kchat.utils.Constants.MMKV_COMMON_DATA import com.kaixed.kchat.utils.Constants.MMKV_USER_SESSION +import com.kaixed.kchat.utils.Constants.STATUS_BAR_DEFAULT_HEIGHT +import com.kaixed.kchat.utils.Constants.STATUS_BAR_HEIGHT import com.kaixed.kchat.utils.Constants.USERNAME_KEY import com.tencent.mmkv.MMKV @@ -13,8 +16,11 @@ import com.tencent.mmkv.MMKV object ConstantsUtil { fun getKeyboardHeight(): Int = MMKV.mmkvWithID(MMKV_COMMON_DATA) - .getInt("keyboardHeight", KEYBOARD_DEFAULT_HEIGHT) + .getInt(KEYBOARD_HEIGHT, KEYBOARD_DEFAULT_HEIGHT) fun getUsername(): String = MMKV.mmkvWithID(MMKV_USER_SESSION) .getString(USERNAME_KEY, "").toString() + + fun getStatusBarHeight(): Int = MMKV.mmkvWithID(MMKV_COMMON_DATA) + .getInt(STATUS_BAR_HEIGHT, STATUS_BAR_DEFAULT_HEIGHT) } diff --git a/app/src/main/res/drawable-hdpi/ic_discovery.xml b/app/src/main/res/drawable-hdpi/ic_discovery.xml new file mode 100644 index 0000000..800305c --- /dev/null +++ b/app/src/main/res/drawable-hdpi/ic_discovery.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-hdpi/ic_discovery_normal.xml b/app/src/main/res/drawable-hdpi/ic_discovery_normal.xml new file mode 100644 index 0000000..5486b5f --- /dev/null +++ b/app/src/main/res/drawable-hdpi/ic_discovery_normal.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable-hdpi/ic_discovery_selected.xml b/app/src/main/res/drawable-hdpi/ic_discovery_selected.xml new file mode 100644 index 0000000..42e8f0c --- /dev/null +++ b/app/src/main/res/drawable-hdpi/ic_discovery_selected.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable-hdpi/ic_location.xml b/app/src/main/res/drawable-hdpi/ic_location.xml new file mode 100644 index 0000000..b79aabc --- /dev/null +++ b/app/src/main/res/drawable-hdpi/ic_location.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable-hdpi/ic_main.xml b/app/src/main/res/drawable-hdpi/ic_main.xml new file mode 100644 index 0000000..7a1029c --- /dev/null +++ b/app/src/main/res/drawable-hdpi/ic_main.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-hdpi/ic_main_normal.xml b/app/src/main/res/drawable-hdpi/ic_main_normal.xml new file mode 100644 index 0000000..d258ab3 --- /dev/null +++ b/app/src/main/res/drawable-hdpi/ic_main_normal.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable-hdpi/ic_main_selected.xml b/app/src/main/res/drawable-hdpi/ic_main_selected.xml new file mode 100644 index 0000000..1948d7e --- /dev/null +++ b/app/src/main/res/drawable-hdpi/ic_main_selected.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable-hdpi/ic_message.xml b/app/src/main/res/drawable-hdpi/ic_message.xml new file mode 100644 index 0000000..1e6c81e --- /dev/null +++ b/app/src/main/res/drawable-hdpi/ic_message.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-hdpi/ic_message_normal.xml b/app/src/main/res/drawable-hdpi/ic_message_normal.xml new file mode 100644 index 0000000..71e507b --- /dev/null +++ b/app/src/main/res/drawable-hdpi/ic_message_normal.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable-hdpi/ic_message_selected.xml b/app/src/main/res/drawable-hdpi/ic_message_selected.xml new file mode 100644 index 0000000..7d7aaf8 --- /dev/null +++ b/app/src/main/res/drawable-hdpi/ic_message_selected.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable-hdpi/ic_mine.xml b/app/src/main/res/drawable-hdpi/ic_mine.xml new file mode 100644 index 0000000..37c0ec6 --- /dev/null +++ b/app/src/main/res/drawable-hdpi/ic_mine.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-hdpi/ic_mine_normal.xml b/app/src/main/res/drawable-hdpi/ic_mine_normal.xml new file mode 100644 index 0000000..a68734f --- /dev/null +++ b/app/src/main/res/drawable-hdpi/ic_mine_normal.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/app/src/main/res/drawable-hdpi/ic_mine_selected.xml b/app/src/main/res/drawable-hdpi/ic_mine_selected.xml new file mode 100644 index 0000000..3b0bdee --- /dev/null +++ b/app/src/main/res/drawable-hdpi/ic_mine_selected.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/ic_contact_group.xml b/app/src/main/res/drawable/ic_contact_group.xml new file mode 100644 index 0000000..6bd1055 --- /dev/null +++ b/app/src/main/res/drawable/ic_contact_group.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_contact_label.xml b/app/src/main/res/drawable/ic_contact_label.xml new file mode 100644 index 0000000..8e19b35 --- /dev/null +++ b/app/src/main/res/drawable/ic_contact_label.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_contact_new_friend.xml b/app/src/main/res/drawable/ic_contact_new_friend.xml new file mode 100644 index 0000000..c9521ab --- /dev/null +++ b/app/src/main/res/drawable/ic_contact_new_friend.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_contact_public_account.xml b/app/src/main/res/drawable/ic_contact_public_account.xml new file mode 100644 index 0000000..1d70490 --- /dev/null +++ b/app/src/main/res/drawable/ic_contact_public_account.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_contact_service_account.xml b/app/src/main/res/drawable/ic_contact_service_account.xml new file mode 100644 index 0000000..1252f88 --- /dev/null +++ b/app/src/main/res/drawable/ic_contact_service_account.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_default_avatar.xml b/app/src/main/res/drawable/ic_default_avatar.xml new file mode 100644 index 0000000..7e48db5 --- /dev/null +++ b/app/src/main/res/drawable/ic_default_avatar.xml @@ -0,0 +1,18 @@ + + + + diff --git a/app/src/main/res/drawable/ic_discovery_music.xml b/app/src/main/res/drawable/ic_discovery_music.xml new file mode 100644 index 0000000..0f43921 --- /dev/null +++ b/app/src/main/res/drawable/ic_discovery_music.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_discovery_scan.xml b/app/src/main/res/drawable/ic_discovery_scan.xml new file mode 100644 index 0000000..fcd6c6f --- /dev/null +++ b/app/src/main/res/drawable/ic_discovery_scan.xml @@ -0,0 +1,12 @@ + + + diff --git a/app/src/main/res/drawable/ic_friend_circle.xml b/app/src/main/res/drawable/ic_friend_circle.xml new file mode 100644 index 0000000..f333c9e --- /dev/null +++ b/app/src/main/res/drawable/ic_friend_circle.xml @@ -0,0 +1,54 @@ + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_mine_album.xml b/app/src/main/res/drawable/ic_mine_album.xml new file mode 100644 index 0000000..538f8d4 --- /dev/null +++ b/app/src/main/res/drawable/ic_mine_album.xml @@ -0,0 +1,12 @@ + + + diff --git a/app/src/main/res/drawable/ic_mine_card.xml b/app/src/main/res/drawable/ic_mine_card.xml new file mode 100644 index 0000000..df3dacd --- /dev/null +++ b/app/src/main/res/drawable/ic_mine_card.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_mine_collection.xml b/app/src/main/res/drawable/ic_mine_collection.xml new file mode 100644 index 0000000..8691965 --- /dev/null +++ b/app/src/main/res/drawable/ic_mine_collection.xml @@ -0,0 +1,24 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_mine_emoji.xml b/app/src/main/res/drawable/ic_mine_emoji.xml new file mode 100644 index 0000000..f849840 --- /dev/null +++ b/app/src/main/res/drawable/ic_mine_emoji.xml @@ -0,0 +1,24 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_mine_favorites.xml b/app/src/main/res/drawable/ic_mine_favorites.xml new file mode 100644 index 0000000..32e1b01 --- /dev/null +++ b/app/src/main/res/drawable/ic_mine_favorites.xml @@ -0,0 +1,12 @@ + + + diff --git a/app/src/main/res/drawable/ic_mine_pay.xml b/app/src/main/res/drawable/ic_mine_pay.xml new file mode 100644 index 0000000..c233bdd --- /dev/null +++ b/app/src/main/res/drawable/ic_mine_pay.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_mine_setting.xml b/app/src/main/res/drawable/ic_mine_setting.xml new file mode 100644 index 0000000..f40f722 --- /dev/null +++ b/app/src/main/res/drawable/ic_mine_setting.xml @@ -0,0 +1,12 @@ + + + diff --git a/app/src/main/res/drawable/mine_icon_wallet.xml b/app/src/main/res/drawable/mine_icon_wallet.xml index 0493483..df3dacd 100644 --- a/app/src/main/res/drawable/mine_icon_wallet.xml +++ b/app/src/main/res/drawable/mine_icon_wallet.xml @@ -5,11 +5,11 @@ android:viewportHeight="1024"> + android:fillColor="#1588F4"/> + android:fillColor="#1588F4"/> + android:fillColor="#1588F4"/> diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 5b479ac..4178bd0 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -3,115 +3,163 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@drawable/background" - android:fitsSystemWindows="true" + android:background="@color/white" android:orientation="vertical"> - + android:layout_height="0dp" + android:layout_weight="1.0" + android:background="@color/white" /> - + + + + + + app:layout_constraintWidth_percent="0.25"> - + - + - + + + app:layout_constraintEnd_toStartOf="@id/cl_discovery" + app:layout_constraintStart_toEndOf="@id/cl_home" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintWidth_percent="0.25"> - + + + + + + + app:layout_constraintStart_toEndOf="@id/cl_contact" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintWidth_percent="0.25"> - + + + + + + + + app:layout_constraintStart_toEndOf="@id/cl_discovery" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintWidth_percent="0.25"> + + + + + + + - - - - - - - - - - - diff --git a/app/src/main/res/layout/activity_test.xml b/app/src/main/res/layout/activity_test.xml index 6041b86..84eb7fa 100644 --- a/app/src/main/res/layout/activity_test.xml +++ b/app/src/main/res/layout/activity_test.xml @@ -12,159 +12,15 @@ android:layout_height="wrap_content" android:padding="10dp"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + app:layout_constraintTop_toTopOf="parent" + tools:text="这是一个测试文案 这是一个测试文案 这是一个测试文案 这是一个测试文案" /> diff --git a/app/src/main/res/layout/contact_recycle_footer_item.xml b/app/src/main/res/layout/contact_recycle_footer_item.xml new file mode 100644 index 0000000..c87bae7 --- /dev/null +++ b/app/src/main/res/layout/contact_recycle_footer_item.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_contact.xml b/app/src/main/res/layout/fragment_contact.xml new file mode 100644 index 0000000..8c9d51b --- /dev/null +++ b/app/src/main/res/layout/fragment_contact.xml @@ -0,0 +1,27 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_discovery.xml b/app/src/main/res/layout/fragment_discovery.xml new file mode 100644 index 0000000..01f77eb --- /dev/null +++ b/app/src/main/res/layout/fragment_discovery.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml new file mode 100644 index 0000000..bea04b1 --- /dev/null +++ b/app/src/main/res/layout/fragment_home.xml @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_mine.xml b/app/src/main/res/layout/fragment_mine.xml new file mode 100644 index 0000000..96d5f34 --- /dev/null +++ b/app/src/main/res/layout/fragment_mine.xml @@ -0,0 +1,134 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/friend_recycle_item.xml b/app/src/main/res/layout/friend_recycle_item.xml index 5048153..b879906 100644 --- a/app/src/main/res/layout/friend_recycle_item.xml +++ b/app/src/main/res/layout/friend_recycle_item.xml @@ -4,18 +4,28 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/white" - android:paddingHorizontal="15dp" - android:paddingVertical="10dp"> + android:paddingStart="15dp"> + + + app:roundPercent="0.2" /> - - - + app:layout_constraintStart_toStartOf="@id/tv_nickname" /> \ No newline at end of file diff --git a/app/src/main/res/layout/item_custom.xml b/app/src/main/res/layout/item_custom.xml index bc80bd8..fac6f83 100644 --- a/app/src/main/res/layout/item_custom.xml +++ b/app/src/main/res/layout/item_custom.xml @@ -9,12 +9,23 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/attr.xml b/app/src/main/res/values/attr.xml index db8c077..2e70e4b 100644 --- a/app/src/main/res/values/attr.xml +++ b/app/src/main/res/values/attr.xml @@ -18,6 +18,8 @@ + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0a66164..7c1e696 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -5,5 +5,7 @@ 设置 %1$s 撤回了一条消息 + + Hello blank fragment \ No newline at end of file