RecyclerView Adapter 位置改变视图大小和可见性的问题

Problems with RecyclerView Adapter position changing views size and visibility

我有一个我创建的标准回收器视图适配器,但它有一个奇怪的问题,即某些视图会随机改变它们的可见性或大小,

好吧,我随机地说,这显然是我改变了这些视图的大小和/或可见性,所以让我们举一个简短的例子,如果我根据我的对象内部没有文本将位置 0 的可见性更改为消失,当适配器被称为位置 0 确实会消失,但如果我向下和向后滚动视图变得可见,我的文本视图也会发生类似的事情,所以如果它少于十个字符,它应该是 x 大小,否则应该是 y 大小,在第一次加载时大小是正确的但是向下和向后滚动大小变化,我知道在幕后加载和回收视图(因此名称回收视图)但是我认为位置传递到 onBindViewHolder(最终 MyViewHolder 持有人, final int position) 总是正确的,因为它来自主列表,如果有人能在这里帮助我,我将不胜感激,因为现在我正在挠头,我将 post 我的整个适配器 class 因为担心没有包含足够的内容,但通常它是我的 onBindViewH我觉得较旧的是罪魁祸首,它确实包含很多大部分是任意的代码,如前所述,一切都在一定程度上有效,但是当我上下滚动视图时出现奇怪的错误正确的数据并不总是正确的大小或可见性,将不胜感激任何帮助

public class LiveMessageAdapter extends 
RecyclerView.Adapter<LiveMessageAdapter.MyViewHolder> {
    private static final int VIEW_TYPE_MESSAGE_SENT = 1;
    private static final int VIEW_TYPE_MESSAGE_RECEIVED = 2;
    private ArrayList<DatabaseMessage> messageList;
    private FrameLayout fade;
    private ImageView holderImage;
    private MessageListActivity.OnItemTouchListener onItemTouchListener;
    private Context context;
    private String userId;
    private Date dateCheck;
    private SparseBooleanArray selectedItems;

public class MyViewHolder extends RecyclerView.ViewHolder {
    public EmojiTextView message_text;
    public TextView time_stamp;
    public ImageView holderImage;
    public ConstraintLayout holder;
    public View sentReceived;
    public View fade;
    public ProgressBar progressBar;
    public ShapeOfView shapeOfView;
    public ConstraintLayout dateHolder;
    public TextView bigDateText;

    public MyViewHolder(View view) {
        super(view);
        message_text = view.findViewById(R.id.text_message_body);
        time_stamp = view.findViewById(R.id.text_message_time);
        fade = view.findViewById(R.id.fade);
        progressBar = view.findViewById(R.id.progress);
        holderImage = view.findViewById(R.id.image_view);
        shapeOfView = view.findViewById(R.id.image_view_holder);
        holder = view.findViewById(R.id.holder);
        sentReceived = view.findViewById(R.id.send_received);
        dateHolder = view.findViewById(R.id.date_holder);
        bigDateText = view.findViewById(R.id.big_date_text);
        Linkify.addLinks(message_text, Linkify.ALL);
        holder.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                onItemTouchListener.onCardClick(v, getPosition());
            }
        });
        holder.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                onItemTouchListener.onCardLongClick(v, getPosition());
                return false;
            }
        });
    }
}

public LiveMessageAdapter(ArrayList<DatabaseMessage> messageList,
                          Context context,
                          MessageListActivity.OnItemTouchListener onItemTouchListener,
                          SparseBooleanArray selectedItems, String userId) {
    this.messageList = messageList;
    this.onItemTouchListener = onItemTouchListener;
    this.context = context;
    this.selectedItems = selectedItems;
    this.userId = userId;
}

@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View itemView;
    if (viewType == VIEW_TYPE_MESSAGE_RECEIVED) {
        itemView = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.received_message_holder, parent, false);
        return new MyViewHolder(itemView);
    } else {
        itemView = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.sent_message_holder, parent, false);
        return new MyViewHolder(itemView);
    }
}

@Override
public int getItemViewType(int position) {
    DatabaseMessage message = messageList.get(position);
    if (message.getSenderId().equals(userId)) {
        // If the current user is the sender of the message
        return VIEW_TYPE_MESSAGE_SENT;
    } else {
        // If some other user sent the message
        return VIEW_TYPE_MESSAGE_RECEIVED;
    }
}

