使用 SQLite 的搜索结果更新 RecyclerView

Posted

技术标签:

【中文标题】使用 SQLite 的搜索结果更新 RecyclerView【英文标题】:Update RecyclerView with search results from SQLite 【发布时间】:2020-08-18 06:25:35 【问题描述】:

我在使用 SearchView 结果更新 RecyclerView 时遇到了问题。 进行实际搜索似乎工作正常,因为我已经在“performFiltering”中逐步完成了代码,并且“publishResults”具有正确的列表。但我似乎无法弄清楚如何更新 RecyclerView。我已经盯着这段代码 3 天了,这可能是一个我忽略的简单解决方案。

我的电影片段

public class MyMoviesFragment extends Fragment 

private SearchView searchView = null;
private SearchView.OnQueryTextListener queryTextListener;

private static final boolean DEFAULT_SORT_ORDER_IS_ASC = false;
private static final int MY_MOVIES_LOADER_ID = 173201;

private ArrayList<Movie> _movies = new ArrayList();
private ArrayList<Movie> _moviesRaw = new ArrayList();
private Button _btnMoviesNotFound;
private DBHandler _repository;
private MyMoviesAdapter _myMoviesAdapter;
private ProgressBar _progressBar;
private RecyclerView _rvMovies;
private SharedPreferences _preferences;
private View _view;

public MyMoviesFragment() 
    // Required empty public constructor


@Override
public void onCreate(@Nullable Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);
    setHasOptionsMenu(true);


@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) 
    //        inflater.inflate(R.menu.main, menu);
    MenuItem searchItem = menu.findItem(R.id.action_search);
    SearchManager searchManager = (SearchManager) getActivity().getSystemService(Context.SEARCH_SERVICE);

    if (searchItem != null) 
        searchView = (SearchView) searchItem.getActionView();
    
    if (searchView != null) 
        searchView.setSearchableInfo(searchManager.getSearchableInfo(getActivity().getComponentName()));

        queryTextListener = new SearchView.OnQueryTextListener() 
            @Override
            public boolean onQueryTextChange(String newText) 
                Log.i("onQueryTextChange", newText);
                _myMoviesAdapter.getFilter().filter(newText);
                return true;
            

            @Override
            public boolean onQueryTextSubmit(String query) 
                Log.i("onQueryTextSubmit", query);

                return true;
            
        ;
        searchView.setOnQueryTextListener(queryTextListener);
    
    super.onCreateOptionsMenu(menu, inflater);


@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 
    // Inflate the layout for this fragment
    this._view = inflater.inflate(R.layout.fragment_my_movies, container, DEFAULT_SORT_ORDER_IS_ASC);
    getActivity().setTitle(R.string.movies_fragment_title);
    this._repository = DBHandler.getInstance(getActivity());
    this._progressBar = (ProgressBar) this._view.findViewById(R.id.progressBar2);
    this._progressBar.setVisibility(View.VISIBLE);
    this._rvMovies = (RecyclerView) this._view.findViewById(R.id.rvMovies);
    this._rvMovies.setLayoutManager(new LinearLayoutManager(getActivity()));
    this._myMoviesAdapter = new MyMoviesAdapter(this._movies, this);
    this._rvMovies.setAdapter(this._myMoviesAdapter);
    this._btnMoviesNotFound = (Button) this._view.findViewById(R.id.btnMoviesNotFound);
    if (savedInstanceState == null) 
        loadMyMoviesList();
    
    return this._view;


public void loadMyMoviesList() 

    getLoaderManager().initLoader(MY_MOVIES_LOADER_ID, null, new LoaderManager.LoaderCallbacks<ArrayList<Movie>>() 

        @NonNull
        @Override
        public Loader<ArrayList<Movie>> onCreateLoader(int id, Bundle args) 
            return new MyMoviesLoader(MyMoviesFragment.this.getContext());
        

        public void onLoadFinished(Loader<ArrayList<Movie>> loader, ArrayList<Movie> movies) 

            if (movies != null) 
                MyMoviesFragment.this._movies.clear();
                Iterator it = movies.iterator();
                while (it.hasNext()) 
                    MyMoviesFragment.this._movies.add((Movie) it.next());
                
                MyMoviesFragment.this._moviesRaw = new ArrayList(MyMoviesFragment.this._movies);
                MyMoviesFragment.this._myMoviesAdapter.notifyDataSetChanged();
                MyMoviesFragment.this.displayListState();
            
        

        public void onLoaderReset(Loader<ArrayList<Movie>> loader) 
        
    ).forceLoad();


public void displayListState() 
    this._progressBar.setVisibility(View.GONE);
    if (this._movies.size() < 1) 
        this._rvMovies.setVisibility(View.GONE);
        this._btnMoviesNotFound.setVisibility(View.VISIBLE);
        return;
    
    this._btnMoviesNotFound.setVisibility(View.GONE);
    this._rvMovies.setVisibility(View.VISIBLE);

