【问题标题】:Delete a message from the RecyclerView从 RecyclerView 中删除消息
【发布时间】:2021-07-04 22:27:17
【问题描述】:

我有一个发送消息的应用程序。

我想实现删除消息的功能。如果您长时间单击该消息,则会出现一个标记的复选框。接下来,用户可以选择他要删除的所有消息,然后单击垃圾桶图标。

消息是使用 RecyclerView 实现的。使用 LiveData 和 Room。

这是ListAdapter 的样子:

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.R;
import com.mardaunt.telesupp.room.Message;
import com.mardaunt.telesupp.room.TimeStampConverter;

public class MessageListAdapter extends ListAdapter<Message, MessageViewHolder> {

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

    @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;
    }

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

    @Override
    public void onBindViewHolder(MessageViewHolder holder, int position) {
        Message current = getItem(position);

        holder.bind(current.getPhone(),
                    current.getText(),
                    TimeStampConverter.getTime(current.getDate())); // Бинтим телефон сообщение и время.
    }

    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());
        }
    }
}

ViewHolder 是这样的:

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;

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

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

    public void bind(String phone, String message, String time) {
        phoneItemView.setText(phone);
        messageItemView.setText(message);
        timeItemView.setText(time);
    }

        //viewType содержит id для нужного layout.
    static MessageViewHolder create(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                    .inflate(viewType, parent, false);
        return new MessageViewHolder(view);
    }
}

请告诉我如何正确启动此任务?如果我认为正确的话,我需要在 MessageListAdapter 类中设置一个长按消息的侦听器。

MainActivity 在 GitHub 上。

应用程序的工作原型 (apk file)。

