Search code examples
android-studioandroid-activityandroid-recyclerview

Why does my activity crash when trying to search recycler view?


I'm having a problem where I want to display a circular loading progress bar for when the recycler view is loading and only display recyler view when it is fully loaded, because when I search for an item in recycler view it crashes giving me the error:

java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 1(offset:1).state:8077 androidx.recyclerview.widget.RecyclerView{e6252d0 VFED....

The only way to not get the crash is to wait for around 4 seconds and scroll a little bit till it fully loads. And activity only opens when it can visibly display recycler view which also gives it a delay.

So how do I fix this error, maybe by displaying recycler view and search bar only after it is fully loaded?

    private void getAllStocks() {

        try {

            String[] nasdaqLine;
            String[] nyseLine;
           while ((nasdaqLine = nasdaqReader.readNext()) != null){

                nasdaqSymbol = nasdaqLine[0];
                nasdaqFullName = nasdaqLine[1];
                sector = nasdaqLine[5];
                industry = nasdaqLine[6];


                stocksList.add(new StocksList(nasdaqSymbol, nasdaqFullName, sector, industry));


            }

            while ((nyseLine = nyseReader.readNext()) != null){

                nyseSymbol = nyseLine[0];
                nyseFullName = nyseLine[1];


                stocksList.add(new StocksList(nyseSymbol, "", nyseFullName, null));

            }


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

}

This is how I populate my recyler view.

    getAllStocks();


        layoutManager = new LinearLayoutManager(this, RecyclerView.VERTICAL, false);
        adapter = new StocksListAdapter(stocksList);

        recyclerView.setLayoutManager(layoutManager);
        recyclerView.setAdapter(adapter);

        if (stocksList.size() == 8893) {
            adapter.notifyDataSetChanged();
            circularProgressBar.setVisibility(View.GONE);
            recyclerView.setVisibility(View.VISIBLE);

        }

And this is how I try to only show it after it fully loads but activity only opens after it can visibly display recycler view.

public Filter getFilter() {
        return new Filter() {
            @Override
            protected FilterResults performFiltering(CharSequence charSequence) {

                String Key = charSequence.toString();
                if (Key.isEmpty()){

                    stocksListFilterd = stocksList;

                } else {

                    ArrayList<StocksList> listFiltered = new ArrayList<>();

                    for (StocksList stock : stocksList){

                        if (stock.getFullName().toLowerCase().contains(Key.toLowerCase()) || stock.getSymbol().toLowerCase().contains(Key.toLowerCase())
                                || stock.getSector().toLowerCase().contains(Key.toLowerCase())){
                            listFiltered.add(stock);
                        }
                    }

                    stocksListFilterd = listFiltered;

                }

                FilterResults filterResults = new FilterResults();
                filterResults.values = stocksListFilterd;
                return filterResults;
            }

            @Override
            protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
                stocksListFilterd = (ArrayList<StocksList>) filterResults.values;
                notifyDataSetChanged();
            }
        };
    }

And this is the filter class in my adapter


Solution

  • Figured out how to fix error by another video and it is pretty much just a more simple Filter rewrite

    Where I populate my recycler view all I did was add this

      while ((nyseLine = nyseReader.readNext()) != null){
    
                    nyseSymbol = nyseLine[0];
                    nyseFullName = nyseLine[1];
    
    
                    stocksList.add(new StocksList(nyseSymbol, "", nyseFullName, null));
    
                }
    
                //I added this to set the adapter and show the list immediately after 
                //loading   
                adapter = new StocksListAdapter(stocksList);
                recyclerView.setAdapter(adapter);
                adapter.notifyDataSetChanged();
    

    Then I crated this method in The SAME activity not in the adapter class

     private void Filter(String searched) {
    
            for (StocksList stock : stocksList ){
                if (stock.getSymbol().toLowerCase().equals(searched)){
                    //created new array list for the filter       
                    filteredList.add(stock);
                }
            }
            recyclerView.setAdapter(new StocksListAdapter(filteredList));
            adapter.notifyDataSetChanged();
    
        }
    

    And this is the OnCreate class editText listener

     recyclerView.setLayoutManager(new LinearLayoutManager(this));
            recyclerView.setHasFixedSize(true);
    
            searchStocks.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) {
    
                }
    
                //Moved the the stuff to afterTextChanged not onTextChanged        
                @Override
                public void afterTextChanged(Editable editable) {
    
                    filteredList.clear();
    
                    if (editable.toString().isEmpty()){
                        recyclerView.setAdapter(new StocksListAdapter(stocksList));
                        adapter.notifyDataSetChanged();
                    } else {
                        Filter(editable.toString());
                    }
                }
            });
    

    I'm pretty sure the error was the fact that I set all the recycler view values like recyclerview.setAdapter(new StocksListAdapter(stocksList) in one method that I called once in the oncreate method And when it needed to update the adapter with a new filtered list adapter.notifyDataSetChanged() was unreachable and didn't do anything.

    And so inside the adapter class it was working with inconsistent lists back and forth thus crashing. This is only my guess though, and the comment from @shagberg really helped.

    P.S anyone else having this problem in the future: check your notifyDataSetChanged() calls so they aren't after Filter calls or other data changing methods because if it's called in the wrong place it crashes.