David Parks
David Parks

Reputation: 69

Android recyclerview adding duplicate elements on each activity creation

When I launch a new fragment from my navigation drawer, it's meant to take me to a home page with a cardView of matching profiles. It works great the first time and every profile I want to show up is there. However, when I navigate away from the fragment and then click on it again, the elements are duplicated, and each subsequent time I refresh the page the items are duplicated once more. I believe this problem lies somewhere in my RecyclerView adapter not clearing on an activity create, but I have't been able to pinpoint it. Here's my fragment class

public class HomeFragment extends Fragment {
    private CustomListAdapter listAdapter;
    private static final String profileUrl = "http://10.0.2.2:3000/apip/buyers/profiles";
    private static final String TAG = selectBuyerProfile.class.getSimpleName();
    private ProgressDialog pDialog;
    private ListView listView;
    private List<BuyerProfile> buyersProfiles = new ArrayList<BuyerProfile>();
    private View root;
    //private RVAdapter recyclerAdapter;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        root = inflater.inflate(R.layout.fragment_home, container, false);
        return root;
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        RecyclerView rv = (RecyclerView) getActivity().findViewById(R.id.rv);
        //rv.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL_LIST));
        rv.setHasFixedSize(true);
        LinearLayoutManager llm = new LinearLayoutManager(getActivity());
        rv.setLayoutManager(llm);
        rv.setItemAnimator(new DefaultItemAnimator());
        final RVAdapter recyclerAdapter = new RVAdapter(buyersProfiles);
        rv.setAdapter(recyclerAdapter);

        RequestQueue mRequestQueue;

        Cache cache = new DiskBasedCache(getActivity().getCacheDir(), 1024 * 1024);

        Network network = new BasicNetwork(new HurlStack());
        mRequestQueue = new RequestQueue(cache, network);
        mRequestQueue.start();


        JsonArrayRequest profileRequest = new JsonArrayRequest(profileUrl,
                new Response.Listener<JSONArray>() {
                    @Override
                    public void onResponse(JSONArray response) {
                        // Parsing json
                        for(int i = 0; i < response.length(); i++) {
                            try {
                                JSONObject obj = response.getJSONObject(i);
                                BuyerProfile parsedProfile = new BuyerProfile();
                                parsedProfile.setBuyerProfTitle(obj.getString("title"));
                                parsedProfile.setDescription(obj.getString("description"));
                                parsedProfile.setLocations(obj.getString("locations"));
                                parsedProfile.setAssetTypes(obj.getString("asset_type"));
                                parsedProfile.setPropertyStatuses(obj.getString("property_status"));
                                //parsedProfile.setBuyerId("Select");
                                buyersProfiles.add(parsedProfile);
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                        recyclerAdapter.notifyDataSetChanged();
                        // notifying list adapter about data changes
                        // so that it renders the list view with updated data
                        //hidePDialog();
                    }
                }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                //Toast.makeText(selectBuyerProfile.this,"Error",Toast.LENGTH_LONG).show();

            }
        });
        mRequestQueue.add(profileRequest);

    }
}

And here is my RVAdapter class

public class RVAdapter extends RecyclerView.Adapter<RVAdapter.PersonViewHolder> {

    private Activity activity;
    private LayoutInflater inflater;
    private List<BuyerProfile> profileItems;
    private static boolean itemFavorited;

    RVAdapter(List<BuyerProfile> profiles) {
        this.profileItems = profiles;
    }

    public static class PersonViewHolder extends RecyclerView.ViewHolder {
        TextView name;
        TextView description;
        TextView locations;
        TextView id;
        TextView investmentRangeMin;
        TextView investmentRangeMax;
        TextView assetTypes;
        TextView propertyStatuses;
        ImageView favoriteButton;
        CardView cardView;
        PersonViewHolder(View itemView) {
            super(itemView);
            name = (TextView) itemView.findViewById(R.id.titleText);
            description = (TextView) itemView.findViewById(R.id.descriptionText);
            investmentRangeMin = (TextView) itemView.findViewById(R.id.investmentRangeMin);
            investmentRangeMax = (TextView) itemView.findViewById(R.id.investmentRangeMax);
            locations = (TextView) itemView.findViewById(R.id.locations);
            id = (TextView) itemView.findViewById(R.id.profileNumber);
            assetTypes = (TextView) itemView.findViewById(R.id.assetTypes);
            propertyStatuses = (TextView) itemView.findViewById(R.id.propertyStatuses);
            favoriteButton = (ImageView) itemView.findViewById(R.id.favorite_select);
            cardView = (CardView) itemView.findViewById(R.id.cv);
            //User selects favorite on a matched profile
            itemFavorited = false;
            favoriteButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if(!itemFavorited) {
                        favoriteButton.setImageResource(R.drawable.ic_favorite);
                        //cardView.setCardBackgroundColor(R.color.colorPrimary);
                        itemFavorited = true;
                    } else {
                        favoriteButton.setImageResource(R.drawable.ic_favorite_border);
                        itemFavorited = false;
                    }
                }
            });
        }
    }

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

    @Override
    public PersonViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
        View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item, viewGroup, false);
        PersonViewHolder pvh = new PersonViewHolder(v);
        return pvh;
    }

    @Override
    public void onBindViewHolder(PersonViewHolder personViewHolder, int i) {
        personViewHolder.name.setText(profileItems.get(i).getBuyerProfTitle());
        personViewHolder.description.setText(profileItems.get(i).getDescription());
        personViewHolder.locations.setText(profileItems.get(i).getLocations());
        personViewHolder.assetTypes.setText(profileItems.get(i).getAssetTypes());
        personViewHolder.propertyStatuses.setText(profileItems.get(i).getPropertyStatuses());
    }

    @Override
    public void onAttachedToRecyclerView(RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);
    }

    /*
    @Override
    public Object getItem(int location) {
        return profileItems.get(location);
    }
    */

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

}

