问题描述
我对RecyclerView
进行了多类型化,并在项目内部添加了动画视图(闪烁的白色圆圈).在recyclerView
滚动期间,动画可以随机停止工作.
I have multityped RecyclerView
with animated views(blinking white circles) inside items. During recyclerView
scrolling animations can randomly stop working.
我认为此问题与onCreateViewHolder
或onBindViewHolder
有关,但是即使没有调用任何方法,该问题仍然存在.
I thought this problem is connected with onCreateViewHolder
or onBindViewHolder
, but this issue reproducing even if none of this methods were called.
动画重复计数设置为无限,clearAnimation()
仅在onBindViewHolder
中调用.
Animation repeat count set to infinite, clearAnimation()
only called in onBindViewHolder
.
我的适配器代码:
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import android.widget.TextView;
import com.annimon.stream.Stream;
import com.squareup.picasso.Picasso;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.TimeZone;
import butterknife.Bind;
import butterknife.ButterKnife;
public class ChatAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements View.OnClickListener {
private final int avatarSize;
private List<IncomingTextMessage> chatMessages = new ArrayList<>();
private User appOwner;
private User wallOwner;
private int MESSAGE_TYPE_MY = 0;
private int MESSAGE_TYPE_INTERLOCUTOR = 1;
private SimpleDateFormat timeFormat;
private SimpleDateFormat dateFormat;
private static final String TAG = "ChatAdapter";
private int bindViewHolderCallCounter = 0;
public ChatAdapter(User appOwner, User wallOwner, Context context) {
this.appOwner = appOwner;
this.wallOwner = wallOwner;
avatarSize = context.getResources().getDimensionPixelSize(R.dimen.post_avatar_size);
timeFormat = new SimpleDateFormat(context.getString(R.string.time_format));
dateFormat = new SimpleDateFormat(context.getString(R.string.server_date_parsing_format));
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Random rnd = new Random();
int color = Color.argb(255, rnd.nextInt(256), rnd.nextInt(256), rnd.nextInt(256));
View v;
if (viewType == MESSAGE_TYPE_MY) {
v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_my_chat_message, parent, false);
v.setBackgroundColor(color);
return new MyMessageViewHolder(v);
} else { //viewType == MESSAGE_TYPE_INTERLOCUTOR
v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_interlocutor_chat_message, parent, false);
v.setBackgroundColor(color);
return new InterlocutorMessageViewHolder(v);
}
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
bindViewHolderCallCounter++;
Log.d(TAG, "onBindViewHolder:" + chatMessages.get(position).getText() + " " + (getItemViewType(position) == MESSAGE_TYPE_MY));
if (getItemViewType(position) == MESSAGE_TYPE_MY) {
MyMessageViewHolder myMessageViewHolder = (MyMessageViewHolder) holder;
setUserAvatar(appOwner, myMessageViewHolder.ivUserAvatar);
myMessageViewHolder.ivUserAvatar.setTag(appOwner);
myMessageViewHolder.ivUserAvatar.setOnClickListener(this);
myMessageViewHolder.tvText.setText(chatMessages.get(position).getText() +" bindViewHolderCallCounter " + bindViewHolderCallCounter);
myMessageViewHolder.tvTime.setText(formatTime(chatMessages.get(position).getDateTime()));
setupMessageState(myMessageViewHolder, chatMessages.get(position));
} else /*if (getItemViewType(position) == MESSAGE_TYPE_INTERLOCUTOR)*/ {
InterlocutorMessageViewHolder interlocutorMessageViewHolder = (InterlocutorMessageViewHolder) holder;
setUserAvatar(wallOwner, interlocutorMessageViewHolder.ivUserAvatar);
interlocutorMessageViewHolder.ivUserAvatar.setTag(wallOwner);
interlocutorMessageViewHolder.ivUserAvatar.setOnClickListener(this);
interlocutorMessageViewHolder.tvText.setText(chatMessages.get(position).getText());
interlocutorMessageViewHolder.tvTime.setText(formatTime(chatMessages.get(position).getDateTime()));
}
}
private void setupMessageState(MyMessageViewHolder myMessageViewHolder, IncomingTextMessage message) {
Log.d(TAG, "setupMessageState");
Animation animation = AnimationUtils.loadAnimation(myMessageViewHolder.ivUserAvatar.getContext(), R.anim.fade_out_in_chat_circle);
myMessageViewHolder.vMessageStatusAwaitingSending.clearAnimation();
myMessageViewHolder.vMessageStatusAwaitingReading.clearAnimation();
myMessageViewHolder.vMessageStatusAwaitingSending.clearAnimation();
switch (message.getState()) {
case MessageNotification.SENT: {
Log.d(TAG, "MessageNotification.SENT" + message.getText());
myMessageViewHolder.vMessageStatusAwaitingSending.setVisibility(View.VISIBLE);
myMessageViewHolder.vMessageStatusAwaitingReading.setVisibility(View.INVISIBLE);
myMessageViewHolder.vMessageStatusAwaitingSending.setVisibility(View.INVISIBLE);
myMessageViewHolder.vMessageStatusAwaitingSending.setAnimation(animation);
break;
}
case MessageNotification.RECEIVED:
{
Log.d(TAG, "MessageNotification.RECEIVED" + message.getText());
myMessageViewHolder.vMessageStatusAwaitingSending.setVisibility(View.VISIBLE);
myMessageViewHolder.vMessageStatusAwaitingReading.setVisibility(View.VISIBLE);
myMessageViewHolder.vMessageStatusAwaitingSending.setVisibility(View.INVISIBLE);
myMessageViewHolder.vMessageStatusAwaitingReading.setAnimation(animation);
break;
}
case MessageNotification.DELIEVERED:
{
Log.d(TAG, "MessageNotification.DELIEVERED" + message.getText());
myMessageViewHolder.vMessageStatusAwaitingSending.setVisibility(View.VISIBLE);
myMessageViewHolder.vMessageStatusAwaitingReading.setVisibility(View.VISIBLE);
myMessageViewHolder.vMessageStatusAwaitingSending.setVisibility(View.VISIBLE);
myMessageViewHolder.vMessageStatusAwaitingReading.setAnimation(animation);
break;
}
case MessageNotification.READ:
{
Log.d(TAG, "MessageNotification.READ" + message.getText());
myMessageViewHolder.vMessageStatusAwaitingSending.setVisibility(View.VISIBLE);
myMessageViewHolder.vMessageStatusAwaitingReading.setVisibility(View.VISIBLE);
myMessageViewHolder.vMessageStatusAwaitingSending.setVisibility(View.VISIBLE);
break;
}
}
}
private String formatTime(String severDate) {
try {
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
return timeFormat.format(dateFormat.parse(severDate));
} catch (ParseException e) {
e.printStackTrace();
Log.e(TAG, "Time string parsing error :" + severDate);
return "";
}
}
@Override
public int getItemViewType(int position) {
if (chatMessages.get(position).getAuthorId().equals(String.valueOf(appOwner.getId())))
return MESSAGE_TYPE_MY;
else return MESSAGE_TYPE_INTERLOCUTOR;
}
private void setUserAvatar(BaseUser user, ImageView imageView) {
if (user != null && user.getPrimaryImageUrl() != null && !user.getPrimaryImageUrl().isEmpty()) {
Picasso.with(imageView.getContext())
.load(user.getPrimaryImageUrl())
.error(R.drawable.ic_user_avatar_128)
.centerCrop()
.resize(avatarSize, avatarSize)
.transform(new RoundedTransformation())
.into(imageView);
} else {
imageView.setImageResource(R.drawable.ic_user_avatar_128);
}
}
/* public void addNewUsers(List<BaseUser> newUsers) {
this.chatMessages = newUsers;
this.notifyDataSetChanged();
}*/
public void addMessage(IncomingTextMessage incomingTextMessage) {
this.chatMessages.add(incomingTextMessage);
notifyDataSetChanged();
}
public void changeMessageState(MessageNotification notification) {
Stream.of(chatMessages)
.filter(message -> message.getId().equals(notification.getMessageId()))
.forEach(message ->
{
message.setState(notification.getState());
notifyItemChanged(chatMessages.indexOf(message));
});
}
@Override
public int getItemCount() {
return chatMessages.size();
}
@Override
public long getItemId(int position) {
return chatMessages.get(position).getId().hashCode();
}
@Override
public void onClick(View view) {
if (view.getTag() instanceof User) {
User user = (User) view.getTag();
Intent intent = new Intent(view.getContext(), MainActivity.class);
intent.putExtra(Config.USER_STRING_EXTRA, user.getId());
view.getContext().startActivity(intent);
}
}
public void addMessages(ArrayList<IncomingTextMessage> incomingTextMessages) {
chatMessages.addAll(incomingTextMessages);
this.notifyDataSetChanged();
}
class MyMessageViewHolder extends RecyclerView.ViewHolder {
@Bind(R.id.vMessageStatusAwaitingSending)
View vMessageStatusAwaitingSending;
@Bind(R.id.vMessageStatusAwaitingDelivering)
View vMessageStatusAwaitingDelivering;
@Bind(R.id.vMessageStatusAwaitingReading)
View vMessageStatusAwaitingReading;
@Bind(R.id.ivUserAvatar)
ImageView ivUserAvatar;
@Bind(R.id.tvText)
TextView tvText;
@Bind(R.id.tvTime)
TextView tvTime;
public MyMessageViewHolder(View view) {
super(view);
ButterKnife.bind(this, view);
}
}
class InterlocutorMessageViewHolder extends RecyclerView.ViewHolder {
@Bind(R.id.ivUserAvatar)
ImageView ivUserAvatar;
@Bind(R.id.tvText)
TextView tvText;
@Bind(R.id.tvTime)
TextView tvTime;
public InterlocutorMessageViewHolder(View view) {
super(view);
ButterKnife.bind(this, view);
}
}
}
闪烁动画xml
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="650"
android:fromAlpha="1.0"
android:repeatMode="reverse"
android:repeatCount="infinite"
android:toAlpha="0.1" />
推荐答案
我遇到了同样的问题,发现当视图与窗口分离时,动画停止了.重新连接后,您将不会收到onBindViewHolder
调用,因此动画将不会开始.
I ran into the same issue and found that the animation stops when the view gets detached from the window. When it is reattached, you won't get the onBindViewHolder
call so the animation won't start.
解决方案是覆盖RecyclerView.Adapter<>
中的onViewAttachedToWindow
并从那里调用setAnimation
.您还需要在ViewHolder
中维护对IncomingTextMessage
的引用,因为onViewAttachedToWindow
不会通过该位置.
The solution is to override onViewAttachedToWindow
in your RecyclerView.Adapter<>
and call setAnimation
from there. You'll also need to maintain a reference to the IncomingTextMessage
in the ViewHolder
because onViewAttachedToWindow
doesn't pass in the position.
示例:
@Override
public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {
if (holder instanceof MyMessageViewHolder) {
MyMessageViewHolder messageHolder = (MyMessageViewHolder)holder;
setupMessageState(messageHolder, messageHolder.message); // messageHolder.message being the IncomingTextMessage kept in MyMessageViewHolder
}
}
这篇关于滚动后,RecyclerView中的无限动画停止的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!