public void toggleSelection(int pos) {
    if (selectedItems.get(pos, false)) {
        selectedItems.delete(pos);
        //view.setBackgroundColor(Color.WHITE);
    } else {
        selectedItems.put(pos, true);
        //view.setBackgroundColor(Color.BLUE);
    }
    notifyItemChanged(pos);
}

public void clearSelections() {
    selectedItems.clear();
    notifyDataSetChanged();
}

public int getSelectedItemCount() {
    return selectedItems.size();
}

public ArrayList<DatabaseMessage> getSelectedItems() {
    ArrayList<DatabaseMessage> items = new ArrayList<>(selectedItems.size());
    for (int i = 0; i < selectedItems.size(); i++) {
        items.add(messageList.get(selectedItems.keyAt(i)));
    }
    return items;
}

public void refreshMyList(ArrayList<DatabaseMessage> list) {
    this.messageList.clear();
    this.messageList.addAll(list);
    this.notifyDataSetChanged();
}

public ArrayList<DatabaseMessage> getList() {
    return messageList;
}

@Override
public void onBindViewHolder(final MyViewHolder holder, final int position) {
    final DatabaseMessage userMessage = messageList.get(position);
    if (dateCheck == null){
        //first load show date
        dateCheck = userMessage.getTime_stamp();
        holder.dateHolder.setVisibility(View.VISIBLE);
        holder.bigDateText.setText(getTextDateHolder(dateCheck.getTime()));
    }else{
        //get time and date from message
        Calendar messageTime = Calendar.getInstance();
        messageTime.setTimeInMillis(userMessage.getTime_stamp().getTime());
        // get time and from date check
        Calendar dateCheckTime = Calendar.getInstance();
        dateCheckTime.setTimeInMillis(dateCheck.getTime());
        //check if they are the same
        if (dateCheckTime.get(Calendar.DATE) == messageTime.get(Calendar.DATE)
                && ((dateCheckTime.get(Calendar.MONTH) == messageTime.get(Calendar.MONTH)))
                && ((dateCheckTime.get(Calendar.YEAR) == messageTime.get(Calendar.YEAR)))) {
            //make sure this is gone if they are the same
            holder.dateHolder.setVisibility(View.GONE);
        }else{
            //set date holder visible and set the text
            holder.dateHolder.setVisibility(View.VISIBLE);
            holder.bigDateText.setText(getTextDateHolder(dateCheck.getTime()));
            //set date check to most recent checked time
            dateCheck = userMessage.getTime_stamp();
        }
        //check whether to show the message time
        if (dateCheckTime.get(Calendar.DATE) == messageTime.get(Calendar.DATE)
                && ((dateCheckTime.get(Calendar.MONTH) == messageTime.get(Calendar.MONTH)))
                && ((dateCheckTime.get(Calendar.YEAR) == messageTime.get(Calendar.YEAR)))
                && (dateCheckTime.get(Calendar.HOUR) == messageTime.get(Calendar.HOUR))
                && dateCheckTime.get(Calendar.MINUTE) == messageTime.get(Calendar.MINUTE)) {
            //make sure this is gone if they are the same
            holder.time_stamp.setVisibility(View.GONE);
        }else{
            //set date holder visible and set the text
            holder.time_stamp.setVisibility(View.VISIBLE);
            holder.time_stamp.setText(getSmsTodayYestFromMilli(userMessage.getTime_stamp().getTime()));
        }
    }


    holder.message_text.setText(userMessage.getMessage());

    if (userMessage.getData_type().equals(Constants.DATA_TYPE_IMAGE)) {
        holder.shapeOfView.setVisibility(View.VISIBLE);
        Glide.with(context)
                .load(userMessage.getData_url())
                .apply(new RequestOptions()
                        .dontAnimate().placeholder(R.drawable.placeholder).centerCrop())
                .listener(new RequestListener<Drawable>() {
                    @Override
                    public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
                        return false;
                    }

                    @Override
                    public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
                        holder.progressBar.setVisibility(View.INVISIBLE);
                        return false;
                    }
                })
                .into(holder.holderImage);

    } else {
        holder.shapeOfView.setVisibility(View.GONE);
    }
    if (userMessage.getMessage().length() < 1) {
        holder.message_text.setVisibility(View.GONE);
    } else if (userMessage.getMessage().length() >= 1 && userMessage.getMessage().length() < 10) {
        holder.message_text.setTextSize(22f);
    } else {
        holder.message_text.setTextSize(14f);
    }
    switch (userMessage.getSent_received()) {
        case 0:
            holder.sentReceived.setBackground(context.getResources().getDrawable(R.drawable.circle_white));
            break;
        case 1:
            holder.sentReceived.setBackground(context.getResources().getDrawable(R.drawable.circle_grey));
            break;
        case 2:

            holder.sentReceived.setBackground(context.getResources().getDrawable(R.drawable.circle_blue));
            break;

    }
    final EmojiInformation emojiInformation = EmojiUtils.emojiInformation(userMessage.getMessage());
    final int res;
    if (emojiInformation.emojis.size() > 0){
        if (emojiInformation.isOnlyEmojis && emojiInformation.emojis.size() == 1) {
            res = R.dimen.emoji_size_single_emoji;
        } else if (emojiInformation.isOnlyEmojis && emojiInformation.emojis.size() > 1) {
            res = R.dimen.emoji_size_only_emojis;
        } else {
            res = R.dimen.emoji_size_default;
        }
        holder.message_text.setEmojiSizeRes(res, false);
    }

    if (selectedItems.get(position, false)) {
        holder.fade.setVisibility(View.VISIBLE);
    } else {
        holder.fade.setVisibility(View.INVISIBLE);
    }
}

