【发布时间】:2020-09-12 14:38:59
【问题描述】:
我的应用中有一个包含消息的 RecyclerView。在向右滑动时,我将其配置为删除消息,在向左滑动时,将其存档。下面是删除和归档相关的ItemTouchHelper部分的代码:
if (direction == ItemTouchHelper.LEFT) {
donorMessagesAdapter.deleteMessages(position);
} else if (direction == ItemTouchHelper.RIGHT) {
donorMessagesAdapter.archiveMessages(position);
}
这里是 deleteMessages() 和 archiveMessages() 函数(它们几乎相同):
public void deleteMessages(final int position) {
recentlyDeletedPeer = sortedPeers.get(position);
recentlyDeletedPeerPosition = position;
sortedPeers.remove(position);
notifyItemRemoved(position);
Snackbar snackbar = Snackbar.make(activity.findViewById(R.id.constraint_layout),
"Messages Deleted", Snackbar.LENGTH_LONG);
snackbar.setAction("UNDO", new View.OnClickListener() {
@Override
public void onClick(View view) {
sortedPeers.add(recentlyDeletedPeerPosition, recentlyDeletedPeer);
notifyItemInserted(recentlyDeletedPeerPosition);
}
});
snackbar.setActionTextColor(0xff0099ff);
snackbar.addCallback(new Snackbar.Callback() {
@Override
public void onDismissed(Snackbar snackbar, int event) {
if (event == Snackbar.Callback.DISMISS_EVENT_TIMEOUT || event == Snackbar.Callback.DISMISS_EVENT_CONSECUTIVE) {
MainActivity.database.receivedMessagesDao().deleteAllReceivedMessagesFromSender(email, recentlyDeletedPeer);
MainActivity.database.sentMessagesDao().deleteAllSentMessagesFromReceiver(email, recentlyDeletedPeer);
}
}
});
snackbar.show();
}
public void archiveMessages(final int position) {
recentlyArchivedPeer = sortedPeers.get(position);
recentlyArchivedPeerPosition = position;
sortedPeers.remove(position);
notifyItemRemoved(position);
Snackbar snackbar = Snackbar.make(activity.findViewById(R.id.constraint_layout),
"Messages archived", Snackbar.LENGTH_LONG);
snackbar.setAction("UNDO", new View.OnClickListener() {
@Override
public void onClick(View view) {
sortedPeers.add(recentlyArchivedPeerPosition, recentlyArchivedPeer);
notifyItemInserted(recentlyArchivedPeerPosition);
}
});
snackbar.setActionTextColor(0xff0099ff);
snackbar.addCallback(new Snackbar.Callback() {
@Override
public void onDismissed(Snackbar snackbar, int event) {
if (event == Snackbar.Callback.DISMISS_EVENT_TIMEOUT || event == Snackbar.Callback.DISMISS_EVENT_CONSECUTIVE) {
MainActivity.database.receivedMessagesDao().archive(email, recentlyArchivedPeer);
MainActivity.database.sentMessagesDao().archive(email, recentlyArchivedPeer);
}
}
});
snackbar.show();
}
删除/归档一条消息时一切正常。在 Snackbar 消失后,滑动会删除或存档消息,当我单击撤消时,删除/存档会反转。删除/归档多条消息时会出现问题。
问题 1: 当我删除/归档多条消息而不等待每条消息的 Snackbar 消失时,除了第一条消息之外的所有消息都被删除/归档。让我感到奇怪的是,并不是只有最后一条消息被删除/归档,除了第一条消息之外的所有消息都被删除/归档。
问题2:当我删除/归档多条消息而不等待每条消息的Snackbar消失时,如果我按UNDO,即在Snackbar上删除最后一条消息/存档,应用程序崩溃。
顺便说一句,我正在使用 Room Library 将消息存储在数据库中(由 MainActivity.database 引用)。
我认为问题出在 Snackbar 的 addCallback() 函数中。我应该为event == Snackbar.Callback.DISMISS_EVENT_CONSECUTIVE 和event == Snackbar.Callback.DISMISS_EVENT_TIMEOUT 做一些不同的事情,但我不知道我应该做些什么不同的事情,以及究竟是什么导致了问题。
这是我的整个适配器类供参考(错误消息行旁边标有注释):
package com.example.treeapp;
import android.app.Activity;
import android.content.Intent;
import android.os.Handler;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.snackbar.Snackbar;
import com.mikhaellopez.circularimageview.CircularImageView;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
public class DonorMessagesAdapter extends RecyclerView.Adapter<DonorMessagesAdapter.DonorMessagesViewHolder> { // line 24 in error message
private Activity activity;
public String email;
public List<String> sortedPeers;
public static CircularImageView imageView;
private String recentlyDeletedPeer;
private int recentlyDeletedPeerPosition;
private String recentlyArchivedPeer;
private int recentlyArchivedPeerPosition;
public static class DonorMessagesViewHolder extends RecyclerView.ViewHolder {
public Button middleButton;
public TextView nameTextView;
public TextView messageTextView;
DonorMessagesViewHolder(View view) {
super(view);
middleButton = view.findViewById(R.id.middle_button);
imageView = view.findViewById(R.id.image_view);
nameTextView = view.findViewById(R.id.text_view_name);
messageTextView = view.findViewById(R.id.text_view_message);
}
}
DonorMessagesAdapter(Activity activity, String email) {
this.activity = activity;
this.email = email;
this.sortedPeers = getSortedPeers(email);
}
@NonNull
@Override
public DonorMessagesViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.message, parent, false);
return new DonorMessagesViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull DonorMessagesViewHolder holder, int position) {
final String current = sortedPeers.get(position);
// Start of getting last message
String lastMessage;
String lastMessageType;
List<Integer> receivedMessages = MainActivity.database.receivedMessagesDao().getMessagesToReceiverFromSender(email, current, 0);
List<Integer> sentMessages = MainActivity.database.sentMessagesDao().getMessagesBySenderToReceiver(email, current, 0);
if (sentMessages.size() == 0) {
lastMessage = MainActivity.database.receivedMessagesDao().getMessage(receivedMessages.get(receivedMessages.size() - 1)); // line 74 in error message
lastMessageType = "received";
if (MainActivity.database.receivedMessagesDao().getImage(receivedMessages.get(receivedMessages.size() - 1)) == 1) {
lastMessageType = "receivedImage";
}
} else if (receivedMessages.size() == 0) {
lastMessage = MainActivity.database.sentMessagesDao().getMessage(sentMessages.get(sentMessages.size() - 1));
lastMessageType = "sent";
if (MainActivity.database.sentMessagesDao().getImage(sentMessages.get(sentMessages.size() - 1)) == 1) {
lastMessageType = "sentImage";
}
} else {
LocalDateTime lastReceivedMessageTime = LocalDateTime.parse(MainActivity.database.receivedMessagesDao().getTime(receivedMessages.get(receivedMessages.size() - 1)));
LocalDateTime lastSentMessageTime = LocalDateTime.parse(MainActivity.database.sentMessagesDao().getTime(sentMessages.get(sentMessages.size() - 1)));
if (lastReceivedMessageTime.isAfter(lastSentMessageTime)) {
lastMessage = MainActivity.database.receivedMessagesDao().getMessage(receivedMessages.get(receivedMessages.size() - 1));
lastMessageType = "received";
if (MainActivity.database.receivedMessagesDao().getImage(receivedMessages.get(receivedMessages.size() - 1)) == 1) {
lastMessageType = "receivedImage";
}
} else {
lastMessage = MainActivity.database.sentMessagesDao().getMessage(sentMessages.get(sentMessages.size() - 1));
lastMessageType = "sent";
if (MainActivity.database.sentMessagesDao().getImage(sentMessages.get(sentMessages.size() - 1)) == 1) {
lastMessageType = "sentImage";
}
}
}
// End of getting last message
if (MainActivity.database.plantersDao().checkEmail(current) == 1) {
if (MainActivity.database.plantersDao().getImageUrl(current) != null) {
new DownloadImageTask(imageView).execute(MainActivity.database.plantersDao().getImageUrl(current));
}
} else if (MainActivity.database.donorsDao().checkEmail(current) == 1) {
if (MainActivity.database.donorsDao().getImageUrl(current) != null) {
new DownloadImageTask(imageView).execute(MainActivity.database.donorsDao().getImageUrl(current));
}
}
if (MainActivity.database.plantersDao().checkEmail(current) == 1) {
holder.nameTextView.setText(MainActivity.database.plantersDao().getName(current));
} else if (MainActivity.database.donorsDao().checkEmail(current) == 1) {
holder.nameTextView.setText(MainActivity.database.donorsDao().getName(current));
}
if (lastMessageType.equals("received")) {
if (lastMessage.length() > 17) {
holder.messageTextView.setText(lastMessage.substring(0, 16) + "...");
} else {
holder.messageTextView.setText(lastMessage);
}
} else if (lastMessageType.equals("sent")) {
if (lastMessage.length() > 12) {
holder.messageTextView.setText("You: " + lastMessage.substring(0, 11) + "...");
} else {
holder.messageTextView.setText("You: " + lastMessage);
}
} else if (lastMessageType.equals("receivedImage")) {
holder.messageTextView.setText("Image");
} else if (lastMessageType.equals("sentImage")) {
holder.messageTextView.setText("You: " + "Image");
}
holder.middleButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(view.getContext(), MessageActivity.class);
intent.putExtra("email", email);
intent.putExtra("peerEmail", current);
intent.putExtra("archived", 0);
view.getContext().startActivity(intent);
}
});
}
@Override
public int getItemCount() {
return sortedPeers.size();
}
public void reload() {
sortedPeers = getSortedPeers(email);
notifyDataSetChanged();
}
private List<String> getSortedPeers(String email) {
List<String> senders = MainActivity.database.receivedMessagesDao().getSendersForReceiver(email, 0);
List<String> receivers = MainActivity.database.sentMessagesDao().getReceiversForSender(email, 0);
List<String> peers = new ArrayList<>();
peers.addAll(senders);
peers.addAll(receivers);
peers = new ArrayList<>(new HashSet<>(peers));
List<String> sortedPeers = new ArrayList<>();
while (peers.size() > 0) {
LocalDateTime latestMessageTime = null;
String latestPeer = null;
for (String peer : peers) {
List<Integer> receivedMessages = MainActivity.database.receivedMessagesDao().getMessagesToReceiverFromSender(email, peer, 0);
List<Integer> sentMessages = MainActivity.database.sentMessagesDao().getMessagesBySenderToReceiver(email, peer, 0);
LocalDateTime lastMessageTime;
if (sentMessages.size() == 0) {
lastMessageTime = LocalDateTime.parse(MainActivity.database.receivedMessagesDao().getTime(receivedMessages.get(receivedMessages.size() - 1)));
} else if (receivedMessages.size() == 0) {
lastMessageTime = LocalDateTime.parse(MainActivity.database.sentMessagesDao().getTime(sentMessages.get(sentMessages.size() - 1)));
} else {
LocalDateTime lastReceivedMessageTime = LocalDateTime.parse(MainActivity.database.receivedMessagesDao().getTime(receivedMessages.get(receivedMessages.size() - 1)));
LocalDateTime lastSentMessageTime = LocalDateTime.parse(MainActivity.database.sentMessagesDao().getTime(sentMessages.get(sentMessages.size() - 1)));
if (lastReceivedMessageTime.isAfter(lastSentMessageTime)) {
lastMessageTime = lastReceivedMessageTime;
} else {
lastMessageTime = lastSentMessageTime;
}
}
if (latestMessageTime != null) {
if (lastMessageTime.isAfter(latestMessageTime)) {
latestMessageTime = lastMessageTime;
latestPeer = peer;
}
} else {
latestMessageTime = lastMessageTime;
latestPeer = peer;
}
}
sortedPeers.add(latestPeer);
peers.remove(latestPeer);
}
return sortedPeers;
}
public void deleteMessages(final int position) {
recentlyDeletedPeer = sortedPeers.get(position);
recentlyDeletedPeerPosition = position;
sortedPeers.remove(position);
notifyItemRemoved(position);
Snackbar snackbar = Snackbar.make(activity.findViewById(R.id.constraint_layout),
"Messages Deleted", Snackbar.LENGTH_LONG);
snackbar.setAction("UNDO", new View.OnClickListener() {
@Override
public void onClick(View view) {
sortedPeers.add(recentlyDeletedPeerPosition, recentlyDeletedPeer);
notifyItemInserted(recentlyDeletedPeerPosition);
}
});
snackbar.setActionTextColor(0xff0099ff);
snackbar.addCallback(new Snackbar.Callback() {
@Override
public void onDismissed(Snackbar snackbar, int event) {
if (event == Snackbar.Callback.DISMISS_EVENT_TIMEOUT || event == Snackbar.Callback.DISMISS_EVENT_CONSECUTIVE) {
MainActivity.database.receivedMessagesDao().deleteAllReceivedMessagesFromSender(email, recentlyDeletedPeer);
MainActivity.database.sentMessagesDao().deleteAllSentMessagesFromReceiver(email, recentlyDeletedPeer);
}
}
});
snackbar.show();
}
public void archiveMessages(final int position) {
recentlyArchivedPeer = sortedPeers.get(position);
recentlyArchivedPeerPosition = position;
sortedPeers.remove(position);
notifyItemRemoved(position);
Snackbar snackbar = Snackbar.make(activity.findViewById(R.id.constraint_layout),
"Messages archived", Snackbar.LENGTH_LONG);
snackbar.setAction("UNDO", new View.OnClickListener() {
@Override
public void onClick(View view) {
sortedPeers.add(recentlyArchivedPeerPosition, recentlyArchivedPeer);
notifyItemInserted(recentlyArchivedPeerPosition);
}
});
snackbar.setActionTextColor(0xff0099ff);
snackbar.addCallback(new Snackbar.Callback() {
@Override
public void onDismissed(Snackbar snackbar, int event) {
if (event == Snackbar.Callback.DISMISS_EVENT_TIMEOUT || event == Snackbar.Callback.DISMISS_EVENT_CONSECUTIVE) {
MainActivity.database.receivedMessagesDao().archive(email, recentlyArchivedPeer);
MainActivity.database.sentMessagesDao().archive(email, recentlyArchivedPeer);
reload();
}
}
});
snackbar.show();
}
}
这是我得到的错误(我的类标有**):
2020-09-12 19:24:11.547 689-689/com.example.treeapp E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.treeapp, PID: 689
java.lang.ArrayIndexOutOfBoundsException: length=0; index=-1
at java.util.ArrayList.get(ArrayList.java:439)
**at com.example.treeapp.DonorMessagesAdapter.onBindViewHolder(DonorMessagesAdapter.java:74)
at com.example.treeapp.DonorMessagesAdapter.onBindViewHolder(DonorMessagesAdapter.java:24)**
at androidx.recyclerview.widget.RecyclerView$Adapter.onBindViewHolder(RecyclerView.java:7065)
at androidx.recyclerview.widget.RecyclerView$Adapter.bindViewHolder(RecyclerView.java:7107)
at androidx.recyclerview.widget.RecyclerView$Recycler.tryBindViewHolderByDeadline(RecyclerView.java:6012)
at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:6279)
at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6118)
at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6114)
at androidx.recyclerview.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2303)
at androidx.recyclerview.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1627)
at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1587)
at androidx.recyclerview.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:665)
at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:4134)
at androidx.recyclerview.widget.RecyclerView.dispatchLayout(RecyclerView.java:3851)
at androidx.recyclerview.widget.RecyclerView.onLayout(RecyclerView.java:4404)
at android.view.View.layout(View.java:20967)
at android.view.ViewGroup.layout(ViewGroup.java:6440)
at androidx.constraintlayout.widget.ConstraintLayout.onLayout(ConstraintLayout.java:1762)
at android.view.View.layout(View.java:20967)
at android.view.ViewGroup.layout(ViewGroup.java:6440)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
at android.widget.FrameLayout.onLayout(FrameLayout.java:261)
at android.view.View.layout(View.java:20967)
at android.view.ViewGroup.layout(ViewGroup.java:6440)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1791)
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1635)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1544)
at android.view.View.layout(View.java:20967)
at android.view.ViewGroup.layout(ViewGroup.java:6440)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
at android.widget.FrameLayout.onLayout(FrameLayout.java:261)
at android.view.View.layout(View.java:20967)
at android.view.ViewGroup.layout(ViewGroup.java:6440)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1791)
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1635)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1544)
at android.view.View.layout(View.java:20967)
at android.view.ViewGroup.layout(ViewGroup.java:6440)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
at android.widget.FrameLayout.onLayout(FrameLayout.java:261)
at com.android.internal.policy.DecorView.onLayout(DecorView.java:955)
at android.view.View.layout(View.java:20967)
at android.view.ViewGroup.layout(ViewGroup.java:6440)
at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:3092)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2779)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1863)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:8072)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:911)
at android.view.Choreographer.doCallbacks(Choreographer.java:723)
at android.view.Choreographer.doFrame(Choreographer.java:658)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:897)
at android.os.Handler.handleCallback(Handler.java:790)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:164)
【问题讨论】:
标签: android android-recyclerview