android - 滚动后 RecyclerView 中的无限动画停止

标签 android android-animation android-recyclerview

我有多类型的RecyclerView,项目内部有动画 View (闪烁的白色圆圈)。在 recyclerView 期间,滚动动画可能会随机停止工作。

我认为这个问题与 onCreateViewHolderonBindViewHolder 有关,但即使没有调用这些方法,这个问题也会重现。

动画重复计数设置为无限,clearAnimation() 仅在 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" />

enter image description here

最佳答案

我遇到了同样的问题,发现当 View 从窗口分离时动画停止。重新连接后,您不会得到 onBindViewHolder调用这样动画就不会开始。

解决方案是覆盖onViewAttachedToWindow在你的RecyclerView.Adapter<>并调用setAnimation从那里。您还需要维护对 IncomingTextMessage 的引用在ViewHolder因为onViewAttachedToWindow不传位。

例子:

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

关于android - 滚动后 RecyclerView 中的无限动画停止,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36219485/

相关文章:

java - 使用 Seekbar 将 imageView 旋转一定角度

android - 使用卡片在 RecyclerView 上添加搜索过滤器?

android - 如何从 recyclerView 中删除一个项目?

Android OnItemCLickListener 在 ListView 中不起作用

android - RxJavaPlugins 错误找不到类 "com.google.devtools.build.android.desugar.runtime.ThrowableExtension"

android - 是否有用于 Fragment 转换的 "setSharedElementsUseOverlay()"方法?

java - 如何围绕正方形移动图像

android - 如何在 RecyclerView 中的 CardView 上使用 OnClickListener?

android - 在 Android 上的 TextView 中突出显示文本

Android:RESULT_OK 的反义词是什么