如何在聊天消息顶部显示日历日期?
How do I display the calendar date on the top of chat messages?
我正在开发一个聊天应用程序,但我正在努力弄清楚如何在聊天消息的顶部显示日历日期;例如,像这样:
另一张带时间戳的图片:
如您在示例中所见,日期显示在新一批短信的顶部。我想做同样的事情,只是针对与特定日期相关的一批消息。假设我有 10 月 19 日的消息,它会在顶部显示 10 月 19 日,然后是 10 月 20 日的消息,依此类推...这是与我的类似的工作示例代码:
http://www.codeproject.com/Tips/897826/Designing-Android-Chat-Bubble-Chat-UI
代码的构造与我的相同,除了顶部显示的日期是我坚持的东西。我为每条消息显示了我的时间戳,我只想以 "Monday, October 19, 2015" 格式显示该批消息的日期;只有一次在顶部,从 10 月 19 日开始,以及过去和未来消息的日历日期,如图所示。有什么线索吗?
谢谢!
只需在该日期内创建一个消息视图组。或者我会推荐这样的结构:
ListView
- ListViewItem1
- (TextView with date)
- ListViewOfMsgsForThatDate
a) Message1ForThisDate
b) Message2ForThisDate
- ListViewItem2
- (TextView with date)
- ListViewOfMsgsForThatDate
a) Message1ForThisDate
b) Message2ForThisDate
好的;这是一个解决方案,可以为您提供构建的想法。它将解决您的日期问题,并为您提供有关如何解决时间显示方式的提示。这种程序涉及大量工作才能使其作为应用程序可行。您需要考虑时区、存储消息的位置等。
现在堆栈溢出的目的不是为人们设计程序,而是试图帮助引导人们朝着正确的方向发展。
为了回答您的问题;我介绍的是基础知识,我不会给你准备好的代码 运行,你需要做一些工作:)
这可以通过多种方式解决,我选择了以下方式:
使用共享首选项来存储最新的日期时间。我考虑过访问聊天消息数组,但认为这可能更简单。我选择了共享首选项,以便在关闭并重新打开应用程序时保留最后一条消息的日期。 (最终,聊天消息需要保留在数据库 [SQLite] 中,以便在应用程序关闭和打开时消息仍然存在)。请看最后我的评论。
public class ChatActivity extends ActionBarActivity {
public static final String MyPREF = "MyPrefs" ;
public static final String DATE_KEY = "DateKey" ;
SharedPreferences prefs;
SharedPreferences.Editor editor;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
initControls();
pref = getSharedPreferences(MyPREF, MODE_PRIVATE);
// Check that the shared preferences has a value
if(contains(DATE_KEY)){
editor = prefs.edit();
// get the current datetime.
editor.Long(DATE_KEY, date);
editor.commit();
// set the text of the textview android:id="@+id/date"
// with the current formatted date.
}
}
在你的onclick侦听器中,你测试最后存储的日期是今天的日期,如果不是,它被转换成聊天消息,所以它的值可以添加到数组并显示。您将不得不调整格式化这些消息类型的方式,您甚至可以为这些日期消息添加一个唯一 ID 并对此进行测试,或者测试没有 ID。有很多解决方案。
sendBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
/.../
}
ChatMessage chatMessage = new ChatMessage();
long LastDate = pref.getLong(DATE_KEY, date);
// create an empty chat message with only a date.
// check if the last used date is not today's date.
// if it's not today's date, save it and display it as
// an archived bubble.
if(!LastDate.isToday()){
ChatMessage dateholder = new ChatMessage();
// You'll need to format the date
dateholder.setDate(Formatted LastDate);
// set/change the text of the textview android:id="@+id/date"
// with the current formatted date.
}
// put the current date time into your preferences.
editor = prefs.edit();
editor.putString(DATE_KEY, date);
editor.commit();
/.../
// this is now setting up the new chat message.
chatMessage.setDate(DateFormat.getDateTimeInstance().format(new Date()));
/.../
displayMessage(chatMessage);
}
});
xml 好的,现在您不希望只有一条消息时日期浮动在屏幕顶部。所以将它与滚动列表分组,然后将其设置为相对于屏幕布局,因为列表本身就是这样。线性布局就可以做到这一点,只要确保它的方向设置为垂直即可。
将它们固定在一起。
<LinearLayout
android:layout_above="@+id/messageEdit"
android:layout_below="@+id/meLbl"
android:id="@+id/layout"
.../orentation:vertical ../
<TextView
android:id="@+id/date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
/..//>
<ListView
android:id="@+id/messagesContainer"
.../
...//>
</LinearLayout>
<TextView
android:id="@+id/meLbl"
/.../
/>
<TextView
android:id="@+id/friendLabel"
/.../
/>
更改每条消息的显示 您将需要停止显示每条消息的日期时间。如果您希望单独显示每条消息的时间,并在同一分钟内跳过这些消息,正如您在评论中提到的那样,那么我已经为您提供了框架,以找出如何测试这个以及程序逻辑是否是否显示。
您需要在您的:
中更改此设置
public View getView(final int position, View convertView, ViewGroup parent) {
holder.txtMessage.setText(chatMessage.getMessage());
// TODO,
// do a check if(mins are the same don't post
// else post the format without the date)
// holder.txtInfo.setText(chatMessage.getDate());
不要忘记应用所有必要的导入。
我建议,如果您开始使用没有共享首选项的最后一次访问数据库,或者继续使用共享首选项。这变得更加复杂,因为要保存多个聊天记录,在这种情况下,最好使用数据库。
据我了解,您只想为特定的消息组而不是每条消息显示 time/date。所以这里是如何做到这一点。
前提条件:我假设每个消息项都有时间戳,我们将根据该时间戳进行分组
想法:我们需要每个列表项都有 timeview:TextView 元素,我们将根据它的位置和 TS(时间戳)显示和隐藏该元素
示例:
item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:orientation="vertical"
android:padding="@dimen/padding_small">
<TextView
android:id="@+id/timeText"
style="@style/DefaultText"
android:paddingBottom="@dimen/padding_small"
android:paddingRight="@dimen/padding_small"
android:paddingTop="@dimen/padding_small"
android:text="@string/hello_world"
android:textSize="@dimen/font_size_small_10"/>
<TextView
android:id="@+id/textView"
style="@style/DefaultText"
android:layout_width="wrap_content"
android:background="@drawable/bg_gray_rounded"
android:paddingBottom="@dimen/padding_extra_small"
android:paddingLeft="@dimen/padding_small"
android:paddingRight="@dimen/padding_small"
android:paddingTop="@dimen/padding_extra_small"
android:textColor="@color/gray"
android:textSize="@dimen/font_size_md"/>
</LinearLayout>
ChatRecyclerAdapter.java
public class ChatEGRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
public static class TextViewHolder extends RecyclerView.ViewHolder {
public TextView textView;
public TextView timeText;
public TextViewHolder(View v) {
super(v);
timeText = (TextView) v.findViewById(R.id.timeText);
textView = (TextView) v.findViewById(R.id.textView);
}
}
private final List<Message> messages;
public ChatEGRecyclerAdapter(List<Message> messages) {
this.messages = messages;
}
// Create new views (invoked by the layout manager)
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item, parent, false);
return new TextViewHolder(v);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
final Message m = messages.get(position);
final Context context = viewHolder.itemView.getContext();
TextViewHolder holder = (TextViewHolder)viewHolder;
holder.textView.setVisibility(View.VISIBLE);
holder.textView.setText(m.getText());
long previousTs = 0;
if(position>1){
Message pm = messages.get(position-1);
previousTs = pm.getTimeStamp();
}
setTimeTextVisibility(m.getTimeStamp(), previousTs, holder.timeText);
}
private void setTimeTextVisibility(long ts1, long ts2, TextView timeText){
if(ts2==0){
timeText.setVisibility(View.VISIBLE);
timeText.setText(Utils.formatDayTimeHtml(ts1));
}else {
Calendar cal1 = Calendar.getInstance();
Calendar cal2 = Calendar.getInstance();
cal1.setTimeInMillis(ts1);
cal2.setTimeInMillis(ts2);
boolean sameMonth = cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) &&
cal1.get(Calendar.MONTH) == cal2.get(Calendar.MONTH);
if(sameMonth){
timeText.setVisibility(View.GONE);
timeText.setText("");
}else {
timeText.setVisibility(View.VISIBLE);
timeText.setText(Utils.formatDayTimeHtml(ts2));
}
}
}
@Override
public int getItemCount() {
return messages.size();
}
}
剩下的就是创建您的 RecylcerView 并为其提供此适配器
使用此 xml 有助于根据您的 link - Designing Android Chat Bubble (Chat UI) 建议创建您正在寻找的布局。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:orientation="vertical">
<TextView
android:id="@+id/txtDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp"
android:layout_gravity="right"
android:textSize="12sp"
android:textColor="@android:color/darker_gray" />
<LinearLayout
android:id="@+id/contentWithBackground"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:background="@drawable/your_msg_bubble_bg"
android:paddingLeft="10dp"
android:paddingBottom="10dp"
android:orientation="vertical">
<TextView
android:id="@+id/txtMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@android:color/black"
android:maxWidth="240dp" />
</LinearLayout>
</LinearLayout>
我为 date
texview 更新此代码:-
<TextView
android:id="@+id/txtDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp"
android:layout_gravity="right"
android:textSize="12sp"
android:textColor="@android:color/darker_gray" />
Vilen 回答正确。我只是在提高性能。我们可以在条件为真时以编程方式添加文本视图,而不是对每个视图都进行视图消失和可见。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/white"
android:orientation="vertical"
android:padding="10dp">
<LinearLayout
android:id="@+id/timeTextLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical" />
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/bg_gray_rounded"
android:paddingBottom="@dimen/padding_extra_small"
android:paddingLeft="@dimen/padding_small"
android:paddingRight="@dimen/padding_small"
android:paddingTop="@dimen/padding_extra_small"
android:textColor="@color/gray"
android:textSize="@dimen/font_size_md" />
</LinearLayout>
ChatAdapter.java
public class ChatEGRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
public static class TextViewHolder extends RecyclerView.ViewHolder {
public TextView textView;
public LinearLayout timeText;
public TextViewHolder(View v) {
super(v);
timeText = (LinearLayout) v.findViewById(R.id.timeText);
textView = (TextView) v.findViewById(R.id.textView);
}
}
private final List<Message> messages;
public ChatEGRecyclerAdapter(List<Message> messages) {
this.messages = messages;
}
// Create new views (invoked by the layout manager)
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item, parent, false);
return new TextViewHolder(v);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
final Message m = messages.get(position);
final Context context = viewHolder.itemView.getContext();
TextViewHolder holder = (TextViewHolder)viewHolder;
holder.textView.setVisibility(View.VISIBLE);
holder.textView.setText(m.getText());
//Group by Date
long previousTs = 0;
if(position >= 1){
Message previousMessage = messages.get(position-1);
previousTs = Long.parseLong(previousMessage.getSentTime());
}
Calendar cal1 = Calendar.getInstance();
Calendar cal2 = Calendar.getInstance();
cal1.setTimeInMillis(Long.parseLong(messages.get(position).getSentTime())*1000);
cal2.setTimeInMillis(previousTs*1000);
boolean sameDay = cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) &&
cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR);
if (!sameDay) {
TextView dateView = new TextView(context);
dateView.setText(messages.get(position).getSentTime());
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
params.gravity = Gravity.CENTER_HORIZONTAL;
dateView.setLayoutParams(params);
holder.timeText.addView(dateView);
}
}
@Override
public int getItemCount() {
return messages.size();
}
}
此外,格式化和检查每封邮件的时间也很昂贵。实际上,为了最大限度地减少此代码执行,我们可以在发送消息时比较时间戳。获取上一条消息的时间戳并与当前消息进行比较。如果大于 24 小时,则使用 isNewGroup:true 发送到服务器。然后填充适配器可以在没有时间比较的情况下完成,如下所示。
@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
final Message m = messages.get(position);
final Context context = viewHolder.itemView.getContext();
TextViewHolder holder = (TextViewHolder)viewHolder;
holder.textView.setVisibility(View.VISIBLE);
holder.textView.setText(m.getText());
if (messages.get(position).getIsNewGroup()) {
TextView dateView = new TextView(context);
dateView.setText(messages.get(position).getSentTime());
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
params.gravity = Gravity.CENTER_HORIZONTAL;
dateView.setLayoutParams(params);
holder.timeText.addView(dateView);
}
}
@Vilen Answer 帮我解决这个问题。
通过 Api 响应根据不同密钥中的日期和时间提供数据,因此在适配器中管理我很容易
Api 回复喜欢……
{
"status": "success",
"unread": "0",
"data": [
{
"from_id": "152000000",
"to_id": "15500545452",
"message": "do their Jami hit",
"profile_image": "1667822677.jpg",
"addedon": "8:34 am",
"groupby": "April 2, 2021"
}
],
"message": "Chat History fetch successfully."
}
在绑定ViewHolder
val dateGroupBy=""
if (position > 0) {
dateGroupBy = list.get(position - 1).groupby
}
if (holder is MassageReceiverViewHolder) {
holder.bind(data, dateGroupBy)
} else if (holder is MassageSenderViewHolder) {
holder.bind(data, dateGroupBy)
}
在两个 Viewholder 绑定函数中
if (dateGroupBy != data.groupby) {
binding.tvDateGroup.visibility = View.VISIBLE
binding.tvDateGroup.text = data.groupby
}
绑定很简单 xml。我已经通过 Bindig 完成了。
我想这个问题不需要在已经接受的解决方案上叠加另一个答案,但这可能会有所帮助。牢记 @Vilen 个建议我做到了:
-> 将日期字符串值推送到 API
中的每个 child
-> 比较适配器中每个人的日期 onBindViewHolder()
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int, model: Message) {
val isSameUser: Boolean =
if (position > 0) model.userId == getItem(position - 1).userId else false
val isSameDate: Boolean =
if (position > 0) model.date == getItem(position - 1).date else false
when (viewHolder) {
// Based on data I initiated val [viewHolder] with appropriate
// enum class member in [getItemViewType()]
EnumViewHolder.SenderViewHolder ->
(holder as SenderMessageViewHolder).bindData(model, isSameDate, isSameUser)
EnumViewHolder.MessageViewHolder ->
(holder as MessageViewHolder).bindData(model, isSameDate, isSameUser)
else -> false
}
}
...
....
inner class MessageViewHolder(private val binding: MessageBinding) : RecyclerView.ViewHolder(binding.root) {
fun bindData(item: Message,
isSameDate: Boolean,
isSameUser: Boolean
) {
binding.messageTextTextView.text = item.text
binding.messageTimeTextView.text = item.time
if (isSameDate) {
binding.messageDateTextView.visibility = View.GONE
} else binding.messageDateTextView.text = item.date
// Omit user profile picture in case of repeated message
if (isSameUser && isSameDate) {
binding.messageLinearLayout.setBackgroundResource(R.drawable.message_background)
binding.userImageImageView.visibility = View.INVISIBLE
} else {
// loading image URL to imageView
...
}
}
}
// I did the same for SenderMessageViewHolder()
这是我得到的结果:
Screenshot
我正在开发一个聊天应用程序,但我正在努力弄清楚如何在聊天消息的顶部显示日历日期;例如,像这样:
另一张带时间戳的图片:
如您在示例中所见,日期显示在新一批短信的顶部。我想做同样的事情,只是针对与特定日期相关的一批消息。假设我有 10 月 19 日的消息,它会在顶部显示 10 月 19 日,然后是 10 月 20 日的消息,依此类推...这是与我的类似的工作示例代码:
http://www.codeproject.com/Tips/897826/Designing-Android-Chat-Bubble-Chat-UI
代码的构造与我的相同,除了顶部显示的日期是我坚持的东西。我为每条消息显示了我的时间戳,我只想以 "Monday, October 19, 2015" 格式显示该批消息的日期;只有一次在顶部,从 10 月 19 日开始,以及过去和未来消息的日历日期,如图所示。有什么线索吗? 谢谢!
只需在该日期内创建一个消息视图组。或者我会推荐这样的结构:
ListView
- ListViewItem1
- (TextView with date)
- ListViewOfMsgsForThatDate
a) Message1ForThisDate
b) Message2ForThisDate
- ListViewItem2
- (TextView with date)
- ListViewOfMsgsForThatDate
a) Message1ForThisDate
b) Message2ForThisDate
好的;这是一个解决方案,可以为您提供构建的想法。它将解决您的日期问题,并为您提供有关如何解决时间显示方式的提示。这种程序涉及大量工作才能使其作为应用程序可行。您需要考虑时区、存储消息的位置等。
现在堆栈溢出的目的不是为人们设计程序,而是试图帮助引导人们朝着正确的方向发展。
为了回答您的问题;我介绍的是基础知识,我不会给你准备好的代码 运行,你需要做一些工作:)
这可以通过多种方式解决,我选择了以下方式:
使用共享首选项来存储最新的日期时间。我考虑过访问聊天消息数组,但认为这可能更简单。我选择了共享首选项,以便在关闭并重新打开应用程序时保留最后一条消息的日期。 (最终,聊天消息需要保留在数据库 [SQLite] 中,以便在应用程序关闭和打开时消息仍然存在)。请看最后我的评论。
public class ChatActivity extends ActionBarActivity {
public static final String MyPREF = "MyPrefs" ;
public static final String DATE_KEY = "DateKey" ;
SharedPreferences prefs;
SharedPreferences.Editor editor;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
initControls();
pref = getSharedPreferences(MyPREF, MODE_PRIVATE);
// Check that the shared preferences has a value
if(contains(DATE_KEY)){
editor = prefs.edit();
// get the current datetime.
editor.Long(DATE_KEY, date);
editor.commit();
// set the text of the textview android:id="@+id/date"
// with the current formatted date.
}
}
在你的onclick侦听器中,你测试最后存储的日期是今天的日期,如果不是,它被转换成聊天消息,所以它的值可以添加到数组并显示。您将不得不调整格式化这些消息类型的方式,您甚至可以为这些日期消息添加一个唯一 ID 并对此进行测试,或者测试没有 ID。有很多解决方案。
sendBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
/.../
}
ChatMessage chatMessage = new ChatMessage();
long LastDate = pref.getLong(DATE_KEY, date);
// create an empty chat message with only a date.
// check if the last used date is not today's date.
// if it's not today's date, save it and display it as
// an archived bubble.
if(!LastDate.isToday()){
ChatMessage dateholder = new ChatMessage();
// You'll need to format the date
dateholder.setDate(Formatted LastDate);
// set/change the text of the textview android:id="@+id/date"
// with the current formatted date.
}
// put the current date time into your preferences.
editor = prefs.edit();
editor.putString(DATE_KEY, date);
editor.commit();
/.../
// this is now setting up the new chat message.
chatMessage.setDate(DateFormat.getDateTimeInstance().format(new Date()));
/.../
displayMessage(chatMessage);
}
});
xml 好的,现在您不希望只有一条消息时日期浮动在屏幕顶部。所以将它与滚动列表分组,然后将其设置为相对于屏幕布局,因为列表本身就是这样。线性布局就可以做到这一点,只要确保它的方向设置为垂直即可。
将它们固定在一起。
<LinearLayout
android:layout_above="@+id/messageEdit"
android:layout_below="@+id/meLbl"
android:id="@+id/layout"
.../orentation:vertical ../
<TextView
android:id="@+id/date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
/..//>
<ListView
android:id="@+id/messagesContainer"
.../
...//>
</LinearLayout>
<TextView
android:id="@+id/meLbl"
/.../
/>
<TextView
android:id="@+id/friendLabel"
/.../
/>
更改每条消息的显示 您将需要停止显示每条消息的日期时间。如果您希望单独显示每条消息的时间,并在同一分钟内跳过这些消息,正如您在评论中提到的那样,那么我已经为您提供了框架,以找出如何测试这个以及程序逻辑是否是否显示。
您需要在您的:
中更改此设置public View getView(final int position, View convertView, ViewGroup parent) {
holder.txtMessage.setText(chatMessage.getMessage());
// TODO,
// do a check if(mins are the same don't post
// else post the format without the date)
// holder.txtInfo.setText(chatMessage.getDate());
不要忘记应用所有必要的导入。
我建议,如果您开始使用没有共享首选项的最后一次访问数据库,或者继续使用共享首选项。这变得更加复杂,因为要保存多个聊天记录,在这种情况下,最好使用数据库。
据我了解,您只想为特定的消息组而不是每条消息显示 time/date。所以这里是如何做到这一点。 前提条件:我假设每个消息项都有时间戳,我们将根据该时间戳进行分组 想法:我们需要每个列表项都有 timeview:TextView 元素,我们将根据它的位置和 TS(时间戳)显示和隐藏该元素 示例:
item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:orientation="vertical"
android:padding="@dimen/padding_small">
<TextView
android:id="@+id/timeText"
style="@style/DefaultText"
android:paddingBottom="@dimen/padding_small"
android:paddingRight="@dimen/padding_small"
android:paddingTop="@dimen/padding_small"
android:text="@string/hello_world"
android:textSize="@dimen/font_size_small_10"/>
<TextView
android:id="@+id/textView"
style="@style/DefaultText"
android:layout_width="wrap_content"
android:background="@drawable/bg_gray_rounded"
android:paddingBottom="@dimen/padding_extra_small"
android:paddingLeft="@dimen/padding_small"
android:paddingRight="@dimen/padding_small"
android:paddingTop="@dimen/padding_extra_small"
android:textColor="@color/gray"
android:textSize="@dimen/font_size_md"/>
</LinearLayout>
ChatRecyclerAdapter.java
public class ChatEGRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
public static class TextViewHolder extends RecyclerView.ViewHolder {
public TextView textView;
public TextView timeText;
public TextViewHolder(View v) {
super(v);
timeText = (TextView) v.findViewById(R.id.timeText);
textView = (TextView) v.findViewById(R.id.textView);
}
}
private final List<Message> messages;
public ChatEGRecyclerAdapter(List<Message> messages) {
this.messages = messages;
}
// Create new views (invoked by the layout manager)
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item, parent, false);
return new TextViewHolder(v);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
final Message m = messages.get(position);
final Context context = viewHolder.itemView.getContext();
TextViewHolder holder = (TextViewHolder)viewHolder;
holder.textView.setVisibility(View.VISIBLE);
holder.textView.setText(m.getText());
long previousTs = 0;
if(position>1){
Message pm = messages.get(position-1);
previousTs = pm.getTimeStamp();
}
setTimeTextVisibility(m.getTimeStamp(), previousTs, holder.timeText);
}
private void setTimeTextVisibility(long ts1, long ts2, TextView timeText){
if(ts2==0){
timeText.setVisibility(View.VISIBLE);
timeText.setText(Utils.formatDayTimeHtml(ts1));
}else {
Calendar cal1 = Calendar.getInstance();
Calendar cal2 = Calendar.getInstance();
cal1.setTimeInMillis(ts1);
cal2.setTimeInMillis(ts2);
boolean sameMonth = cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) &&
cal1.get(Calendar.MONTH) == cal2.get(Calendar.MONTH);
if(sameMonth){
timeText.setVisibility(View.GONE);
timeText.setText("");
}else {
timeText.setVisibility(View.VISIBLE);
timeText.setText(Utils.formatDayTimeHtml(ts2));
}
}
}
@Override
public int getItemCount() {
return messages.size();
}
}
剩下的就是创建您的 RecylcerView 并为其提供此适配器
使用此 xml 有助于根据您的 link - Designing Android Chat Bubble (Chat UI) 建议创建您正在寻找的布局。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:orientation="vertical">
<TextView
android:id="@+id/txtDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp"
android:layout_gravity="right"
android:textSize="12sp"
android:textColor="@android:color/darker_gray" />
<LinearLayout
android:id="@+id/contentWithBackground"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:background="@drawable/your_msg_bubble_bg"
android:paddingLeft="10dp"
android:paddingBottom="10dp"
android:orientation="vertical">
<TextView
android:id="@+id/txtMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@android:color/black"
android:maxWidth="240dp" />
</LinearLayout>
</LinearLayout>
我为 date
texview 更新此代码:-
<TextView
android:id="@+id/txtDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp"
android:layout_gravity="right"
android:textSize="12sp"
android:textColor="@android:color/darker_gray" />
Vilen 回答正确。我只是在提高性能。我们可以在条件为真时以编程方式添加文本视图,而不是对每个视图都进行视图消失和可见。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/white"
android:orientation="vertical"
android:padding="10dp">
<LinearLayout
android:id="@+id/timeTextLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical" />
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/bg_gray_rounded"
android:paddingBottom="@dimen/padding_extra_small"
android:paddingLeft="@dimen/padding_small"
android:paddingRight="@dimen/padding_small"
android:paddingTop="@dimen/padding_extra_small"
android:textColor="@color/gray"
android:textSize="@dimen/font_size_md" />
</LinearLayout>
ChatAdapter.java
public class ChatEGRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
public static class TextViewHolder extends RecyclerView.ViewHolder {
public TextView textView;
public LinearLayout timeText;
public TextViewHolder(View v) {
super(v);
timeText = (LinearLayout) v.findViewById(R.id.timeText);
textView = (TextView) v.findViewById(R.id.textView);
}
}
private final List<Message> messages;
public ChatEGRecyclerAdapter(List<Message> messages) {
this.messages = messages;
}
// Create new views (invoked by the layout manager)
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item, parent, false);
return new TextViewHolder(v);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
final Message m = messages.get(position);
final Context context = viewHolder.itemView.getContext();
TextViewHolder holder = (TextViewHolder)viewHolder;
holder.textView.setVisibility(View.VISIBLE);
holder.textView.setText(m.getText());
//Group by Date
long previousTs = 0;
if(position >= 1){
Message previousMessage = messages.get(position-1);
previousTs = Long.parseLong(previousMessage.getSentTime());
}
Calendar cal1 = Calendar.getInstance();
Calendar cal2 = Calendar.getInstance();
cal1.setTimeInMillis(Long.parseLong(messages.get(position).getSentTime())*1000);
cal2.setTimeInMillis(previousTs*1000);
boolean sameDay = cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) &&
cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR);
if (!sameDay) {
TextView dateView = new TextView(context);
dateView.setText(messages.get(position).getSentTime());
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
params.gravity = Gravity.CENTER_HORIZONTAL;
dateView.setLayoutParams(params);
holder.timeText.addView(dateView);
}
}
@Override
public int getItemCount() {
return messages.size();
}
}
此外,格式化和检查每封邮件的时间也很昂贵。实际上,为了最大限度地减少此代码执行,我们可以在发送消息时比较时间戳。获取上一条消息的时间戳并与当前消息进行比较。如果大于 24 小时,则使用 isNewGroup:true 发送到服务器。然后填充适配器可以在没有时间比较的情况下完成,如下所示。
@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
final Message m = messages.get(position);
final Context context = viewHolder.itemView.getContext();
TextViewHolder holder = (TextViewHolder)viewHolder;
holder.textView.setVisibility(View.VISIBLE);
holder.textView.setText(m.getText());
if (messages.get(position).getIsNewGroup()) {
TextView dateView = new TextView(context);
dateView.setText(messages.get(position).getSentTime());
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
params.gravity = Gravity.CENTER_HORIZONTAL;
dateView.setLayoutParams(params);
holder.timeText.addView(dateView);
}
}
@Vilen Answer 帮我解决这个问题。
通过 Api 响应根据不同密钥中的日期和时间提供数据,因此在适配器中管理我很容易
Api 回复喜欢……
{
"status": "success",
"unread": "0",
"data": [
{
"from_id": "152000000",
"to_id": "15500545452",
"message": "do their Jami hit",
"profile_image": "1667822677.jpg",
"addedon": "8:34 am",
"groupby": "April 2, 2021"
}
],
"message": "Chat History fetch successfully."
}
在绑定ViewHolder
val dateGroupBy=""
if (position > 0) {
dateGroupBy = list.get(position - 1).groupby
}
if (holder is MassageReceiverViewHolder) {
holder.bind(data, dateGroupBy)
} else if (holder is MassageSenderViewHolder) {
holder.bind(data, dateGroupBy)
}
在两个 Viewholder 绑定函数中
if (dateGroupBy != data.groupby) {
binding.tvDateGroup.visibility = View.VISIBLE
binding.tvDateGroup.text = data.groupby
}
绑定很简单 xml。我已经通过 Bindig 完成了。
我想这个问题不需要在已经接受的解决方案上叠加另一个答案,但这可能会有所帮助。牢记 @Vilen 个建议我做到了:
-> 将日期字符串值推送到 API
中的每个 child-> 比较适配器中每个人的日期 onBindViewHolder()
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int, model: Message) {
val isSameUser: Boolean =
if (position > 0) model.userId == getItem(position - 1).userId else false
val isSameDate: Boolean =
if (position > 0) model.date == getItem(position - 1).date else false
when (viewHolder) {
// Based on data I initiated val [viewHolder] with appropriate
// enum class member in [getItemViewType()]
EnumViewHolder.SenderViewHolder ->
(holder as SenderMessageViewHolder).bindData(model, isSameDate, isSameUser)
EnumViewHolder.MessageViewHolder ->
(holder as MessageViewHolder).bindData(model, isSameDate, isSameUser)
else -> false
}
}
...
....
inner class MessageViewHolder(private val binding: MessageBinding) : RecyclerView.ViewHolder(binding.root) {
fun bindData(item: Message,
isSameDate: Boolean,
isSameUser: Boolean
) {
binding.messageTextTextView.text = item.text
binding.messageTimeTextView.text = item.time
if (isSameDate) {
binding.messageDateTextView.visibility = View.GONE
} else binding.messageDateTextView.text = item.date
// Omit user profile picture in case of repeated message
if (isSameUser && isSameDate) {
binding.messageLinearLayout.setBackgroundResource(R.drawable.message_background)
binding.userImageImageView.visibility = View.INVISIBLE
} else {
// loading image URL to imageView
...
}
}
}
// I did the same for SenderMessageViewHolder()
这是我得到的结果: Screenshot