Kotlin高仿微信-项目实践58篇详细讲解了各个功能点,包括:注册、登录、主页、单聊(文本、表情、语音、图片、小视频、视频通话、语音通话、红包、转账)、群聊、个人信息、朋友圈、支付服务、扫一扫、搜索好友、添加好友、开通VIP等众多功能。
效果图:
详细的聊天功能请查看Kotlin高仿微信-第8篇-单聊,这里是提示文本功能的部分实现。
实现代码:
<RelativeLayout android:id="@+id/chat_item_me_content_layout" app:layout_constraintEnd_toStartOf="@+id/chat_item_me_avatar" app:layout_constraintTop_toTopOf="@+id/chat_item_me_avatar" android:layout_width="280dp" android:gravity="right" android:visibility="gone" android:layout_height="wrap_content"> <androidx.appcompat.widget.AppCompatTextView android:id="@+id/chat_item_me_content" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="10dp" android:background="@drawable/wc_chat_me3_normal" android:gravity="left|center" android:paddingVertical="12dp" android:paddingLeft="12dp" android:paddingRight="18dp" android:text="我的" android:textColor="@color/black" android:textSize="18sp"/> </RelativeLayout>
<RelativeLayout android:id="@+id/chat_item_other_content_layout" app:layout_constraintStart_toEndOf="@+id/chat_item_other_avatar" app:layout_constraintTop_toTopOf="@+id/chat_item_other_avatar" android:visibility="gone" android:layout_width="280dp" android:layout_height="wrap_content"> <androidx.appcompat.widget.AppCompatTextView android:id="@+id/chat_item_other_content" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingVertical="12dp" android:paddingLeft="18dp" android:paddingRight="12dp" android:layout_marginStart="10dp" android:gravity="left|center" android:background="@drawable/wc_chat_other3_normal" android:visibility="visible" android:textColor="@color/black" android:textSize="18sp" android:text="1"/> </RelativeLayout>
Emoji控件 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <com.wn.wechatclientdemo.view.EmojiViewPager android:id="@+id/chat_emoji_viewpage" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="10dp" /> <com.wn.wechatclientdemo.view.EmojiIndicatorView android:id="@+id/chat_emoji_iv" android:layout_width="match_parent" android:layout_height="20dp" android:layout_gravity="bottom" android:layout_marginLeft="16dp" android:layout_marginTop="30dp" android:layout_marginRight="16dp" android:background="@color/wc_base_bg" app:gravity="0" app:indicatorColor="#668b8989" app:indicatorColorSelected="#ffffff" app:indicatorWidth="6" /> </LinearLayout>
fun initEmoji(){ emojiViewModel.getAllEmoji() emojiViewModel.emojiListLiveData.observe(viewLifecycleOwner) { mListEmoji = it val vpEmoji: ViewPager = chat_emoji_viewpage val indEmoji: EmojiIndicatorView = chat_emoji_iv val inflater = LayoutInflater.from(activity) val pageSize: Int = EVERY_PAGE_SIZE val mEmojiBean = EmojiBean(0, 0) val deleteCount = Math.ceil(mListEmoji.size * 1.0 / EVERY_PAGE_SIZE) .toInt() //要显示的删除键的数量 //TagUtils.d("" + deleteCount) for (i in 1 until deleteCount + 1) { if (i == deleteCount) { mListEmoji.add(mListEmoji.size, mEmojiBean) } else { mListEmoji.add(i * EVERY_PAGE_SIZE - 1, mEmojiBean) } //TagUtils.d("添加次数$i") } val pageCount = Math.ceil(mListEmoji.size * 1.0 / pageSize).toInt() //一共的页数 //TagUtils.d("总共的页数:$pageCount") val viewList: MutableList<View> = ArrayList() for (index in 0 until pageCount) { //每个页面创建一个recycleview val recyclerView = inflater.inflate(R.layout.wc_chat_emoji_item_vprecy, vpEmoji, false) as RecyclerView recyclerView.layoutManager = GridLayoutManager(activity, 7) var entranceAdapter: EmojiAdapter if (index == pageCount - 1) { //最后一页的数据 val lastPageList: List<EmojiBean> = mListEmoji.subList( index * EVERY_PAGE_SIZE, mListEmoji.size ) entranceAdapter = EmojiAdapter(lastPageList) } else { entranceAdapter = EmojiAdapter( mListEmoji.subList( index * EVERY_PAGE_SIZE, (index + 1) * EVERY_PAGE_SIZE )) } entranceAdapter.setOnItemClickListener(object : BaseQuickAdapter.OnItemClickListener{ override fun onItemClick( adapter: BaseQuickAdapter<*, *>, view: View?, position: Int ) { if (mEmojiBean.id === 0) { //如果是删除键 //mEditText.dispatchKeyEvent( KeyEvent( KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL ) ) } else { //mEditText.append((adapter.getData().get(position) as EmojiBean).getUnicodeInt() ) } } }) recyclerView.adapter = entranceAdapter viewList.add(recyclerView) entranceAdapter.setOnItemClickListener { adapter, view, position -> TagUtils.d("点击表情:${position} , ${adapter.getItem(position)}") var emojiBean = adapter.getItem(position) as EmojiBean var content = chat_content.text.toString() content += emojiBean.getEmojiStringByUnicode(emojiBean.unicode) chat_content.setText(content) //光标放在最后 chat_content?.setSelection(content.length) } } val adapter = EmojiVpAdapter(viewList) vpEmoji.adapter = adapter indEmoji.setIndicatorCount(vpEmoji.adapter!!.count) indEmoji.setCurrentIndicator(vpEmoji.currentItem) vpEmoji.addOnPageChangeListener(object : SimpleOnPageChangeListener() { override fun onPageSelected(position: Int) { indEmoji.setCurrentIndicator(position) } }) //TagUtils.d("Emoji初始化完毕") } }
//发送文本、红包、表情 private fun sendMessage(chatBean: ChatBean){ if(chatBean == null){ ToastUtils.makeText(requireActivity(), "发送信息不能为空") return } var content = chatBean.content if(TextUtils.isEmpty(content)){ ToastUtils.makeText(requireActivity(), "发送信息不能为空") } else { ChatManagerUtils.getInstance().sendMessage(toUserId, content) chat_content.setText("") CoroutineScope(Dispatchers.IO).launch { if(chatBean.contentType == ChatBean.CONTENT_TYPE_REDPACKET){ var content = chatBean.content chatBean.content = CommonUtils.Chat.getRedpacket(content).toString() } else if(chatBean.contentType == ChatBean.CONTENT_TYPE_TRANSFER){ var content = chatBean.content chatBean.content = CommonUtils.Chat.getTransfer(content).toString() } ChatRepository.insertChat(chatBean) } refreshBase(chatBean) } } /** * 刷新发送、接收聊天信息 * @param chatBean ChatBean */ private fun refreshBase(chatBean: ChatBean){ CoroutineScope(Dispatchers.Main).launch { //chatViewModel.insertChat(chatBean) TagUtils.d("ChatFragment refreshBase 刷新聊天信息 ") adapter.refresh(chatBean) if(chatBean.contentType == ChatBean.CONTENT_TYPE_LOCATION){ delay(200) } swipe_target.scrollToPosition(adapter.itemCount -1) } }