Background color or Images shuffling on scrolling in recyclerview?

5.4k views Asked by At

I am wondering that my images and color of layouts shuffling when i scrolls downwards or upwards, I created cardview using recyclerview. and set an image(changes color on click like to know if its user's favourite item) and setbackgroundcolor(randomly chossen) to the parent layout to make cardview attractive. but when i scrolls 1. the image that image changes position, 2. the layout background changes color automatically.

I am posting my adapter's code here.

public class TOAdapter extends RecyclerView.Adapter<TOAdapter.ViewHolder> {
JSONArray jsonArray;
private String title;
private String image;
private ImageLoader imageLoader;
private String subtitle;
private String subti;
private Context context;
private ImageView clip;

public TOAdapter(JSONArray jsonArray) {
    this.jsonArray = jsonArray;
}

// Create new views (invoked by the layout manager)
@Override
public TOAdapter.ViewHolder onCreateViewHolder(final ViewGroup parent,
                                               int viewType) {
    // create a new view
    View itemLayoutView = LayoutInflater.from(parent.getContext())
            .inflate(R.layout.top_twenty_list, parent, false);
    final ViewHolder viewHolder = new ViewHolder(itemLayoutView);


    final Random random = new Random(System.currentTimeMillis());// We add 155 since we want at least 155 in each channel.// Then we add to it a random number between 0 and 100.
    int r = 155 + random.nextInt(101);
    int g = 155 + random.nextInt(101);
    int b = 155 + random.nextInt(101);
    int color = Color.rgb(r, g, b);

    viewHolder.frame.setBackgroundColor(color);
    viewHolder.layer.setBackgroundColor(color);
    clip = (ImageView) itemLayoutView.findViewById(R.id.ic_clip);
    clip.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            int iColor = Color.parseColor("#0000FF");

            int red = (iColor & 0xFF0000) / 0xFFFF;
            int green = (iColor & 0xFF00) / 0xFF;
            int blue = iColor & 0xFF;

            float[] matrix = {0, 0, 0, 0, red
                    , 0, 0, 0, 0, green
                    , 0, 0, 0, 0, blue
                    , 0, 0, 0, 1, 0};

            ColorFilter colorFilter = new ColorMatrixColorFilter(matrix);
            clip.setColorFilter(colorFilter);
        }
    });


    return viewHolder;
}

// Replace the contents of a view (invoked by the layout manager)
@Override
public void onBindViewHolder(final ViewHolder viewHolder, int position) {

    // - get data from your itemsData at this position
    // - replace the contents of the view with that itemsData
    // myTypeface = Typeface.createFromAsset(context.getAssets(), "fonts/RobotoCondensedBoldItalic.ttf");


    try {
        JSONObject obj = jsonArray.getJSONObject(position);
        title = obj.getString("title");
        image = obj.getString("brand_logo");
        subtitle = obj.getString("sub_title");
    } catch (JSONException e) {
        e.printStackTrace();
    }
    viewHolder.txtViewTitle.setText(subtitle);
    viewHolder.subtitle.setText(title);


    if (imageLoader == null)
        imageLoader = AppController.getInstance().getImageLoader();
    String full_Url = "http://mycompany/assets/new" + image;
    viewHolder.thumbnail.setImageUrl(full_Url, imageLoader);
    viewHolder.btn_get_deal.setTag(position);

    viewHolder.btn_get_deal.setOnClickListener(new View.OnClickListener() {
        public JSONObject obj;
        public ArrayList<String> offerlist = new ArrayList();

        @Override
        public void onClick(View view) {

            Intent offerpage = new Intent(AppController.getInstance().getApplicationContext(), OfferDetails.class);
            Integer pos = (Integer) view.getTag();
            try {
                obj = jsonArray.getJSONObject(pos);
                offerpage.putExtra("jsonObj", obj.toString());

            } catch (JSONException e) {
                e.printStackTrace();
            }

            //offerpage.getParcelableArrayListExtra(offerlist);
            offerpage.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            AppController.getInstance().getApplicationContext().startActivity(offerpage);
        }
    });


    //viewHolder.txtViewTitle.setTypeface(myTypeface);


}

