如何为传入和传出的聊天消息使用不同的布局

Posted

技术标签:

【中文标题】如何为传入和传出的聊天消息使用不同的布局【英文标题】:How to use different layouts for incoming & outgoing chat messages 【发布时间】:2021-09-11 03:40:22 【问题描述】:

我有一个消息设计。 2个xml文件负责设计:

recyclerview_item_incoming.xml
recyclerview_item_outgoing.xml

看起来像这样:

消息存储在RecyclerView 中。 RecyclerView 通过 LiveData -> Room 连接到 SQLite。 More Info

要使用 RecyclerView,我有 2 个类:

MessageListAdapter:

package com.mardaunt.telesupp.recyclerview;

import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;

import com.mardaunt.telesupp.room.Message;

public class MessageListAdapter extends ListAdapter<Message, MessageViewHolder> 

    public static int idMessage = 0; // I added that
    public static int countMessages = 0; // I added that

    public MessageListAdapter(@NonNull DiffUtil.ItemCallback<Message> diffCallback) 
        super(diffCallback);
    

    @Override
    public MessageViewHolder onCreateViewHolder(ViewGroup parent, int viewType) 
        countMessages = getItemCount();
        //System.out.println(countMessages + " - This countMessages\n" + idMessage + "- This idMessage; " + viewType + " - viewType");
        System.out.println(idMessage);
        //if(idMessage >= countMessages) idMessage = 0;
        // I need to pass the create index method of the current message.!!!!!!!!!!
        // The getItem() function returns a Message object.
        return MessageViewHolder.create(parent, getItem(idMessage));
    

    @Override
    public void onBindViewHolder(MessageViewHolder holder, int position) 
        Message current = getItem(position);
        //System.out.println(current.getId() + " " + current.getPhone() + " " + current.getText());
        holder.bind(current.getPhone() ,current.getText()); // Бинтим только тело телефон и сообщение!
        idMessage = position+1;
        if(idMessage >= countMessages) idMessage = 0;
    

    public static class MessageDiff extends DiffUtil.ItemCallback<Message> 

        @Override
        public boolean areItemsTheSame(@NonNull Message oldItem, @NonNull Message newItem) 
            return oldItem == newItem;
        

        @Override
        public boolean areContentsTheSame(@NonNull Message oldItem, @NonNull Message newItem) 
            return oldItem.getText().equals(newItem.getText());
        
    

MessageViewHolder:

package com.mardaunt.telesupp.recyclerview;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import androidx.recyclerview.widget.RecyclerView;

import com.mardaunt.telesupp.R;
import com.mardaunt.telesupp.room.Message;

class MessageViewHolder extends RecyclerView.ViewHolder 
    private final TextView phoneItemView;
    private final TextView messageItemView;

    private MessageViewHolder(View itemView) 
        super(itemView);
        messageItemView = itemView.findViewById(R.id.text_view_message);
        phoneItemView = itemView.findViewById(R.id.text_view_phone);
    

    public void bind(String phone, String message) 
        phoneItemView.setText(phone);
        messageItemView.setText(message);
    

        //The method decides which design to choose for the message bubble.
    static MessageViewHolder create(ViewGroup parent, Message current) 
        View view;
        if (current.getNature().equals("outgoing"))
            view = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.recyclerview_item_outgoing, parent, false);
        else view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.recyclerview_item_incoming, parent, false);
        return new MessageViewHolder(view);
    

消息:

package com.mardaunt.telesupp.room;

import androidx.annotation.NonNull;
import androidx.room.Entity;
import androidx.room.PrimaryKey;

@Entity(tableName = "messages_table")
public class Message 

    @PrimaryKey(autoGenerate = true)
    private int id;
    private String phone;
    private String text;
    private String nature;

    public Message(int id,
                   @NonNull String phone,
                   @NonNull String text,
                   @NonNull String nature
                    ) 
        this.id = id;
        this.phone = phone;
        this.text = text;
        this.nature = nature; // incoming OR outgoing
    

    public int getId()return this.id;
    public String getPhone()return this.phone;
    public String getText()return this.text;
    public String getNature()return this.nature;

