androidandroid-recyclerviewadapter

RecyleView showing all items duplicates


I am trying to display posts from a server in listView. So I used recycle-view to achieve that. Everything is working fine except that ll items are displaying twice.

I counted the total fetched items from server, and the count is 5, but adapter.getItemCount is showing 10.

After searching hours on the internet, I tried following :

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

and

homeFragmentAdapter.setHasStableIds(true);

Below is my fragment...

package com.example.projectName;


import static android.content.Context.MODE_PRIVATE;
import static android.webkit.ConsoleMessage.MessageLevel.LOG;
import static com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade;

public class HomeFollowersFragment extends Fragment implements InfiniteScrollListener.OnLoadMoreListener, RecyclerViewItemListener {

    private static final String TAG = "HomeFollowersFragment";
    private static final String URL = "https://api.androidhive.info/json/movies_2017.json";
    private RecyclerView recyclerView;
    private ProgressBar postLoader;
    FFmpeg ffmpeg;
//    private List<Movie> movieList;
//    private HomeAdapter mAdapter;

    private List<PostList> postListGlobal = new ArrayList<>();
    List<VerticalDataModal> verticalDataModals;
    List<HorizontalDataModal> horizontalDataModals;
    private SwipeRefreshLayout swipeMore;
    private InfiniteScrollListener infiniteScrollListener;
    private HomeFragmentAdapter homeFragmentAdapter;

    SharedPreferences sharedPreferences;
    private Boolean isLoggedIn = false;
    private String email = "";
    private String token = "";
    private String userId = "";

    private Dialog customLoader;
    SkeletonScreen skeletonScreen;

    private int pastVisiblesItems, visibleItemCount, totalItemCount;
    private boolean loading = false;
    private EndlessScrollListener scrollListener;

    SharedPreferences sp;
    SharedPreferences.Editor Ed;

    public HomeFollowersFragment() {
        //super();
    }
    /**
     * @return A new instance of fragment HomeFollowersFragment.
     */
    public static HomeFollowersFragment newInstance() {
        return new HomeFollowersFragment();
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_home, container, false);
//        ((AppCompatActivity) getActivity()).getSupportActionBar().show();

        try{
            sharedPreferences = getActivity().getSharedPreferences("Login", MODE_PRIVATE);
            email = sharedPreferences.getString("email", null);
            token = sharedPreferences.getString("token", null);
            isLoggedIn = sharedPreferences.getBoolean("isLoggedIn", false);
            userId = sharedPreferences.getString("id", null);
        }catch (Exception e){
            e.printStackTrace();
            Log.d("StackError", "StackError: "+e);
        }
        sp = getActivity().getSharedPreferences("Posts", MODE_PRIVATE);
        if(!isLoggedIn || token == null || userId == null){
            Intent intent = new Intent(getActivity(), RegisterActivity.class);
            intent.putExtra("loginFrom", "profile");
            startActivity(intent);
        }

        recyclerView = view.findViewById(R.id.recycler_view);
        postLoader = view.findViewById(R.id.post_loader);
        swipeMore = view.findViewById(R.id.swipe_layout);

        homeFragmentAdapter = new HomeFragmentAdapter(postListGlobal, this, "home");