Finally here is my fragments XML

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              xmlns:card_view="http://schemas.android.com/apk/res-auto"
              android:padding="0dp"
    >
            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:padding="8dp"
                >
                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Matches"
                    android:layout_alignParentTop="true"
                    android:layout_alignParentEnd="false"
                    android:id="@+id/matchesText"
                    android:textAlignment="center"
                    android:textSize="20dp"
                    android:textColor="@color/navigationBarColor"
                    android:layout_alignParentStart="false"
                    android:layout_alignParentBottom="false"
                    android:layout_alignParentLeft="false"
                    android:layout_alignParentRight="true"/>

                    <android.support.v7.widget.RecyclerView
                        android:layout_width="match_parent"
                        android:layout_height="match_parent"
                        android:id="@+id/rv"
                        android:layout_below="@id/matchesText"
                        />
                        <!-- Thumbnail Image -->
                        <ImageView
                            android:id="@+id/imgBillionaire"
                            android:src="@drawable/ic_perm_identity"
                            android:layout_width="80dp"
                            android:layout_height="80dp"
                            android:layout_alignParentLeft="true"
                            android:layout_marginRight="8dp"
                            android:layout_alignParentEnd="false"
                            android:layout_alignParentStart="true"
                            android:nestedScrollingEnabled="false"
                            android:visibility="invisible"/>

                        <!-- Name of Asset -->
                        <TextView
                            android:id="@+id/titleText"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:layout_alignTop="@+id/imgBillionaire"
                            android:layout_toRightOf="@+id/imgBillionaire"
                            android:textSize="@dimen/Title"
                            android:textStyle="bold" />

                        <!-- Description -->
                        <TextView
                            android:id="@+id/descriptionText"
                            android:layout_width="fill_parent"
                            android:layout_height="wrap_content"
                            android:layout_below="@id/titleText"
                            android:layout_marginTop="1dip"
                            android:layout_toRightOf="@+id/imgBillionaire"
                            android:textSize="@dimen/Description" />

                        <!-- Source -->
                        <TextView
                            android:id="@+id/locations"
                            android:layout_width="fill_parent"
                            android:layout_height="wrap_content"
                            android:layout_below="@id/descriptionText"
                            android:layout_marginTop="5dp"
                            android:layout_toRightOf="@+id/imgBillionaire"
                            android:textColor="@color/wealthsource"
                            android:textSize="@dimen/InvestmentRange" />

                        <!-- Source -->
                        <TextView
                            android:id="@+id/assetTypes"
                            android:layout_width="fill_parent"
                            android:layout_height="wrap_content"
                            android:layout_below="@id/locations"
                            android:layout_marginTop="5dp"
                            android:layout_toRightOf="@+id/imgBillionaire"
                            android:textColor="@color/wealthsource"
                            android:textSize="@dimen/InvestmentRange" />

                        <!-- Source -->
                        <TextView
                            android:id="@+id/propertyStatuses"
                            android:layout_width="fill_parent"
                            android:layout_height="wrap_content"
                            android:layout_below="@id/assetTypes"
                            android:layout_marginTop="5dp"
                            android:layout_toRightOf="@+id/imgBillionaire"
                            android:textColor="@color/wealthsource"
                            android:textSize="@dimen/InvestmentRange" />

                        <!-- Year -->
                        <TextView
                            android:id="@+id/profileNumber"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:layout_alignParentBottom="false"
                            android:layout_alignParentRight="true"
                            android:textColor="@color/year"
                            android:textSize="@dimen/Date" />
            </RelativeLayout>


</LinearLayout>

Any ideas why the items in my recyclerview would keep duplicating on each page refresh?

Upvotes: 1

Views: 3303

Answers (1)

EricDS
EricDS

Reputation: 486

What can possibly be happening is that each time the network request returns, it tries to add every new item to an already existing list inside the fragment.

Depending on how you implement your navigation logic, your fragment won't be destroyed by the system once you navigate away from it, instead calling a series of lifecycle callbacks:

onPause() -> onStop() -> onDestroyView()

And when you return to the fragment, all required lifecycle callbacks will be called until it reaches the active state.

onCreateView() -> onActivityCreated() -> onStart() -> onResume()

More information on fragment's lifecycle works can be found here: https://developer.android.com/guide/components/fragments.html

Since your fragment is not getting destroyed and recreated, your buyersProfiles reference may hold the previous data when you return. Since the network call is not overwriting the original list, it will append the new data fetched from the network next to the existing data each time it calls it's onResponse callback.

This may also affect use cases where you implement some kind of pull-to-refresh logic or refresh button, as the new network call will not clean the list.

One thing you could try to avoid this repetition is adding

buyersProfiles = new ArrayList<BuyerProfile>();

to the top of your onResponse() callback.

Hope it helps.

Upvotes: 2

Related Questions