Search code examples
javaandroidandroid-studioandroid-fragmentsandroid-viewpager

ViewPager Cache is making SearchView results appear weird


Little Context

I have a SearchView along with a ViewPager in activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/constraintLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".activity.MainActivity">

    <androidx.appcompat.widget.SearchView
        android:id="@+id/homeSearchView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"
        app:layout_constraintBottom_toTopOf="@id/viewPager"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <androidx.viewpager.widget.ViewPager
        android:id="@+id/viewPager"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toTopOf="@id/homeMenuIcon"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/homeSearchView" />


    <ImageView
        android:id="@+id/homeMenuIcon"
        android:layout_width="36dp"
        android:layout_height="36dp"
        android:layout_margin="8dp"
        android:contentDescription="Overflow Menu Icon"
        android:scaleType="fitXY"
        android:src="@drawable/ic_menu"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@id/viewPager"
        tools:ignore="HardcodedText" />
</androidx.constraintlayout.widget.ConstraintLayout>

I've attached OnQueryTextListener to SearchView in MainActivity.java


ViewPager viewPager = findViewById(R.id.viewPager);
List<Quote> allQuotesList = getQuotes();//From `Volley` Request
QuoteViewPagerAdapter adapter = new QuoteViewPagerAdapter(getSupportFragmentManager(), allQuotesList);
viewPager.setAdapter(adapter);


SearchView searchView = findViewById(R.id.homeSearchView);
        searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                getFilter().filter(query);
                return false;
            }

            @Override
            public boolean onQueryTextChange(String newText) {
                getFilter().filter(newText);
                return false;
            }
        });

 private Filter getFilter() {
        return new Filter() {
            @Override
            protected FilterResults performFiltering(CharSequence constraint) {
                ArrayList<Quote> filteredResults = new ArrayList<>();

                if (constraint.toString().isEmpty())
                    filteredResults.addAll(allQuotesList);
                else
                    for (Quote quote : allQuotesList) {
                        if (quote.getQuote().toLowerCase().contains(constraint.toString().toLowerCase())
                                || quote.getAuthor().toLowerCase().contains(constraint.toString().toLowerCase()))
                            filteredResults.add(quote);
                    }

                FilterResults filterResult = new FilterResults();
                filterResult.values = filteredResults;

                return filterResult;
            }

            @Override
            protected void publishResults(CharSequence constraint, FilterResults results) {
                adapter.setQuoteList((List<Quote>) results.values);
                adapter.notifyDataSetChanged();
            }
        };
    }

Here's QuoteViewPagerAdapter.java

public class QuoteViewPagerAdapter extends FragmentPagerAdapter {
    private List<Quote> quoteList;

    public QuoteViewPagerAdapter(FragmentManager fm, List<Quote> fragmentList) {
        super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
        quoteList = fragmentList;
    }

    @androidx.annotation.NonNull
    @Override
    public Fragment getItem(int position) {
        return QuoteFragment.newInstance(quoteList.get(position));
    }

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

    @Override
    public int getItemPosition(@androidx.annotation.NonNull Object object) {
        return POSITION_NONE;
    }

    public void setQuoteList(List<Quote> quoteList) {
        this.quoteList = quoteList;
    }
}

Here's Quote.java

public class Quote {

    private String quote;
    private String author;

    public Quote() {
    }

    public Quote(String quote, String author) {
        this.quote = quote;
        this.author = author;
    }

    public String getQuote() {
        return quote;
    }

    public void setQuote(String quote) {
        this.quote = quote;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }
}

The Problem

When something is searched the quoteList (List<Quote>) in QuoteViewPagerAdapter is updated with setQuoteList(). So, getCount() returns the new size.

But, the old fragments (2 of them) remain in the ViewPager. This makes the search results appear after scrolling the ViewPager 2 times. Also, this causes the last 2 results to be omitted.

Also, if the size of there is only 1 search result, the ViewPager freezes making scrolling impossible (because getCount() returns 1 so ViewPager is showing only the old first fragment)

What I have Tried

//inside publishResults()
adapter = null;
viewPager.setAdapter(null);
viewPager.removeAllViews();
viewPager.removeAllViewsInLayout();
viewPager.destroyDrawingCache();

adapter = new QuoteViewPagerAdapter(getSupportFragmentManager(), (List<Quote>) results.values);
viewPager.setAdapter(adapter);
adapter.notifyDataSetChanged();

//inside onCreate()

viewPager.setSaveFromParentEnabled(false);
viewPager.setSaveEnabled(false);

The Expected Behaviour

The ViewPager displays the new list from search results from position 0 onwards

Looking forward to help from the StackOverflow community. Thanks in advance.


Solution

  • It works as expected by changing FragmentPagerAdapterto FragmentStatePagerAdapter in QuoteViewPagerAdapter.java

    A bit late but, writing this answer for future readers...