        if(sp.contains("postListGlobal"))
            skeletonScreen = Skeleton.bind(recyclerView)
                    .adapter(homeFragmentAdapter)
                    .shimmer(true)
                    .angle(20)
                    .frozen(false)
                    .duration(1200)
                    .count(10)
                    .load(R.layout.item_skelton_home_page)
                    .show(); //default count is 10
        RecyclerView.LayoutManager mLayoutManager = new GridLayoutManager(getActivity(), 2);
        StaggeredGridLayoutManager sLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);

        recyclerView.setLayoutManager(sLayoutManager);
        homeFragmentAdapter.setHasStableIds(true);
        recyclerView.setAdapter(homeFragmentAdapter);
        recyclerView.setNestedScrollingEnabled(false);

        customLoader = new Dialog(getActivity(), R.style.crystal_range_seek_bar);
        customLoader.setCancelable(false);
        View loaderView = getLayoutInflater().inflate(R.layout.custom_loading_layout, null);
        customLoader.getWindow().getAttributes().windowAnimations = R.style.crystal_range_seek_bar;
        customLoader.getWindow().setBackgroundDrawableResource(R.color.translucent_black);

        ImageView imageLoader = loaderView.findViewById(R.id.logo_loader);
        Glide.with(this).load(R.drawable.logo_loader).into(imageLoader);
        customLoader.setContentView(loaderView);

        if(homeFragmentAdapter.getItemCount() == 0 && !loading){
            // server fetchdata
            Log.d(TAG, "no item available..");
            postLoader.setVisibility(View.VISIBLE);
            loading = true;
            fetchStoreItems();
        }else{
            postLoader.setVisibility(View.GONE);
        }
        swipeMore.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                Log.d(TAG, "on refresh...");
                fetchStoreItems();
            }
        });
        return view;
    }
    @Override
    public void onItemClicked(int position) {
        Log.d(TAG, "click position: "+position);
        Toast.makeText(getActivity(),postListGlobal.get(position).getTitle(),Toast.LENGTH_SHORT).show();
//        Toast.makeText(getActivity(),""+position, Toast.LENGTH_SHORT).show();

    }
    public int getLastVisibleItem(int[] lastVisibleItemPositions) {
        int maxSize = 0;
        for (int i = 0; i < lastVisibleItemPositions.length; i++) {
            if (i == 0) {
                maxSize = lastVisibleItemPositions[i];
            }
            else if (lastVisibleItemPositions[i] > maxSize) {
                maxSize = lastVisibleItemPositions[i];
            }
        }
        return maxSize;
    }
    @Override
    public void onLoadMore() {
        homeFragmentAdapter.addNullData();
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                homeFragmentAdapter.removeNull();
                Toast.makeText(getContext(), "load more here...", Toast.LENGTH_LONG).show();
//                fetchStoreItems();
                swipeMore.setRefreshing(false);
            }
        }, 2000);
    }
    private void fetchStoreItems() {
        RequestQueue queue = Volley.newRequestQueue(getActivity());
        Log.d(TAG, "Post Data Followers: "+Constant.FETCH_POSTS_API);
        CacheRequest cacheRequest = new CacheRequest(0, Constant.FETCH_POSTS_API, new Response.Listener<NetworkResponse>() {
            @Override
            public void onResponse(NetworkResponse response) {
                try {
                    final String jsonString = new String(response.data,
                            HttpHeaderParser.parseCharset(response.headers));
                    if (response == null) {
                        Toast.makeText(getActivity(), "Couldn't fetch the store items! Pleas try again.", Toast.LENGTH_LONG).show();
                        loading = false;
                        return;
                    }
                    JSONObject postObj = new JSONObject(jsonString);
                    System.out.println("post full data... : " + postObj);
                    if (postObj.getBoolean("Status")) {
                        try {
                            postLoader.setVisibility(View.GONE);
                            JSONArray arrayResponse = postObj.optJSONArray("Data");
                            int dataArrLength = arrayResponse.length();
                            if(dataArrLength == 0){
                                Toast.makeText(getActivity(), "No posts available at this time, you can create yout own post by clicking on mic button", Toast.LENGTH_SHORT).show();
                            }
                            postListGlobal.clear();
                            Log.d(TAG, "Total Posts count: "+dataArrLength);
                            for(int i=0; i<dataArrLength; i++) {
                                try {
                                    JSONObject dataListObj = arrayResponse.optJSONObject(i);
                                    System.out.println("post full data... : " + dataListObj);

                                    JSONObject postDetailObj = dataListObj.optJSONObject("post_detail");
                                    JSONObject followDtatusObj = dataListObj.optJSONObject("follow_status");
                                    JSONArray postFilesArr = dataListObj.optJSONArray("post_files");
                                    JSONObject userDatasObj = postDetailObj.optJSONObject("user");

                                    String userId = userDatasObj.optString("id");
                                    String userName = userDatasObj.optString("email");
                                    String userImage = userDatasObj.optString("email");

                                    boolean followStatus = followDtatusObj.optBoolean("follow");

                                    String postId = postDetailObj.optString("id");
                                    String postTitle = postDetailObj.optString("post_title");
                                    String postDescription = postDetailObj.optString("post_description");
                                    String postCoverUrl = postDetailObj.optString("post_coverurl", "1");
                                    String postViewType = postDetailObj.optString("view_type", "1");
                                    String postAllowComment = postDetailObj.optString("allow_comments", "1");
                                    String postAllowDownload = postDetailObj.optString("allow_download", "1");
                                    String postTotalPost = postDetailObj.optString("total_post", "1");
                                    String postPostSection = postDetailObj.optString("post_section", "image");
                                    String postActiveStatus = postDetailObj.optString("is_active", "1");

                                    String postTotalViews = postDetailObj.optString("total_watched","0");
                                    String postTotalShare = postDetailObj.optString("total_share","0");
                                    String postTotalDownload = postDetailObj.optString("total_download","0");
                                    String postTotalReaction = postDetailObj.optString("total_reaction","0");

                                    String postTotalLike = postDetailObj.optString("total_like","0");
                                    String postTotalSmile = postDetailObj.optString("smile_reaction","0");
                                    String postTotalLaugh = postDetailObj.optString("laugh_reaction","0");
                                    String postTotalSad = postDetailObj.optString("sad_reaction","0");
                                    String postTotalLove = postDetailObj.optString("love_reaction","0");
                                    String postTotalShock = postDetailObj.optString("shock_reaction","0");

                                    int totalPostFiles = Integer.parseInt(postTotalPost);
                                    int postArrLength = postFilesArr.length();
                                    String postImageUrl = null;
                                    String postMusicUrl = null;

                                    String commonUrl = "http://serverName.com/";
                                    if(postArrLength >= 1){
                                        JSONObject dataFilesListObj = postFilesArr.optJSONObject(0);
//                                        System.out.println("post files full data... : " + dataFilesListObj);
                                        String postFileId = dataFilesListObj.optString("id");
                                        postImageUrl = dataFilesListObj.optString("image_file_path");
                                        postMusicUrl = dataFilesListObj.optString("music_file_path");
                                        System.out.println("post files full data... : " + dataFilesListObj);
                                    }
                                    System.out.println("post files full data... : " + commonUrl+postMusicUrl);
                                    System.out.println("post files full data... : " + commonUrl+postImageUrl);
                                    PostList postList = new PostList();
                                    postList.setId(postId);
                                    postList.setTitle(postTitle);
                                    postList.setTotalPost(""+dataArrLength);
                                    postList.setTotalView(postTotalViews);
                                    postList.setTotalReaction(postTotalReaction);
                                    postList.setMusicPath(commonUrl+postMusicUrl);

                                    postList.setImagePath(commonUrl+postImageUrl);
                                    if(postImageUrl == null){
                                        postList.setImagePath("https://amazonBucket.s3.location.amazonaws.com/images/pic1.jpg");
                                    }
                                    postList.setUserId(userId);
                                    postList.setUserName(userName);
                                    postList.setPostDataObject(arrayResponse);
                                    postListGlobal.add(postList);

                                    Log.d(TAG, "Total Posts: "+dataListObj);

                                } catch (Exception e) {
                                    e.printStackTrace();
                                    Log.d(TAG, "Post Data Error1: "+e);
                                    Toast.makeText(getActivity(), "File now found", Toast.LENGTH_LONG).show();
                                    loading = false;
                                }

                            }

                        } catch (Exception e){
                            e.printStackTrace();
                            Log.d(TAG, "Post Data Error2: "+e);
                            Toast.makeText(getActivity(), R.string.server_error, Toast.LENGTH_LONG).show();
                            loading = false;
                        }
                    }else{
                        try {
                            Toast.makeText(getActivity(), new JSONObject(jsonString).getString("Message"), Toast.LENGTH_LONG).show();
                        } catch (JSONException ex) {
                            ex.printStackTrace();
                            Log.d(TAG, "Post Data Error3: "+ex);
                            Toast.makeText(getActivity(), R.string.server_error, Toast.LENGTH_SHORT).show();
                        }
                        loading = false;
                    }

                    // refreshing recycler view
                    homeFragmentAdapter.removeNull();
                    homeFragmentAdapter.addData(postListGlobal);
                    homeFragmentAdapter.notifyDataSetChanged();
//                    save in local memory
//                    saveArrayList(postListGlobal, "postListGlobal");

                } catch (Exception e) {
                    e.printStackTrace();
                    Log.d(TAG, "Post Data Error4: "+e);
                }
                loading = true;
                homeFragmentAdapter.notifyDataSetChanged();
                swipeMore.setRefreshing(false);
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Toast.makeText(getActivity(), "onErrorResponse: "+ error, Toast.LENGTH_SHORT).show();
                swipeMore.setRefreshing(false);
                loading = true;
                homeFragmentAdapter.notifyDataSetChanged();
                postLoader.setVisibility(View.VISIBLE);
                loading = false;
                Log.d(TAG, "Post Data Error5: "+error);
            }
        }){
            @Override
            public Map<String, String> getHeaders() throws AuthFailureError {
                Map<String, String>  params = new HashMap<String, String>();
                String finalToken = "Bearer  "+token;
                params.put("Authorization", finalToken);
                params.put("Content-Type", "application/json");

                return params;
            }
        };
        // Add the request to the RequestQueue.
        queue.add(cacheRequest);

    }
    private class CacheRequest extends Request<NetworkResponse> {
        private final Response.Listener<NetworkResponse> mListener;
        private final Response.ErrorListener mErrorListener;

        public CacheRequest(int method, String url, Response.Listener<NetworkResponse> listener, Response.ErrorListener errorListener) {
            super(method, url, errorListener);
            this.mListener = listener;
            this.mErrorListener = errorListener;
        }
        @Override
        protected Response<NetworkResponse> parseNetworkResponse(NetworkResponse response) {
            Cache.Entry cacheEntry = HttpHeaderParser.parseCacheHeaders(response);
            if (cacheEntry == null) {
                cacheEntry = new Cache.Entry();
            }
            final long cacheHitButRefreshed = 3 * 60 * 1000; // in 3 minutes cache will be hit, but also refreshed on background
            final long cacheExpired = 24 * 60 * 60 * 1000; // in 24 hours this cache entry expires completely
            long now = System.currentTimeMillis();
            final long softExpire = now + cacheHitButRefreshed;
            final long ttl = now + cacheExpired;
            cacheEntry.data = response.data;
            cacheEntry.softTtl = softExpire;
            cacheEntry.ttl = ttl;
            String headerValue;
            headerValue = response.headers.get("Date");
            if (headerValue != null) {
                cacheEntry.serverDate = HttpHeaderParser.parseDateAsEpoch(headerValue);
            }
            headerValue = response.headers.get("Last-Modified");
            if (headerValue != null) {
                cacheEntry.lastModified = HttpHeaderParser.parseDateAsEpoch(headerValue);
            }
            cacheEntry.responseHeaders = response.headers;
            return Response.success(response, cacheEntry);
        }
        @Override
        protected void deliverResponse(NetworkResponse response) {
            mListener.onResponse(response);
        }
        @Override
        protected VolleyError parseNetworkError(VolleyError volleyError) {
            Log.d(TAG, "Post Data volleyError: "+volleyError);
            return super.parseNetworkError(volleyError);
        }
        @Override
        public void deliverError(VolleyError error) {
            mErrorListener.onErrorResponse(error);
        }
    }
}