// inner class to hold a reference to each item of RecyclerView
public static class ViewHolder extends RecyclerView.ViewHolder {

    private final NetworkImageView thumbnail;
    private final RelativeLayout frame;
    private final RelativeLayout layer;
    public TextView txtViewTitle;
    public TextView subtitle;
    public ImageView clip;
    public CardView btn_get_deal;


    public ViewHolder(View itemLayoutView) {
        super(itemLayoutView);
        txtViewTitle = (TextView) itemLayoutView.findViewById(R.id.txttitle_toptwenty);
        subtitle = (TextView) itemLayoutView.findViewById(R.id.sub_title_toptwenty);
        thumbnail = (NetworkImageView) itemLayoutView.findViewById(R.id.thumbnail_topwenty);
        frame = (RelativeLayout) itemLayoutView.findViewById(R.id.frame);
        layer = (RelativeLayout) itemLayoutView.findViewById(R.id.layer);
        btn_get_deal = (CardView) itemLayoutView.findViewById(R.id.card_view);


    }

}


// Return the size of your itemsData (invoked by the layout manager)
@Override
public int getItemCount() {
    return jsonArray.length();
}

}

4

There are 4 answers

7
Xaver Kapeller On BEST ANSWER

I have created a working example of what you are trying to accomplish. The source of the errors you experience is mostly that you don't understand view recycling. I am not going to explain the whole thing to you now, but anyway here is the example:


For the example I used this layout for each row:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    android:id="@+id/background"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="80dp">

    <TextView
        android:id="@+id/textView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"/>

</FrameLayout>

I used this model:

public class ExampleModel {

    private final int mColor;
    private final String mText;

    public ExampleModel(int color, String text) {
        mColor = color;
        mText = text;
    }

    public int getColor() {
        return mColor;
    }

    public String getText() {
        return mText;
    }
}

And this view holder:

public class ExampleViewHolder extends RecyclerView.ViewHolder {

    private final FrameLayout mBackground;
    private final TextView mTextView;

    public ExampleViewHolder(View itemView) {
        super(itemView);

        mBackground = (FrameLayout) itemView.findViewById(R.id.background);
        mTextView = (TextView) itemView.findViewById(R.id.textView);
    }

    public void bind(ExampleModel model) {
        mBackground.setBackgroundColor(model.getColor());
        mTextView.setText(model.getText());
    }
}

As you can see nothing special, the Adapter implementation is equally simple:

public class ExampleAdapter extends RecyclerView.Adapter<ExampleViewHolder> {

    private final LayoutInflater mInflater;
    private final List<ExampleModel> mModels;

    public ExampleAdapter(Context context, List<ExampleModel> models) {
        mInflater = LayoutInflater.from(context);
        mModels = models;
    }

    @Override
    public ExampleViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        final View itemView = mInflater.inflate(R.layout.item_example, parent, false);
        return new ExampleViewHolder(itemView);
    }

    @Override
    public void onBindViewHolder(ExampleViewHolder holder, int position) {
        final ExampleModel model = mModels.get(position);
        holder.bind(model);
    }

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

And you use the whole thing like this:

final Random mRandom = new Random(System.currentTimeMillis());

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);

    recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));

    final List<ExampleModel> models = new ArrayList<>();
    for (int i = 0; i < 100; i++) {
        final int randomColor = generateRandomPastelColor();
        models.add(new ExampleModel(randomColor, String.valueOf(i)));
    }

    final ExampleAdapter adapter = new ExampleAdapter(getActivity(), models);
    recyclerView.setAdapter(adapter);
}

public int generateRandomPastelColor() {
    final int baseColor = Color.WHITE;

    final int red = (Color.red(baseColor) + mRandom.nextInt(256)) / 2;
    final int green = (Color.green(baseColor) + mRandom.nextInt(256)) / 2;
    final int blue = (Color.blue(baseColor) + mRandom.nextInt(256)) / 2;

    return Color.rgb(red, green, blue);
}

This should do what you are looking for and you can use it as an example of how to implement your Adapter.

3
Snehasis Maity On

I got the same problem ,when i used the radioButton in my listView row, I solved it. As in every scrolling the Cache is cleared, you have to store the changed data in every row. just check my code

@Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        // TODO Auto-generated method stub
//      if(position==0)
//      Toast.makeText(context, ""+QuestionViewPageAdapter.mRadioGroupData, Toast.LENGTH_SHORT).show();

        final ViewHolder viewHolder;
        View row=convertView;

        radioButton=new RadioButton[Integer.parseInt(mNoOfCategoryGreading)];



            LayoutInflater inflater =LayoutInflater.from(context);//(LayoutInflater)context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
            row = inflater.inflate(R.layout.question_row, parent, false);

            viewHolder=new ViewHolder();

            viewHolder.tv_qContent=(TextView)row.findViewById(R.id.tv_question);
            viewHolder.rg=(RadioGroup)row.findViewById(R.id.rgrp);

            for(int i=0;i<Integer.parseInt(mNoOfCategoryGreading);i++)
            {
                radioButton[i]=new RadioButton(context);
                radioButton[i].setLayoutParams(new RadioGroup.LayoutParams(0, LayoutParams.WRAP_CONTENT,1f));
                //1f for layout_weight="1"
                radioButton[i].setId(i);
                radioButton[i].setText(""+(i+1));

                viewHolder.rg.addView(radioButton[i]);
            }
            row.setTag(viewHolder);


            for(int i=0;i<QuestionViewPageAdapter.mRadioGroupData.size();i++){
                int p=Integer.parseInt(""+QuestionViewPageAdapter.mRadioGroupData.get(i).get("position"));
                String id=QuestionViewPageAdapter.mRadioGroupData.get(i).get("Id");
                if(p!=-1 && id.equals(""+data.get(position).get(AppData.question_Set_category_QuestionID)))
                {//radioButton[p].setSelected(true);
                    viewHolder.rg.check(p-1);}
                //Toast.makeText(context, "Yes "+p, Toast.LENGTH_LONG).show();}
            }

        viewHolder.tv_qContent.setText((position+1)+". "+data.get(position).get(AppData.question_Set_category_QuestionContent));
        viewHolder.tv_qContent.setTypeface(typeface_muso_regular300);



        viewHolder.rg.setOnCheckedChangeListener(new OnCheckedChangeListener() {

            @Override
            public void onCheckedChanged(RadioGroup group, int checkedId) {
                // TODO Auto-generated method stub

                String s=data.get(position).get(AppData.question_Set_category_QuestionID);
                isAvailabel(s);


                        int radioButtonID = viewHolder.rg.getCheckedRadioButtonId();
                        View radioButton = viewHolder.rg.findViewById(radioButtonID);
                        int idx = viewHolder.rg.indexOfChild(radioButton);
                        HashMap<String, String> map=new HashMap<String, String>();
                        map.put("position", ""+(idx+1));
                        map.put("Id", ""+data.get(position).get(AppData.question_Set_category_QuestionID));
                        map.put("CategoryId", ""+mCategoryId);
                        QuestionViewPageAdapter.mRadioGroupData.add(map);
                }


        });





        return row;
    }

    private void isAvailabel(String qId){

        for(int i=0;i<QuestionViewPageAdapter.mRadioGroupData.size();i++){
            if(qId.equals(QuestionViewPageAdapter.mRadioGroupData.get(i).get("Id"))){
                position=i;

                QuestionViewPageAdapter.mRadioGroupData.remove(i);
            }
        }

    }

     class ViewHolder {
         TextView tv_qContent ,tv_1,tv_2,tv_3;
            RadioGroup rg;
            RadioButton rBtn1,rBtn2,rBtn3,rBtn4,rBtn5;
     }
0
Jaspinder Kaur On

In my case overriding getItemId and setting sethasStableIds to true solved the shuffling issue.

in adapter:

    @Override
    public long getItemId(int position) {
    Object listItem = feeds.get(position);
    return listItem.hashCode();
    }

in main activity/fragment :

    adapter = new FeedAdapter(feeds, getContext());
    adapter.setHasStableIds(true);
0
Priyavrat Talyan On

After days of struggle and headache, i have found a very simple solution. In your adapter you just need to override 2 methods.

@Override
public int getItemViewType(int position) {
    return position;
}

@Override
public long getItemId(int position) {
    return position;
}

Helped me resolve the shuffling issue of recycler view.