feat(聊天模块): 修改聊天模块adapter

使用多itemtype代替原消息显示逻辑
This commit is contained in:
糕小菜 2024-10-26 23:37:17 +08:00
parent e81bbf516d
commit 7c2a9f0619
16 changed files with 518 additions and 93 deletions

View File

@ -20,6 +20,9 @@
android:theme="@style/Theme.KChatAndroid" android:theme="@style/Theme.KChatAndroid"
android:usesCleartextTraffic="true" android:usesCleartextTraffic="true"
tools:targetApi="31"> tools:targetApi="31">
<activity
android:name=".view.activity.ContactUpdatesActivity"
android:exported="false" />
<activity <activity
android:name=".view.activity.ApproveDetailActivity" android:name=".view.activity.ApproveDetailActivity"
android:exported="false" /> android:exported="false" />

View File

@ -0,0 +1,22 @@
package com.kaixed.kchat.utils
/**
* @Author: kaixed
* @Date: 2024/10/25 14:38
*/
enum class MessageType(val value: Int) {
CUSTOM(0),
TIP(1),
IMAGE(2),
VOICE(3),
LOCATION(4),
EMOJI(5),
RED_PACKET(6);
companion object {
fun fromValue(value: Int): MessageType? {
return entries.find { it.value == value }
}
}
}

View File

@ -36,6 +36,7 @@ import com.kaixed.kchat.database.entity.Messages;
import com.kaixed.kchat.database.entity.Messages_; import com.kaixed.kchat.database.entity.Messages_;
import com.kaixed.kchat.databinding.ActivityChatBinding; import com.kaixed.kchat.databinding.ActivityChatBinding;
import com.kaixed.kchat.service.WebSocketService; import com.kaixed.kchat.service.WebSocketService;
import com.kaixed.kchat.utils.ConstantsUtil;
import com.kaixed.kchat.utils.ImageSpanUtil; import com.kaixed.kchat.utils.ImageSpanUtil;
import com.kaixed.kchat.view.adapter.ChatAdapter; import com.kaixed.kchat.view.adapter.ChatAdapter;
import com.kaixed.kchat.view.adapter.EmojiAdapter; import com.kaixed.kchat.view.adapter.EmojiAdapter;
@ -53,8 +54,6 @@ import io.objectbox.Box;
import io.objectbox.query.Query; import io.objectbox.query.Query;
import io.objectbox.query.QueryBuilder; import io.objectbox.query.QueryBuilder;
import com.kaixed.kchat.utils.ConstantsUtil;
/** /**
* @author hui * @author hui
*/ */
@ -489,7 +488,7 @@ public class ChatActivity extends AppCompatActivity implements OnItemClickListen
"normal", "normal",
username, username,
friendId, friendId,
"normal", "0",
true true
); );

View File

@ -0,0 +1,39 @@
package com.kaixed.kchat.view.activity
import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.constraintlayout.widget.ConstraintSet
import com.kaixed.kchat.databinding.ActivityContactUpdatesBinding
import com.kaixed.kchat.utils.DensityUtil
class ContactUpdatesActivity : AppCompatActivity() {
private lateinit var binding: ActivityContactUpdatesBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
binding = ActivityContactUpdatesBinding.inflate(layoutInflater)
setContentView(binding.root)
val constraintSet = ConstraintSet()
constraintSet.clone(binding.main)
constraintSet.connect(
binding.ifvAvatar.id,
ConstraintSet.START,
ConstraintSet.PARENT_ID,
ConstraintSet.START,
DensityUtil.dpToPx(this, 10)
)
constraintSet.connect(
binding.tvContent.id,
ConstraintSet.START,
binding.ifvAvatar.id,
ConstraintSet.END,
DensityUtil.dpToPx(this, 10)
)
constraintSet.applyTo(binding.main)
}
}

View File

