Q:

Problems with RecyclerView Adapter position changing views size and visibility

I have a standard recycler view adapter I created but it has a strange issue where certain views will change their visibility or size randomly,
Ok I say randomly it's obviously me changing the size and or visibility of these views so lets have a short example, if I change the visibility at position 0 to gone based on my object having no text inside it, when the adapter is called position 0 will indeed be gone, but if i scroll down and back the view becomes visible, a similar thing happens with my text views so if it has less than ten characters it should be size x and else should be size y, on first load the size is correct but scrolling down and back the size changes, I'm aware that behind the scenes the view is loaded and recycled (hence the name recycler view) however I thought that the position passed into onBindViewHolder(final MyViewHolder holder, final int position) would always be correct as it comes from the main list, if anyone can help me here I'd appreciate it as right now I'm scratching my head, I'm going to post my entire adapter class for the fear of not including enough but generally its my onBindViewHolder that i feel is the main culprit which does include a lot of mostly arbritary code, as mentioned everything works to a degree but I have strange errors when scrolling up and down with views changing I believe I'm doing everything I should be everything displays the correct data just not always the right size or visiblity, would appreciate any help
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);
    }
  }
}

a couple screenshots first the initial load 

second after scrolling up and down 

A:

This sort of RecyclerView problem where items change seemingly randomly when scrolling are usually due to setting item characteristics in the wrong place.
A RecyclerView will reuse the view holders created for it (that's the recycle part), so the view holder for position 10, let's say, can be reused for the item at position 0. If the characteristics aren't changed to what they should be when the view holder is bound then the results could be unexpected. In other words, a view that is visible in position 10 would also be visible when the view holder is assigned to position 0.