问题: 我希望传入的消息位于左侧。传出消息位于右侧。 为此,我稍微更改了MessageViewHolder 类的静态方法:

 //The method decides which design to choose for the message bubble.
static MessageViewHolder create(ViewGroup parent, Message current) 
    View view;
    if (current.getNature().equals("outgoing"))
        view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.recyclerview_item_outgoing, parent, false);
    else view = LayoutInflater.from(parent.getContext())
            .inflate(R.layout.recyclerview_item_incoming, parent, false);
    return new MessageViewHolder(view);

但问题是我不知道如何正确地将 Message 对象传递给此方法? 如您所见,我尝试在MessageListAdapter 类中传递Message 对象:

@Override
public MessageViewHolder onCreateViewHolder(ViewGroup parent, int viewType) 
    countMessages = getItemCount();
    // I need to pass the create index method of the current message.!!!!!!!!!!
    // The getItem() function returns a Message object.
    return MessageViewHolder.create(parent, getItem(idMessage));


@Override
public void onBindViewHolder(MessageViewHolder holder, int position) 
    Message current = getItem(position);
    holder.bind(current.getPhone() ,current.getText()); // Бинтим только тело телефон и сообщение!
    idMessage = position+1;
    if(idMessage >= countMessages) idMessage = 0;

我添加了 2 个静态变量(idMessage、countMessages),但这不能正常工作。 如何为方法 MessageViewHolder.create(...) 添加 Message 对象?

GitHub 上的项目:https://github.com/MinorityMeaning/HelloApp

【问题讨论】:

所以标题与问题本身无关。 我认为您需要使用两个不同的视图来更改您的方法并覆盖适配器中的 getViewType 方法。您可以在此链接中查看和示例medium.com/talking-android/… @javdromero,好的。我会考虑主题的名称。 @ManuelMato 研究问题的好文章。我帮助理解了能够使用 viewType 的必要性。 【参考方案1】:

getItemViewType() 是确定项目布局的正确位置。所以你需要把逻辑移到那里,所以在适配器中覆盖它:

@Override
public int getItemViewType(int position) 
    if (getItem(position).getNature().equals("outgoing"))
        return R.layout.recyclerview_item_outgoing;
    else
        return R.layout.recyclerview_item_incoming;

这反映在onCreateViewHolder()viewType 参数中,因此它拥有当前项目的正确布局。因此,您可以将其传递给 ViewHolder:

@Override
public MessageViewHolder onCreateViewHolder(ViewGroup parent, int viewType) 
    return MessageViewHolder.create(parent, viewType);

在 ViewHolder 中将其设置为您的布局:

static MessageViewHolder create(ViewGroup parent, int viewType) 
    View view = LayoutInflater.from(parent.getContext())
            .inflate(viewType, parent, false);
    return new MessageViewHolder(view);

【讨论】:

我花了很多时间尝试使用静态变量。您已经非常准确地看到并解决了我的问题。今晚我会睡得很香。我会将您的昵称添加到我的应用程序中。非常感谢您的宝贵时间! 欢迎 @Mardaunt.. 很高兴能帮上忙.. 这让我很荣幸.. 编码愉快? 你认为我可以如何重命名这个问题? 是的,我愿意。如何对传入和传出的聊天消息使用不同的布局可能会更好。或者你看得更清楚的东西

以上是关于如何为传入和传出的聊天消息使用不同的布局的主要内容,如果未能解决你的问题,请参考以下文章

如何为特定网络接口创建传出套接字?

自定义 TableViewCells 在重新加载 tableview 时闪烁

如何为不同的键盘布局使用默认快捷键?

如何为不同的屏幕尺寸创建具有自动布局的比例视图?

如何为两种景观定义不同的布局?

如何为不同设备自动布局应用程序。