Android RecyclerView not changing background color on first item programmatically

4.1k views Asked by At

I'm creating a list with a RecyclerView. Every list item is a post from a user (Right now hard coded). The background for every post is loaded from a layer-list XML file in the drawable folder.

Everything is working as intended with the texts etc. but I'm trying to change the background color programmatically. It changes the background color for every item, except for the first item, and I cannot figure out why.

The first item always gets the background color specified by the solid color of the shape inside the item called shape_background in the XML file, so it is not changed, but the following items get the color #ff22ff.

This is the implementation of the adapter:

class PostListAdapter extends RecyclerView.Adapter<PostListAdapter.PostViewHolder>{

    private LayoutInflater inflater;
    private List<PostRow> data = Collections.emptyList();

    PostListAdapter(Context context, List<PostRow> data) {
        inflater = LayoutInflater.from(context);
        this.data = data;
    }

    @Override
    public void onBindViewHolder(PostViewHolder holder, int position) {

        PostRow current = data.get(position);
        holder.text.setText(current.text.toUpperCase());
        holder.time.setText(current.time.toUpperCase());
        holder.answers.setText(current.answers.toUpperCase());

        try {
            // "#ff22ff" will be changed to current.color, unique color for every post
            // That string is parsed from a JSON request, hence the try-catch.
            int color = Color.parseColor("#ff22ff"); 
            holder.shape.setColor(color);
        } catch (Exception e){
            e.printStackTrace();
        }

    }

    @Override
    public PostViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = inflater.inflate(R.layout.post_row, parent, false);
        return new PostViewHolder(view);
    }

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

    class PostViewHolder extends RecyclerView.ViewHolder {

        TextView text;
        TextView time;
        TextView answers;
        GradientDrawable shape;

        PostViewHolder(View itemView) {
            super(itemView);
            text = (TextView) itemView.findViewById(R.id.text);
            time = (TextView) itemView.findViewById(R.id.time);
            answers = (TextView) itemView.findViewById(R.id.answers);
            LayerDrawable layers = (LayerDrawable) ContextCompat.getDrawable(itemView.getContext(), R.drawable.bubble);

            shape = (GradientDrawable) (layers.findDrawableByLayerId(R.id.shape_background));
        }

    }

}

Why is the background of the first item not changed, but the texts are?

Thank you in advance!

3

There are 3 answers

1
Vignesh Sundaramoorthy On BEST ANSWER

On your onBindViewHolder, get your view(from holder) for that you need to change background color & get its current background(drawable bubble, that you already set in XML attribute)

LayerDrawable layerDrawable = (LayerDrawable) yourView.getBackground().getCurrent();
GradientDrawable gradientDrawable = (GradientDrawable) layerDrawable.findDrawableByLayerId(R.id. shape_background).getCurrent();
// set you color based on position
gradientDrawable.setColor(Color.parseColor("#ff22ff"));
0
Anton Pogonets On

You have two different issues here.

First - you change drawable itself but not view background. It is solved by liubaoyua and Vignesh Sundar answers.

Second - you change R.drawable.bubble statically. Android can cache and reuse it even after mutation. It can lead to another issues when you start use different colors for different items.

To solve second issue you must call android.graphics.drawable.Drawable#mutate every time before firstly mutate it.

By default, all drawables instances loaded from the same resource share a common state; if you modify the state of one instance, all the other instances will receive the same modification.

So it should looks something like this:

class PostViewHolder extends RecyclerView.ViewHolder {

    TextView text;
    TextView time;
    TextView answers;
    LayerDrawable layers;
    GradientDrawable shape;

    PostViewHolder(View itemView) {
        super(itemView);

        text = (TextView) itemView.findViewById(R.id.text);
        time = (TextView) itemView.findViewById(R.id.time);
        answers = (TextView) itemView.findViewById(R.id.answers);

        layers = (LayerDrawable) ContextCompat
                .getDrawable(itemView.getContext(), R.drawable.bubble)
                .mutate();

        shape = (GradientDrawable) layers.findDrawableByLayerId(R.id.shape_background);
        itemView.setBackground(layers);
    }

    void setColor(Color color) {
        shape.setColor(color);
        itemView.setBackgroundDrawable(layers);
    }
}

May be can be little simplified by moving .mutate() from layer to shape.

1
liubaoyua On

you have called holder.shape.setColor(color);

but the holderView didn't refresh.

maybe you should call

holder.itemView.setBackgroundDrawable( ContextCompat.getDrawable(itemView.getContext(), R.drawable.bubble))

to refresh the holderView