Android filterResults() showing duplicate items

287 Views Asked by At

I don't know why I am getting this error. I had implemented filterResults(). Actually, when I search first time in edit text and click on it, it works fine. Then when I come back and search for some city again, it shows 2 same entries. I have added screenshots. Then when I come back and search 3rd time I got 3 same entries for each city searched.

My java class:


package com.wordpress.myselfnikunj.cofighter;

import android.os.Bundle;

import androidx.fragment.app.Fragment;
import androidx.navigation.Navigation;

import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;

import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonArrayRequest;
import com.android.volley.toolbox.Volley;
import com.wordpress.myselfnikunj.cofighter.Adapters.CountryListAdapter;
import com.wordpress.myselfnikunj.cofighter.Model.CountryNamesModel;

import org.json.JSONArray;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.List;

public class AffectedCountriesFragment extends Fragment {

    EditText searchEditText;
    ListView listView;

    public static List<CountryNamesModel> countryNamesModelList = new ArrayList<>();
    CountryNamesModel countryNamesModel;
    CountryListAdapter countryListAdapter;

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

        searchEditText = (EditText) view.findViewById(R.id.searchEditText);
        listView = (ListView) view.findViewById(R.id.countriesListView);
        countryListAdapter = new CountryListAdapter(getContext(), countryNamesModelList);
//        listView.setAdapter(countryListAdapter);

        fetchData();

        searchEditText.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

            }

            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {

                countryListAdapter.getFilter().filter(charSequence);
                countryListAdapter.notifyDataSetChanged();
            }

            @Override
            public void afterTextChanged(Editable editable) {

            }

        });

        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {

                Bundle position = new Bundle();
                position.putInt("position", i);
                Navigation.findNavController(view).navigate(R.id.action_affectedCountriesFragment_to_countryDetailsFragment, position);

            }
        });

        return view;
    }

    private void fetchData() {
        String Url = "https://corona.lmao.ninja/v2/countries/";
        RequestQueue requestQueue = Volley.newRequestQueue(getContext());
        JsonArrayRequest jsonArrayRequest = new JsonArrayRequest(
                Url,
                new Response.Listener<JSONArray>() {
                    @Override
                    public void onResponse(JSONArray response) {
                        Log.i("hehes","entered");
                        try {
                            Log.i("hehe", response.toString());

                            for (int i = 0; i < response.length(); i++) {
                                JSONObject jsonObject = response.getJSONObject(i);
                                String countryName = jsonObject.getString("country");
                                String cases = jsonObject.getString("cases");
                                String todayCases = jsonObject.getString("todayCases");
                                String deaths = jsonObject.getString("deaths");
                                String recovered = jsonObject.getString("recovered");
                                String todayDeaths = jsonObject.getString("todayDeaths");
                                String active = jsonObject.getString("active");
                                String critical = jsonObject.getString("critical");

                                JSONObject object = jsonObject.getJSONObject("countryInfo");
                                String flagUrl = object.getString("flag");

                                countryNamesModel = new CountryNamesModel(getContext(),flagUrl, countryName, cases, todayCases, deaths, todayDeaths, recovered, active, critical);
                                countryNamesModelList.add(countryNamesModel);
                            }

                            countryListAdapter.notifyDataSetChanged();
                            listView.setAdapter(countryListAdapter);

                        }catch (Exception e) {
                            Log.i("error", e.getMessage());
                            e.printStackTrace();
                        }
                    }
                },
                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        Log.i("errors", error.getMessage());
                        Toast.makeText(getContext(), error.getMessage(), Toast.LENGTH_SHORT).show();
                    }
                }
        );

        requestQueue.add(jsonArrayRequest);
    }

My custom Adapter

package com.wordpress.myselfnikunj.cofighter.Adapters;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Filter;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.bumptech.glide.Glide;
import com.wordpress.myselfnikunj.cofighter.AffectedCountriesFragment;
import com.wordpress.myselfnikunj.cofighter.Model.CountryNamesModel;
import com.wordpress.myselfnikunj.cofighter.R;
import com.wordpress.myselfnikunj.cofighter.UpdatesFragment;

import java.util.ArrayList;
import java.util.List;

public class CountryListAdapter extends ArrayAdapter<CountryNamesModel> {