MyMoviesAdapter

public class MyMoviesAdapter extends RecyclerView.Adapter<MyMoviesAdapter.ViewHolder> implements Filterable 

private Activity _activity;
private ArrayList<Movie> _movieListFull;
private ArrayList<Movie> _movieListFiltered;
private DBHandler _repository = DBHandler.getInstance(this._activity);
private Fragment _fragment;

public MyMoviesAdapter(ArrayList<Movie> movies, Fragment fragment) 
    this._activity = fragment.getActivity();
    this._fragment = fragment;
    this._movieListFull = movies;


protected class ViewHolder extends RecyclerView.ViewHolder 

    private CardView cardMovie;
    private TextView lblMovieTitle;

    public ViewHolder(View itemView) 
        super(itemView);

        this.cardMovie = (CardView) itemView.findViewById(R.id.cardMovie);
        this.lblMovieTitle = (TextView) itemView.findViewById(R.id.lblMovieTitle);
    


// Create new views (invoked by the layout manager)
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) 
    return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_movie, parent, false));


// Replace the contents of a view (invoked by the layout manager)
// Binds the data to the Views in each row
@Override
public void onBindViewHolder(ViewHolder viewHolder, int position) 
    // Get the data model based on position
    Movie movie = _movieListFull.get(position);

    viewHolder.lblMovieTitle.setText(String.valueOf(movie.getTitle()));


// Return the size of your dataset (invoked by the layout manager)
@Override
public int getItemCount() 
    return this._movieListFull.size();


@Override
public Filter getFilter() 

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

            String charString = charSequence.toString();

            if (charString.isEmpty()) 
                _movieListFiltered = _movieListFull;
             else 
                ArrayList<Movie> filteredList = new ArrayList<>();

                for (Movie movie : _movieListFull) 
                    if (movie.getTitle().toLowerCase().contains(charString)) 
                        filteredList.add(movie);
                    
                
                _movieListFiltered = filteredList;
            

            FilterResults filterResults = new FilterResults();
            filterResults.values = _movieListFiltered;
            filterResults.count = _movieListFiltered.size();
            return filterResults;
        

        @Override
        protected void publishResults(CharSequence charSequence, FilterResults filterResults) 
            _movieListFiltered = (ArrayList<Movie>) filterResults.values;
            notifyDataSetChanged();
        
    ;

【问题讨论】:

【参考方案1】:

如果 _movieListFull 是完整数据集,则应使用 _movieListFiltered 进行显示。因此 getItemCount()onBindViewHolder() 应该参考 _movieListFiltered

尝试以下更改:

public MyMoviesAdapter(ArrayList<Movie> movies, Fragment fragment) 
    this._activity = fragment.getActivity();
    this._fragment = fragment;
    this._movieListFiltered = movies;
    _movieListFull = new ArrayList<>();
    _movieListFull.addAll(movies);


// Replace the contents of a view (invoked by the layout manager)
// Binds the data to the Views in each row
@Override
public void onBindViewHolder(ViewHolder viewHolder, int position) 
    // Get the data model based on position
    Movie movie = _movieListFiltered.get(position);

    viewHolder.lblMovieTitle.setText(String.valueOf(movie.getTitle()));


// Return the size of your dataset (invoked by the layout manager)
@Override
public int getItemCount() 
    return this._movieListFiltered.size();


        @Override
        protected FilterResults performFiltering(CharSequence charSequence) 

            String charString = charSequence.toString();

            if (charString.isEmpty()) 
                //_movieListFiltered = _movieListFull;
                _movieListFiltered.addAll(_movieListFull);
             else 
                ArrayList<Movie> filteredList = new ArrayList<>();

                for (Movie movie : _movieListFull) 
                    if (movie.getTitle().toLowerCase().contains(charString)) 
                        filteredList.add(movie);
                    
                
                _movieListFiltered = filteredList;
            

            FilterResults filterResults = new FilterResults();
            filterResults.values = _movieListFiltered;
            filterResults.count = _movieListFiltered.size();
            return filterResults;
        

希望有帮助!

【讨论】:

以上是关于使用 SQLite 的搜索结果更新 RecyclerView的主要内容,如果未能解决你的问题,请参考以下文章

使用SQLite的搜索结果更新RecyclerView

如何使用搜索和替换查询更新 SQLite 数据库?

Apollo GraphQL 突变结果不使用 PostgreSQL 更新,但适用于 SQLite

Xamarin.Forms Sqlite(有时)在更新后获取错误结果

Sqlite的多表连接更新

SQLite 中的更新数据不更新