@ -38,6 +38,7 @@ import java.util.List;
import io.objectbox.Box; import io.objectbox.Box;
/** /**
* @Author: kaixed * @Author: kaixed
* @Date: 2024/5/26 10:02 * @Date: 2024/5/26 10:02
@ -118,7 +119,7 @@ public class MainActivity extends AppCompatActivity implements OnChatListItemCli
return; return;
} }
webSocketService.getLiveData().observe(this, messages -> { webSocketService.getLiveData().observe(this, messages -> {
if ("normal".equals(messages.getType())) { if ("0".equals(messages.getType())) {
processMessage(messages); processMessage(messages);
} }
}); });
@ -286,6 +287,10 @@ public class MainActivity extends AppCompatActivity implements OnChatListItemCli
Intent intent = new Intent(MainActivity.this, AddFriendsActivity.class); Intent intent = new Intent(MainActivity.this, AddFriendsActivity.class);
startActivity(intent); startActivity(intent);
break; break;
case 4:
Intent intent2 = new Intent(MainActivity.this, ContactUpdatesActivity.class);
startActivity(intent2);
break;
case 6: case 6:
Intent intent1 = new Intent(MainActivity.this, FriendListActivity.class); Intent intent1 = new Intent(MainActivity.this, FriendListActivity.class);
startActivity(intent1); startActivity(intent1);

View File

@ -1,65 +1,110 @@
package com.kaixed.kchat.view.adapter package com.kaixed.kchat.view.adapter
import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.graphics.drawable.ColorDrawable import android.graphics.drawable.ColorDrawable
import android.text.SpannableString
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView
import android.widget.PopupWindow import android.widget.PopupWindow
import android.widget.TextView import androidx.constraintlayout.widget.ConstraintSet
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.window.layout.WindowMetrics
import androidx.window.layout.WindowMetricsCalculator
import com.kaixed.kchat.R import com.kaixed.kchat.R
import com.kaixed.kchat.database.entity.Messages
import com.kaixed.kchat.database.ObjectBox import com.kaixed.kchat.database.ObjectBox
import com.kaixed.kchat.database.UserManager import com.kaixed.kchat.database.UserManager
import com.kaixed.kchat.utils.Constants.TYPE_ITEM_MINE import com.kaixed.kchat.database.entity.Messages
import com.kaixed.kchat.utils.Constants.TYPE_ITEM_OTHER import com.kaixed.kchat.databinding.ChatRecycleItemCustomBinding
import com.kaixed.kchat.utils.Constants.TYPE_MESSAGE_WITHDRAW import com.kaixed.kchat.databinding.ChatRecycleItemEmojiBinding
import com.kaixed.kchat.utils.ImageSpanUtil import com.kaixed.kchat.databinding.ChatRecycleItemImageBinding
import com.kaixed.kchat.databinding.ChatRecycleItemMineBinding import com.kaixed.kchat.databinding.ChatRecycleItemLocationBinding
import com.kaixed.kchat.databinding.ChatRecycleItemOtherBinding import com.kaixed.kchat.databinding.ChatRecycleItemRedPacketBinding
import com.kaixed.kchat.databinding.ChatRecycleItemWithdrawBinding import com.kaixed.kchat.databinding.ChatRecycleItemTipBinding
import com.kaixed.kchat.databinding.ChatRecycleItemVoiceBinding
import com.kaixed.kchat.databinding.PopwindowsBinding import com.kaixed.kchat.databinding.PopwindowsBinding
import java.util.LinkedList import com.kaixed.kchat.utils.DensityUtil
import com.kaixed.kchat.utils.ImageSpanUtil
import com.kaixed.kchat.utils.MessageType
import io.objectbox.Box import io.objectbox.Box
import java.util.LinkedList
class ChatAdapter( class ChatAdapter(
private val context: Context, private val context: Context,
private val messages: LinkedList<Messages> private val messages: LinkedList<Messages>
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() { ) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { companion object {
return when (viewType) { private const val ITEM_TYPE_CUSTOM = 0
TYPE_ITEM_MINE -> { private const val ITEM_TYPE_TIP = 1
val binding = ChatRecycleItemMineBinding.inflate( private const val ITEM_TYPE_IMAGE = 2
LayoutInflater.from(parent.context), private const val ITEM_TYPE_VOICE = 3
parent, private const val ITEM_TYPE_LOCATION = 4
false private const val ITEM_TYPE_EMOJI = 5
) private const val ITEM_TYPE_RED_PACKET = 6
MineViewHolder(binding)
} }
TYPE_ITEM_OTHER -> { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val binding = ChatRecycleItemOtherBinding.inflate( return when (viewType) {
ITEM_TYPE_TIP -> {
val binding = ChatRecycleItemTipBinding.inflate(
LayoutInflater.from(parent.context), LayoutInflater.from(parent.context),
parent, parent,
false false
) )
OtherViewHolder(binding) TipViewHolder(binding)
}
ITEM_TYPE_IMAGE -> {
val binding = ChatRecycleItemImageBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
ImageViewHolder(binding)
}
ITEM_TYPE_VOICE -> {
val binding = ChatRecycleItemVoiceBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
VoiceViewHolder(binding)
}
ITEM_TYPE_LOCATION -> {
val binding = ChatRecycleItemLocationBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
LocationViewHolder(binding)
}
ITEM_TYPE_EMOJI -> {
val binding = ChatRecycleItemEmojiBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
EmojiViewHolder(binding)
}
ITEM_TYPE_RED_PACKET -> {
val binding = ChatRecycleItemRedPacketBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
RedPacketViewHolder(binding)
} }
else -> { else -> {
val binding = ChatRecycleItemWithdrawBinding.inflate( val binding = ChatRecycleItemCustomBinding.inflate(
LayoutInflater.from(parent.context), LayoutInflater.from(parent.context),
parent, parent,
false false
) )
WithdrawViewHolder(binding) CustomViewHolder(binding)
} }
} }
} }
@ -68,39 +113,158 @@ class ChatAdapter(
val singleMessage = messages[position] val singleMessage = messages[position]
when (holder) { when (holder) {
is MineViewHolder -> { is TipViewHolder -> {
val spannableString = ImageSpanUtil.setEmojiSpan( holder.bindData(singleMessage)
context, }
singleMessage.content,
holder.binding.tvMineContent.textSize.toInt() is CustomViewHolder -> {
) holder.bindData(singleMessage, context)
holder.binding.tvMineContent.text = spannableString }
holder.binding.tvMineContent.setOnLongClickListener {
is ImageViewHolder -> {
holder.bindData(singleMessage)
}
is VoiceViewHolder -> {
holder.bindData(singleMessage)
}
is LocationViewHolder -> {
holder.bindData(singleMessage)
}
is EmojiViewHolder -> {
holder.bindData(singleMessage)
}
is RedPacketViewHolder -> {
holder.bindData(singleMessage)
}
}
holder.itemView.setOnLongClickListener {
showPopupWindow(it, position) showPopupWindow(it, position)
true true
} }
} }
is OtherViewHolder -> {
val spannableString = ImageSpanUtil.setEmojiSpan( class TipViewHolder(val binding: ChatRecycleItemTipBinding) :
context, RecyclerView.ViewHolder(binding.root) {
singleMessage.content, fun bindData(message: Messages) {
holder.binding.tvOtherContent.textSize.toInt() if (message.show)
binding.tvTip.text = binding.root.context.getString(
R.string.withdraw_message_format,
message.senderId
) )
holder.binding.tvOtherContent.text = spannableString
holder.binding.tvOtherContent.setOnLongClickListener {
showPopupWindow(it, position)
true
} }
} }
is WithdrawViewHolder -> {
holder.bindData(singleMessage.senderId) class CustomViewHolder(val binding: ChatRecycleItemCustomBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bindData(message: Messages, context: Context) {
val isMine: Boolean = message.senderId == UserManager.getUsername()
setViewPosition(binding, isMine, context).applyTo(binding.main)
binding.tvMessageContent.text = ImageSpanUtil.setEmojiSpan(context, message.content, 12)
} }
private fun setViewPosition(
binding: ChatRecycleItemCustomBinding,
isMine: Boolean,
context: Context
): ConstraintSet {
val constraintSet = ConstraintSet()
constraintSet.clone(binding.main)
val avatarId: Int = binding.ifvAvatar.id
val messageId = binding.tvMessageContent.id
val margin = DensityUtil.dpToPx(context, 10)
val (avatarConnectionStart, avatarConnectionEnd) = if (isMine) {
ConstraintSet.END to ConstraintSet.START
} else {
ConstraintSet.START to ConstraintSet.END
}
constraintSet.connect(
avatarId, avatarConnectionStart,
ConstraintSet.PARENT_ID, avatarConnectionStart,
margin
)
constraintSet.connect(
messageId, avatarConnectionStart,
avatarId, avatarConnectionEnd,
margin
)
return constraintSet
} }
} }
class ImageViewHolder(val binding: ChatRecycleItemImageBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bindData(message: Messages) {
}
}
class VoiceViewHolder(val binding: ChatRecycleItemVoiceBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bindData(message: Messages) {
}
}
class LocationViewHolder(val binding: ChatRecycleItemLocationBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bindData(message: Messages) {
}
}
class EmojiViewHolder(val binding: ChatRecycleItemEmojiBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bindData(message: Messages) {
}
}
class RedPacketViewHolder(val binding: ChatRecycleItemRedPacketBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bindData(message: Messages) {
}
}
override fun getItemViewType(position: Int): Int {
val message = messages[position]
return when (MessageType.fromValue(message.type.toInt())) {
MessageType.TIP -> ITEM_TYPE_TIP
MessageType.IMAGE -> ITEM_TYPE_IMAGE
MessageType.VOICE -> ITEM_TYPE_VOICE
MessageType.LOCATION -> ITEM_TYPE_LOCATION
MessageType.EMOJI -> ITEM_TYPE_EMOJI
MessageType.RED_PACKET -> ITEM_TYPE_RED_PACKET
MessageType.CUSTOM -> ITEM_TYPE_CUSTOM
null -> ITEM_TYPE_CUSTOM
}
}
private fun updateDb(message: Messages) {
val messagesBox: Box<Messages> = ObjectBox.get().boxFor(Messages::class.java)
messagesBox.put(message)
}
override fun getItemCount(): Int = messages.size
private fun showPopupWindow(view: View, position: Int) { private fun showPopupWindow(view: View, position: Int) {
val binding: PopwindowsBinding = PopwindowsBinding.inflate(LayoutInflater.from(context)) val binding: PopwindowsBinding = PopwindowsBinding.inflate(LayoutInflater.from(context))
val popupWindow: PopupWindow = PopupWindow( val popupWindow: PopupWindow = PopupWindow(
@ -158,34 +322,4 @@ class ChatAdapter(
popupWindow.dismiss() popupWindow.dismiss()
} }
} }
private fun updateDb(message: Messages) {
val messagesBox: Box<Messages> = ObjectBox.get().boxFor(Messages::class.java)
messagesBox.put(message)
}
override fun getItemViewType(position: Int): Int {
val curUser = UserManager.getUsername()
return if (messages[position].status == "withdraw") {
TYPE_MESSAGE_WITHDRAW
} else {
if (curUser == messages[position].senderId) TYPE_ITEM_MINE else TYPE_ITEM_OTHER
}
}
override fun getItemCount(): Int = messages.size
class MineViewHolder(val binding: ChatRecycleItemMineBinding) :
RecyclerView.ViewHolder(binding.root)
class OtherViewHolder(val binding: ChatRecycleItemOtherBinding) :
RecyclerView.ViewHolder(binding.root)
class WithdrawViewHolder(val binding: ChatRecycleItemWithdrawBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bindData(nickname: String) {
binding.tvUserNickname.text = nickname
}
}
} }

View File

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context=".view.activity.ContactUpdatesActivity">
<androidx.constraintlayout.utils.widget.ImageFilterView
android:id="@+id/ifv_avatar"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginVertical="20dp"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:src="@drawable/ic_avatar"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:roundPercent="0.3" />
<TextView
android:id="@+id/tv_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:background="@drawable/chat_send_btn_bac_mine"
android:gravity="center_vertical"
android:maxWidth="250dp"
android:minHeight="40dp"
android:paddingHorizontal="12dp"
android:paddingVertical="6dp"
android:text="你好你好"
android:textColor="@color/white"
app:layout_constraintBottom_toBottomOf="@id/ifv_avatar"
app:layout_constraintTop_toTopOf="@id/ifv_avatar" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.utils.widget.ImageFilterView
android:id="@+id/ifv_avatar"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginVertical="20dp"
android:layout_marginEnd="10dp"
android:src="@drawable/ic_avatar"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:roundPercent="0.3" />
<TextView
android:id="@+id/tv_message_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:background="@drawable/chat_send_btn_bac_mine"
android:gravity="center_vertical"
android:maxWidth="250dp"
android:minHeight="40dp"
android:paddingHorizontal="12dp"
android:paddingVertical="6dp"
android:text="你好你好"
android:textColor="@color/white"
app:layout_constraintBottom_toBottomOf="@id/ifv_avatar"
app:layout_constraintTop_toTopOf="@id/ifv_avatar" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:paddingVertical="10dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="这是一个小tip"
android:textColor="@color/black"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:paddingVertical="10dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="这是一个小tip"
android:textColor="@color/black"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:paddingVertical="10dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="这是一个小tip"
android:textColor="@color/black"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:paddingVertical="10dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="这是一个小tip"
android:textColor="@color/black"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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">
<androidx.constraintlayout.utils.widget.ImageFilterView
android:id="@+id/ifv_avatar"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginVertical="20dp"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:src="@drawable/ic_avatar"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:roundPercent="0.3" />
<TextView
android:id="@+id/tv_mine_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:background="@drawable/chat_send_btn_bac_mine"
android:gravity="center_vertical"
android:maxWidth="250dp"
android:minHeight="40dp"
android:paddingHorizontal="12dp"
android:paddingVertical="6dp"
android:text="你好你好"
android:textColor="@color/white"
app:layout_constraintEnd_toStartOf="@id/ifv_avatar"
app:layout_constraintTop_toTopOf="@id/ifv_avatar" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:paddingVertical="10dp">
<TextView
android:id="@+id/tv_tip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="这是一个小tip"
android:textColor="@color/black"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:paddingVertical="10dp">
<androidx.constraintlayout.utils.widget.ImageFilterView
android:id="@+id/ifv_avatar"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginEnd="10dp"
android:src="@drawable/ic_avatar"
app:layout_constraintTop_toTopOf="parent"
app:roundPercent="0.3" />
<ImageView
android:id="@+id/iv_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:maxWidth="200dp"
android:maxHeight="200dp"
app:layout_constraintStart_toEndOf="@id/ifv_avatar"
app:layout_constraintTop_toTopOf="@id/ifv_avatar" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -3,18 +3,7 @@
<string name="signature">平安顺遂,喜乐无忧</string> <string name="signature">平安顺遂,喜乐无忧</string>
<string name="name">kaixed</string> <string name="name">kaixed</string>
<string name="setting">设置</string> <string name="setting">设置</string>
<!-- Preference Titles -->
<string name="messages_header">Messages</string>
<string name="sync_header">Sync</string>
<!-- Messages Preferences --> <string name="withdraw_message_format">%1$s 撤回了一条消息</string>
<string name="signature_title">Your signature</string>
<string name="reply_title">Default reply action</string>
<!-- Sync Preferences -->
<string name="sync_title">Sync email periodically</string>
<string name="attachment_title">Download incoming attachments</string>
<string name="attachment_summary_on">Automatically download attachments for incoming emails
</string>
<string name="attachment_summary_off">Only download attachments when manually requested</string>
</resources> </resources>