【问题讨论】:

    标签: java android android-recyclerview android-room android-livedata


    【解决方案1】:

    你可能想要一个像MessageView这样的中间对象

        //this will allow each message to be selected individually.
        //the adapter should display MessageViews instead of Messages directly.
        public class MessageView{
            private Message message;
            private boolean isSelected;
            //constructor and getters and setters;
        }
        //set your view holder like this
        class MessageViewHolder extends RecyclerView.ViewHolder {
            private final TextView phoneItemView;
            private final TextView messageItemView;
            private final TextView timeItemView;
            private final Checkbox checkBoxView;
        
            private MessageViewHolder(View itemView) {
                super(itemView);
                messageItemView = itemView.findViewById(R.id.text_view_message);
                phoneItemView = itemView.findViewById(R.id.text_view_phone);
                timeItemView = itemView.findViewById(R.id.text_view_time);
                checkBoxView= itemView.findViewById(R.id.check_box_view);
                checkboxView.setOnCheckedChangeListener((checkbox,checked)=>{
                     messageList.get(getAdapterPosition()).isSelected(checked);
                });
            }
            public void bind(MessageView messageView) {
                 phoneItemView.setText(messageView.getMessage().getPhone());
                 messageItemView.setText(messageView.getMessage().getMessage());
                 timeItemView.setText(TimeStampConverter.getTime(messageView.getMessage().getDate()));
                 checkBoxView.setChecked(messageView.isSelected());
            }
        }  
        //call this on the select all check box in your activity/fragment
        public void onSelectAll(boolean selected){
            for(MessageView view : messageList)
                 view.isSelected(selected);
            adapter.notifyDataSetChanged();
        }
        //call this on the delete all button click in your activity/fragment
        public void onDeleteSelected(){
           Iterator i = messageList.iterator();
           MessageView messageView;
           while (i.hasNext()) {
              messageView = (MessageView ) i.next();
              if (messageView.isSelected())
                i.remove();
           }
           adapter.notifyDataSetChanged();   
        }
    

    【讨论】:

    • 感谢您对问题进行如此详细的研究。我对这种方法思考了很长时间,但考虑到我的项目中存在 LiveData 对我来说很重要。我试图调整你的方法。下面我抛开发生的事情。也许会有cmets。
    • 你说得对,我忘了你用的是LiveData。对于MessageView,我很确定您可以将Message 实体作为Pojo 自动拉入其中,pojo 自动成为MessageView。对于onDeleteSelected,您应该编译所有选择的列表并将批量删除发送到数据库。
    【解决方案2】:

    我一直在思考可以做些什么。 因此,我决定尝试为 RecyclerView 中的每个元素添加复选框,并在 ListAdapter 类中设置监听器。

    public class MessageListAdapter extends ListAdapter<Message, MessageViewHolder> {
    
        HelperAdapter helperAdapter;
    
        public MessageListAdapter(@NonNull DiffUtil.ItemCallback<Message> diffCallback, HelperAdapter helperAdapter) {
            super(diffCallback);
            this.helperAdapter = helperAdapter;
        }
    
        @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;
        }
    
        @Override
        public MessageViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            return MessageViewHolder.create(parent, viewType);
        }
    
        @Override
        public void onBindViewHolder(MessageViewHolder holder, int position) {
            Message current = getItem(position);
    
            holder.bind(current.getPhone(),
                        current.getText(),
                        TimeStampConverter.getTime(current.getDate())); // Бинтим телефон сообщение и время
    
                // Добавим все view CheckBox в list чтобы скрывать и показывать их по мере необходимости
            helperAdapter.addCheckBox(holder.getMessageCheckBox());
    
                // Установим слушатель короткого нажатия по сообщению.
            holder.getMessageBox().setOnClickListener(v -> {
                EditText editPhone = helperAdapter.getMainActivity()
                                        .findViewById(R.id.edit_phone);
                editPhone.setText(current.getPhone());
            });
    
                // Установим слушатель долгого нажатия по сообщению.
            holder.getMessageBox().setOnLongClickListener(v -> {
                helperAdapter.getMainActivity()
                             .findViewById(R.id.trash_button)
                             .setVisibility(View.VISIBLE);
                helperAdapter.getMainActivity()
                        .findViewById(R.id.close_button)
                        .setVisibility(View.VISIBLE);
    
                for (CheckBox checkBox: helperAdapter.getSetCheckBox())
                    checkBox.setVisibility(View.VISIBLE);
                return false;
            });
                // Установим слушатели для чекбоксов, которые переключают переменную isSelected
            holder.getMessageCheckBox().setOnClickListener(v -> {
                current.isSelected = ((CheckBox) v).isChecked();
    
                helperAdapter.addCheckBox((CheckBox) v);
            });
        }
    
        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());
            }
        }
    }
    

    我还创建了一个辅助类 HelperAdapter 来通过它删除标记的消息。

    public class HelperAdapter {
    
        private static HelperAdapter helperAdapter;
        private MessageViewModel mMessageViewModel;
        private MainActivity mainActivity;
        private final HashSet<CheckBox> listCheckBox = new HashSet<>();
    
        public void deleteSelected() {
            LiveData<List<Message>> temp = mMessageViewModel.getAllMessages();
            List<Message> allMessages = temp.getValue();
    
            if (allMessages != null)
            for(Message message: allMessages)
                if(message.isSelected)
                    mMessageViewModel.delete(message);
    
            // Скроем иконку корзины и чекбоксы
            mainActivity.findViewById(R.id.trash_button).setVisibility(View.GONE);
            mainActivity.findViewById(R.id.close_button).setVisibility(View.GONE);
            for (CheckBox checkBox: listCheckBox)
                checkBox.setVisibility(View.GONE);
        }
    
        public void setMessageViewModel(MessageViewModel messageViewModel){
            mMessageViewModel = messageViewModel;
        }
    
        public MessageViewModel getMessageViewModel() { return mMessageViewModel; }
    
        public HashSet<CheckBox> getSetCheckBox() {return listCheckBox;}
    
        public void addCheckBox(CheckBox checkBox) {
            listCheckBox.add(checkBox);
        }
    
        public void removeCheckBox(CheckBox checkBox){
            listCheckBox.remove(checkBox);
        }
    
        public int sizeCheckBox() {return listCheckBox.size();}
    
        public static HelperAdapter getHelperAdapter(){
            if (helperAdapter == null) helperAdapter = new HelperAdapter();
            return helperAdapter;
        }
    
        public void setMainActivity(MainActivity mainActivity){
            this.mainActivity = mainActivity;
        }
    
        public MainActivity getMainActivity() { return mainActivity; }
    }
    

    我还为消息类添加了一个布尔变量:

    @Entity(tableName = "messages_table")
    public class Message {
    
        @PrimaryKey(autoGenerate = true)
        private int id;
        private String phone;
        private String text;
        private String nature;
        private String service;
        private Date date;
    
        @Ignore
        public boolean isSelected;   // I added it
    
        public Message(int id,
                       @NonNull String phone,
                       @NonNull String text,
                       @NonNull String nature,
                       @NonNull String service,
                       @NonNull Date date
                        ) {
            this.id = id;
            this.phone = phone;
            this.text = text;
            this.nature = nature;
            this.service = service;
            this.date = date;
        }
    
        public int getId(){return this.id;}
        public String getPhone(){return this.phone;}
        public String getText(){return this.text;}
        public String getNature(){return this.nature;}
        public String getService(){return this.service;}
        public Date getDate(){return this.date;}
    }
    

    现在我可以为垃圾桶图标设置监听器了:

    // ОнКлик для иконки корзины
        public void deleteMessage(View view){
            helperAdapter.deleteSelected();
        }
    

    感谢 LiveData,我不必考虑更新 RecyclerView。 LiveData 删除已删除的消息。

    这工作或多或少稳定,但我感觉我搞砸了架构并选择了错误的方法。

    测试 apk 文件:https://dropmefiles.com/UxPPx

    【讨论】:

      猜你喜欢
      • 2021-06-12
      • 1970-01-01
      • 2011-12-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-01
      • 2011-03-21
      相关资源
      最近更新 更多