【问题标题】:Infinite animation in RecyclerView stops after scrollingRecyclerView 中的无限动画在滚动后停止
【发布时间】:2016-03-25 11:58:02
【问题描述】:

我在项目内部使用动画视图(闪烁的白色圆圈)对RecyclerView 进行了多类型化。在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" />

【问题讨论】:

    标签: android android-animation android-recyclerview


    【解决方案1】:

    我遇到了同样的问题,发现当视图与窗口分离时动画停止。重新附加后,您将不会收到 onBindViewHolder 调用,因此动画不会开始。

    解决方案是在您的RecyclerView.Adapter&lt;&gt; 中覆盖onViewAttachedToWindow 并从那里调用setAnimation。您还需要在ViewHolder 中维护对IncomingTextMessage 的引用,因为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
      }
    }
    

    【讨论】:

    • 这对我也有用。但是,谁能解释为什么 onBindViewHolder 在重新连接时没有被调用?
    • @YasithaChinthaka 我了解到,如果项目没有更改,则不会调用 onBind - 项目不需要重新绑定 - 它只是从缓存中获取。我从youtu.be/LqBlYJTfLP4youtu.be/imsr8NrIAMsyoutu.be/KhLVD6iiZQs 学到的
    【解决方案2】:

    尝试在您的 MessageNotification.READ 案例中将动画设置为 null:

        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);
    
                        myMessageViewHolder.vMessageStatusAwaitingReading.setAnimation(null);
    
                        break;
                    }
    

    【讨论】:

    • 我刚刚试了一下,没有成功。无论如何,谢谢你的回答。
    • hm.. 也尝试将 myMessageViewHolder.vMessageStatusAwaitingSending.clearAnimation(); 替换为 myMessageViewHolder.vMessageStatusAwaitingReading.setAnimation(null);
    • 其实我也是这么做的,只是没提过。还要注意onBindViewHolder 方法甚至都没有调用(我仔细检查过)。这个事实让事情变得太复杂了。
    猜你喜欢
    • 1970-01-01
    • 2017-09-02
    • 2015-01-15
    • 1970-01-01
    • 2011-09-16
    • 2021-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多