Ad

RecyclerView Is Scrolling To Up Automatically And Flickering

Basically I'm using a RecyclerView with gridAdapter to load images from server. But the problem is items of RecyclerView are blinking in a quirky manner. I've tried all the possible solutions but none of them worked so far.

I tried to update glide version from 4.4.0 to 4.8.0 but it's futile. I also tried to disable the animations of RecyclerView but that couldn't help. Can anyone please help me to solve this issue?

See how Images are blinking

Code:

GridLayoutManager gridLayoutManager = new GridLayoutManager(v.getContext(),3);
gridLayoutManager.setSmoothScrollbarEnabled(true);
gridLayoutManager.setItemPrefetchEnabled(true);
gridLayoutManager.setInitialPrefetchItemCount(20);

posts_rView.setLayoutManager(gridLayoutManager);
posts_rView.setItemAnimator(null);
posts_rView.setHasFixedSize(true);

gridPostAdapter=new StarredAdapter(timelineDataList);
posts_rView.setAdapter(gridPostAdapter);

Data Updation Code:

private  Emitter.Listener handlePosts = new Emitter.Listener(){

    @Override
    public void call(final Object... args){
        try {
            JSONArray jsonArray=(JSONArray)args[0];
            Needle.onMainThread().execute(() -> {
                timelineDataList.clear();
                swipeRefreshLayout.setRefreshing(false);

                for(int i=0;i<jsonArray.length();i++){
                    try {
                        //JSONArray arr=jsonArray.getJSONArray(i);
                        JSONObject ob=jsonArray.getJSONObject(i);

                        post_username=ob.getString("_pid");
                        post_fullname=ob.getString("owner_fullname");

                        if(ob.has("owner_profPic"))post_profPic=ob.getString("owner_profPic");
                        else post_profPic="";

                        post_time=ob.getString("time");

                        post_link=ob.getString("img_link");
                        likes_counter=ob.getString("likes_counter");
                        comments_counter=ob.getString("comments_counter");
                        if(ob.has("caption")) post_caption=ob.getString("caption");
                        else post_caption=null;

                        //Skipping Private Posts
                        if(ob.getString("private_post_stat").equals("yes")&&!post_username.equals(my_username)) {
                            continue;
                        }
                        else
                            private_post_stat = ob.getString("private_post_stat");

                        comment_disabled=ob.getString("comment_disabled");

                        share_disabled=ob.getString("share_disabled");

                        download_disabled=ob.getString("download_disabled");

                        if(ob.has("short_book_content")) short_book_content=ob.getString("short_book_content");
                        else short_book_content=null;
                        society_name_adp=ob.getString("society");

                        addTimelineData(post_username,post_fullname,post_profPic,post_time,post_link,post_caption,
                                private_post_stat,comment_disabled,share_disabled,download_disabled,likes_counter,comments_counter,short_book_content,society_name_adp);

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

                RecyclerView.Adapter adapter=posts_rView.getAdapter();
                posts_rView.setAdapter(null);
                posts_rView.setAdapter(adapter);
            });
        } catch (Exception e) {
            Log.e("error",e.toString());
        }
    }
};

private void addTimelineData(String username,String fullname,String post_profPic,String time,String img_link,String caption,
                             String private_post_stat,String comment_disabled,String share_disabled,String download_disabled,String likes_counter,
                             String comments_counter,String short_book_content,String society_name_adp){
    boolean isRepeated = false;
    for(TimelineData data:timelineDataList){
        if (data.getTime().equals(time)) {
            isRepeated = true;
        }
    }

    if(!isRepeated){
        timelineDataList.add(new TimelineData(username,fullname,post_profPic,time,img_link,caption,private_post_stat,comment_disabled,share_disabled,download_disabled,likes_counter,comments_counter,short_book_content,society_name_adp));
        gridPostAdapter.notifyDataSetChanged();
        // posts_rView.scrollToPosition(gridPostAdapter.getItemCount()-1);
       // posts_rView.scrollToPosition(0);
    }

     //gridPostAdapter.notifyItemInserted(timelineDataList.size()-1);
}

Adapter Class:

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

    //loading img
    Glide.with( parent.getContext()).asBitmap().load(arrayList.get(position)).apply(new RequestOptions()
        .override(200, 200)
        .dontAnimate()
        .placeholder(new ColorDrawable(Color.parseColor("#20001919"))))
        .thumbnail(0.1f)
        .into(holder.img);
}

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

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

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

Answer

I do not quite understand yet why the flickering and repositioning to the first element is occurring in your case, as I do not see the updating policy of your RecyclerView. However, I would like to suggest something which might remove the problem.

  1. I would like to suggest you remove the setInitialPrefetchItemCount and setItemPrefetchEnabled configurations while setting GridLayoutManager. This will aid in case of you are having a nested RecyclerView which is not your case.
  2. You are setting adapter each time you are updating your data from the server. I guess you are calling the update action code when you are scrolling the items. Which you should not. Moreover, please remove setting up the adapter of your RecyclerView each time you are updating the dataset. Just use, notifyDataSetChanged after each of your updates. Do not clear the timelineDataList and append the new items instead, and then call notifyDataSetChanged on your adapter.
  3. You are calling notifyDataSetChanged in addTimelineData after each insert to your list. This should be done only after adding all the elements to the list.

So I would like to propose the following code for setting your adapter and updating your RecyclerView. Please note that I have not tested the code and you might have to modify some errors which might occur.

The code for setting up the layout manager and the adapter.

GridLayoutManager gridLayoutManager = new GridLayoutManager(v.getContext(),3);
posts_rView.setLayoutManager(gridLayoutManager);
posts_rView.setHasFixedSize(true);

gridPostAdapter = new StarredAdapter(timelineDataList);
posts_rView.setAdapter(gridPostAdapter);

The code for updating the list.

private  Emitter.Listener handlePosts = new Emitter.Listener(){

    @Override
    public void call(final Object... args){
        try {
            JSONArray jsonArray = (JSONArray)args[0];
            Needle.onMainThread().execute(() -> {

                // Do not clear the list. Just append the new data in the list instead
                // timelineDataList.clear();

                swipeRefreshLayout.setRefreshing(false);

                for(int i = 0; i < jsonArray.length(); i++){
                    try {
                        JSONObject ob = jsonArray.getJSONObject(i);

                        post_username = ob.getString("_pid");
                        post_fullname = ob.getString("owner_fullname");
                        if(ob.has("owner_profPic")) post_profPic = ob.getString("owner_profPic");
                        else post_profPic = "";

                        post_time = ob.getString("time");

                        post_link = ob.getString("img_link");
                        likes_counter = ob.getString("likes_counter");
                        comments_counter = ob.getString("comments_counter");
                        if(ob.has("caption")) post_caption = ob.getString("caption");
                        else post_caption = null;

                        //Skipping Private Posts
                        if(ob.getString("private_post_stat").equals("yes")&&!post_username.equals(my_username)) {
                            continue;
                        }
                        else
                            private_post_stat = ob.getString("private_post_stat");

                        comment_disabled = ob.getString("comment_disabled");
                        share_disabled = ob.getString("share_disabled");
                        download_disabled = ob.getString("download_disabled");

                        if (ob.has("short_book_content")) short_book_content = ob.getString("short_book_content");
                        else short_book_content = null;

                        society_name_adp = ob.getString("society");
                        addTimelineData(post_username, post_fullname, post_profPic, post_time, post_link,post_caption, private_post_stat, comment_disabled, share_disabled, download_disabled, likes_counter, comments_counter, short_book_content, society_name_adp);
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                }

                gridPostAdapter.notifyDataSetChanged();
            });

        } catch (Exception e) {
            Log.e("error",e.toString());
        }
    }
};

private void addTimelineData(String username, String fullname, String post_profPic, String time, String img_link, String caption, String private_post_stat, String comment_disabled, String share_disabled, String download_disabled, String likes_counter, String comments_counter, String short_book_content, String society_name_adp) {

    boolean isRepeated = false;
    for(TimelineData data:timelineDataList){
        if (data.getTime().equals(time)) {
            isRepeated = true;
        }
    }

    if(!isRepeated){
        timelineDataList.add(new TimelineData(username,fullname,post_profPic,time,img_link,caption,private_post_stat,comment_disabled,share_disabled,download_disabled,likes_counter,comments_counter,short_book_content,society_name_adp));

        // Do not call notifyDataSetChanged each time you are adding an item. This will be called in the call function above. So remove this line.
        // gridPostAdapter.notifyDataSetChanged();
    }
}

Try to add some pagination in the server side API if it is not there already so that you might fetch the next 20 dataset instead of fetching the whole data again when you are scrolling down.

Hope that helps.

Ad
source: stackoverflow.com
Ad