Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RecyclerView getAdapterPosition() returns -1 on a callback so I can't show the new appearance for the item

Each item on my RecyclerView has a button that has three states: OPEN, LOADING, and CLOSED.

Initially all the buttons are in the OPEN state. When a button is clicked, the state is changed to LOADING and a network call is performed in the background. After the network call succeeds, the button state should be changed to CLOSED.

So in my adapter I used the following:

holder.button.setOnClickListener(v -> {
    holder.state = LOADING;
    notifyItemChanged(holder.getAdapterPosition()); /* 1 */
    callNetwork(..., () -> {
        /* this is the callback that runs on the main thread */
        holder.state = CLOSED;
        notifyItemChanged(holder.getAdapterPosition()); /* 2 */
    });
});

The LOADING state is always visualized correctly at /* 1 */ because getAdapterPosition() gives me the correct position.

However, the CLOSED state of the button is never visualized, because getAdapterPosition at /* 2 */ always returns -1.

I might understand getAdapterPosition() wrongly in this case.

How do I refresh the appearance of an item on a callback?

like image 713
Randy Sugianto 'Yuku' Avatar asked Mar 28 '16 09:03

Randy Sugianto 'Yuku'


2 Answers

From the docs:

Note that if you've called notifyDataSetChanged(), until the next layout pass, the return value of this method will be NO_POSITION

NO_POSITION is a constant whose value is -1. This might explain why you are getting a return value of -1 here.

In any case, why don't you find the position of the model in the underlying dataset and then call notifyItemChanged(int position)? You could save the model as a field in the holder.

For example:

public class MyHolder extends RecyclerView.ViewHolder {
    
    private Model mMyModel;

    public MyHolder(Model myModel) {
        mMyModel = myModel;
    }
    
    public Model getMyModel() {
        return mMyModel;
    }
}

holder.button.setOnClickListener(v -> {
    holder.state = LOADING;
    notifyItemChanged(holder.getAdapterPosition());
    callNetwork(..., () -> {
        /* this is the callback that runs on the main thread */
        holder.state = CLOSED;
        int position = myList.indexOf(holder.getMyModel());
        notifyItemChanged(position);
    });
});

Alternatively you can just ignore if the position is -1, like this:

holder.button.setOnClickListener(v -> {
    holder.state = LOADING;
    int preNetworkCallPosition = holder.getAdapterPosition();
    if (preNetworkCallPosition != RecyclerView.NO_POSITION) {
        notifyItemChanged(preNetworkCallPosition);
    }
    callNetwork(..., () -> {
        /* this is the callback that runs on the main thread */
        holder.state = CLOSED;
        int postNetworkCallPosition = holder.getAdapterPosition();
        if (postNetworkCallPosition != RecyclerView.NO_POSITION) {
             notifyItemChanged(postNetworkCallPosition);
        }
    });
});
like image 118
MidasLefko Avatar answered Oct 12 '22 23:10

MidasLefko


getAdapterPosition(); It will always return -1 when recyclerview makes layout calculations. You are calling this methods inside ViewHolder.. It means RecyclerView is doing calculations.

If you need position inside click actions of view, call it in the public void onClick(final View v) method for example:

"@Override public void onBindViewHolder(@NonNull final ViewHolder holder, final int position) {

    final Students user = mUsers.get(position);
    holder.Name.setText(user.getFullname());
    holder.Index.setText(user.getIndex_number());


    if (user.getThumbnail().equals("default")) {
        holder.profile_image.setImageResource(R.drawable.profile_pic);
    } else {
        Picasso.get().load(user.getThumbnail())
                .placeholder(R.drawable.profile_pic)
                .into(holder.profile_image);
    }

  holder.itemView.setOnClickListener(new View.OnClickListener() {
       @Override
       public void onClick(final View v) {
           **list_user_id = mUsers.get(position).getId();**

           Intent Sub = new Intent(mContext, UserProfileActivity.class);
           Sub.putExtra("user_id1", list_user_id);
           mContext.startActivity(Sub);

BUT NOT

getAdapterPosition(); It will always return -1 when recyclerview makes layout calculations. You are calling this methods inside ViewHolder.. It means RecyclerView is doing calculations.

If you need position inside click actions of view, call it in the public void onClick(final View v) method for example:

@Override public void onBindViewHolder(@NonNull final ViewHolder holder, final int position) {

    final Students user = mUsers.get(position);
    holder.Name.setText(user.getFullname());
    holder.Index.setText(user.getIndex_number());

**list_user_id = mUsers.get(position).getId();**



    if (user.getThumbnail().equals("default")) {
        holder.profile_image.setImageResource(R.drawable.profile_pic);
    } else {
        Picasso.get().load(user.getThumbnail())
                .placeholder(R.drawable.profile_pic)
                .into(holder.profile_image);
    }

  holder.itemView.setOnClickListener(new View.OnClickListener() {
       @Override
       public void onClick(final View v) {

           Intent Sub = new Intent(mContext, UserProfileActivity.class);
           Sub.putExtra("user_id1", list_user_id);
           mContext.startActivity(Sub);
like image 43
Benjamin Arhen Avatar answered Oct 12 '22 22:10

Benjamin Arhen