    private Context context;
    private List<CountryNamesModel> countryModelList;
    private List<CountryNamesModel> countryModelListFiltered;

    public CountryListAdapter( Context context, List<CountryNamesModel> countryModelList) {
        super(context, R.layout.countryitem, countryModelList);
        this.context = context;
        this.countryModelList = countryModelList;
        this.countryModelListFiltered = countryModelList;
    }

    @NonNull
    @Override
    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {

        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.countryitem, null, true);
        TextView tvCountryName = view.findViewById(R.id.countryName);
        ImageView imageView = view.findViewById(R.id.countryImageFlag);

        tvCountryName.setText(countryModelListFiltered.get(position).getCountry());
        Glide.with(context).load(countryModelListFiltered.get(position).getFlag()).into(imageView);

        return view;
    }

    @Override
    public int getCount() {
        return countryModelListFiltered.size();
    }

    @Nullable
    @Override
    public CountryNamesModel getItem(int position) {
        return countryModelListFiltered.get(position);
    }

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

    @NonNull
    @Override
    public Filter getFilter() {
         Filter filter = new Filter() {
            @Override
            protected FilterResults performFiltering(CharSequence charSequence) {
                FilterResults filterResults = new FilterResults();
                if (charSequence == null
                        || charSequence.length() == 0
                ) {
                    filterResults.count = countryModelList.size();
                    filterResults.values = countryModelList;
                } else {
                    List<CountryNamesModel> resultsModel = new ArrayList<>();
                    String searchStr = charSequence.toString().toLowerCase();

                    for (CountryNamesModel itemsModel: countryModelList) {
                        if(itemsModel.getCountry().toLowerCase().contains(searchStr)) {
                            resultsModel.add(itemsModel);
                        }
                        filterResults.count = resultsModel.size();
                        filterResults.values = resultsModel;
                    }
                }
                return  filterResults;
            }

            @Override
            protected void publishResults(CharSequence charSequence, FilterResults results) {
                countryModelListFiltered = (List<CountryNamesModel>) results.values;
                UpdatesFragment.countryNamesModelList = (List<CountryNamesModel>) results.values;
                //AffectedCountriesFragment.countryNamesModelList = (List<CountryNamesModel>) results.values;
                notifyDataSetChanged();
            }
        };
        return filter;
    }
}

Screenshots

Second Time

Third Time

1

There are 1 best solutions below

2
On BEST ANSWER

When you navigate to another fragment view of the AffectedCountriesFragment getting destroyed and public void onDestroyView() is called. When you return back public View onCreateView is called again. But your fragment was not totally destroyed and all of its variables still exist and are in the state they were left.

It means that when you navigate to the next fragment your countryNamesModelList stays filled and when you return back to the AffectedCountriesFragment the same filled list getting filled again resulting in duplicated entries.

What you can do?

Track the state of your fragment. Create a new class-level variable private View view; that will hold the view of your fragment.

    // If null - must be initialized and returned in `onCreateView`
    // If not null - must be returned in `onCreateView` immediately
    // avoiding recreation of the same view (performance improvement)
    private View view;
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        if (view == null) {
            // Inflate the layout for this fragment ONLY IF IT IS NULL
            View view = inflater.inflate(R.layout.fragment_affected_countries, container, false);

            searchEditText = (EditText) view.findViewById(R.id.searchEditText);
            listView = (ListView) view.findViewById(R.id.countriesListView);
            countryListAdapter = new CountryListAdapter(getContext(), countryNamesModelList);
    //        listView.setAdapter(countryListAdapter);

            fetchData();

            searchEditText.addTextChangedListener(new TextWatcher() {
                @Override
                public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

                }

                @Override
                public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {

                    countryListAdapter.getFilter().filter(charSequence);
                    countryListAdapter.notifyDataSetChanged();
                }

                @Override
                public void afterTextChanged(Editable editable) {

                }

            });

            listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                @Override
                public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {

                    Bundle position = new Bundle();
                    position.putInt("position", I);
                    Navigation.findNavController(view).navigate(R.id.action_affectedCountriesFragment_to_countryDetailsFragment, position);

                }
            });
        }

        return view;
    }

Make sure you are not missing something in an understanding of Activity and Fragment lifecycles. Here is a good example of their lifecycles.