and Adapter Class

package com.example.ProjectName;


public class HomeFragmentAdapter extends RecyclerView.Adapter <HomeFragmentAdapter.HomeViewHolder>{

    //    private ArrayList<Integer> dataList;
    private List<PostList> postListGlobal;
    int VIEW_TYPE_LOADING;
    int VIEW_TYPE_ITEM;
    Context context;
    private RecyclerViewItemListener callback;
    FFmpeg ffmpeg;
    String callingPage;

    public HomeFragmentAdapter(List<PostList> postListGlobal, RecyclerViewItemListener callback, String callingPage) {
        this.postListGlobal = postListGlobal;
        this.callback = callback;
        this.callingPage = callingPage;
//        setHasStableIds(true);
    }

    @NonNull
    @Override
    public HomeFragmentAdapter.HomeViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View root = null;
        context = parent.getContext();
        root = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_home_tile_list, parent, false);
        return new DataViewHolder(root);
    }

    @Override
    public void onBindViewHolder(@NonNull HomeFragmentAdapter.HomeViewHolder holder, int position) {
        if (holder instanceof DataViewHolder) {
            final PostList postList = postListGlobal.get(position);
            holder.postTitle.setText(postList.getTitle());
            holder.postWatch.setText(postList.getTotalView());
            holder.postReaction.setText(postList.getTotalReaction());

            String imageUrl = postList.getImagePath();
//            String imageUrl = Constant.SERVER_URL+"/"+postList.getImagePath();
            String musicUrl = postList.getMusicPath();
//            String musicUrl = Constant.SERVER_URL+"/"+postList.getMusicPath();
            Log.d(TAG, "Post url: "+imageUrl+" -- "+musicUrl);
//            int totalMusicTime = getDurationVal(musicUrl, "second");
            holder.postTime.setText(postList.getTotalPost());

            holder.thumbnail.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    callback.onItemClicked(position);
                    Log.d("homeView", "screenName : "+callingPage);
                    if(callingPage.equals("home")){
                        Log.d("homeView", "screenName : "+position);
                        Intent intent = new Intent(context, MainViewActivity.class);
                        intent.putExtra("loginFrom", "homeView");
                        intent.putExtra("postDataObj", postList.getPostDataObject().toString());
                        intent.putExtra("postPosition", ""+position);
                        intent.putExtra("tabId", "1");
                        context.startActivity(intent);
                    }
                }
            });

            Drawable mDefaultBackground = context.getResources().getDrawable(R.drawable.influencers);
            CircularProgressDrawable circularProgressDrawable = new CircularProgressDrawable(context);
            circularProgressDrawable.setStrokeWidth(5f);
            Glide.with(context)
                    .load(imageUrl)
                    .listener(new RequestListener<Drawable>() {
                        @Override
                        public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
//                            progressBar.setVisibility(View.GONE);
                            return false;
                        }

                        @Override
                        public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
//                            progressBar.setVisibility(View.GONE);
                            return false;
                        }
                    })
                    .error(mDefaultBackground)
                    .into(holder.thumbnail);
        }else{
            //Do whatever you want. Or nothing !!
        }
    }

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

    class DataViewHolder extends HomeViewHolder {
        public DataViewHolder(View itemView) {
            super(itemView);
        }

    }

    class ProgressViewHolder extends HomeViewHolder {
        public ProgressViewHolder(View itemView) {
            super(itemView);
        }
    }

    class HomeViewHolder extends RecyclerView.ViewHolder {
        public TextView postTitle, postTime, postWatch, postReaction;
        public ImageView thumbnail;
        public HomeViewHolder(View itemView) {
            super(itemView);
            postTitle = itemView.findViewById(R.id.post_title);
            postTime = itemView.findViewById(R.id.total_time);
            postWatch = itemView.findViewById(R.id.total_watch);
            postReaction = itemView.findViewById(R.id.total_reaction);
            thumbnail = itemView.findViewById(R.id.thumbnail);
    }
    @Override
    public long getItemId(int position) {
        return position;
    }
    @Override
    public int getItemViewType(int position) {
        return position;
    }
    public void addNullData() {
    }
    public void removeNull() {
        notifyItemRemoved(postListGlobal.size());
    }
    public void addData(List<PostList> postLists) {
        postListGlobal.addAll(postLists);
        notifyDataSetChanged();
    }
}

After trying everything, I was still not able to resolve the issue. Any help/suggestions are welcome. Let me know If I left out any needed code--if so I can update it here.


Solution

  • postListGlobal.add(postList); below this line add homeFragmentAdapter.notifyDataSetChanged(); and remove homeFragmentAdapter.removeNull(); homeFragmentAdapter.addData(postListGlobal);homeFragmentAdapter.notifyDataSetChanged(); this code, because in this case list added twice without notifying datasetchange check with your code by removing this.