@Override
public int getItemCount() {
    return messageList.size();
}

private String getSmsTodayYestFromMilli(long msgTimeMillis) {

    Calendar messageTime = Calendar.getInstance();
    messageTime.setTimeInMillis(msgTimeMillis);
    // get Currunt time
    Calendar now = Calendar.getInstance();

    final String strTimeFormate = "h:mm aa";
    final String strDateFormate = "dd/MM/yyyy h:mm aa";

    if (now.get(Calendar.DATE) == messageTime.get(Calendar.DATE)
            &&
            ((now.get(Calendar.MONTH) == messageTime.get(Calendar.MONTH)))
            &&
            ((now.get(Calendar.YEAR) == messageTime.get(Calendar.YEAR)))
            ) {

        return DateFormat.format(strTimeFormate, messageTime).toString();

    } else if (
            ((now.get(Calendar.DATE) - messageTime.get(Calendar.DATE)) == 1)
                    &&
                    ((now.get(Calendar.MONTH) == messageTime.get(Calendar.MONTH)))
                    &&
                    ((now.get(Calendar.YEAR) == messageTime.get(Calendar.YEAR)))
            ) {
        return "yesterday at " + DateFormat.format(strTimeFormate, messageTime);
    } else {
        return "date : " + DateFormat.format(strDateFormate, messageTime);
    }
}

private String getTextDateHolder(long msgTimeMillis) {

    Calendar messageTime = Calendar.getInstance();
    messageTime.setTimeInMillis(msgTimeMillis);
    // get Currunt time
    Calendar now = Calendar.getInstance();

    final String strDateFormate = "dd/MM/yyyy";

    if (now.get(Calendar.DATE) == messageTime.get(Calendar.DATE)
            && ((now.get(Calendar.MONTH) == messageTime.get(Calendar.MONTH)))
            && ((now.get(Calendar.YEAR) == messageTime.get(Calendar.YEAR)))
            ) {

        return "TODAY";

    } else if (
            ((now.get(Calendar.DATE) - messageTime.get(Calendar.DATE)) == 1)
                    && ((now.get(Calendar.MONTH) == messageTime.get(Calendar.MONTH)))
                    && ((now.get(Calendar.YEAR) == messageTime.get(Calendar.YEAR)))
            ) {
        return "YESTERDAY";
    } else {
        return "" + DateFormat.format(strDateFormate, messageTime);
    }
  }
}

首先是初始加载的几个屏幕截图

上下滚动后秒

这种RecyclerView项目在滚动时看似随机变化的问题通常是由于在错误的地方设置了项目特征。

A RecyclerView 将重用为其创建的视图持有者(即 recycle 部分),因此可以重用位置 10 的视图持有者对于位置 0 处的项目。如果在视图持有者 bound 时未将特征更改为应有的特征,则结果可能出乎意料。换句话说,当视图持有者被分配到位置 0 时,在位置 10 可见的视图也将可见。