首页 > 解决方案 > 用于搜索查询的 Android 分页

问题描述

我是 android 开发的新手,正在尝试为我的应用程序构建 UI。该应用程序与 REST 后端集成,后者接受搜索查询和项目列表作为响应。

interface RetrofitEndpoint {

    @GET("paged/list/endpoint")
    Call<PagedList<Object>> getPagedList(@Query("query") String query, @Query("pageSize") int pageSize, Query("pageOffset") int pageOffset);
}

UI 一次向用户显示一个项目。

我正在将列表加载到 recyclerview

public class SomeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private List<Object> list;


    // .. other overridden members

    public void setList(List<Object> list) {
        this.list = list;
        notifyDataSetChanged();
    }

    public void addAll(List<Object> newList) {
        int lastIndex = list.size() - 1;
        list.addAll(newList);
        notifyItemRangeInserted(lastIndex, newList.size());
    }
}

我无法弄清楚的部分是当我到达回收站视图的末尾(或之前以避免延迟)时如何加载更多数据,是否有任何库/API可以做到这一点?

标签: androidandroid-recyclerviewpaginationretrofit2android-paging

解决方案


要使分页列表正常工作,它需要在您的应用程序中添加更多内容。此实现使用视图模型、实时数据和房间持久性,因此它可以离线工作。

你的 build.gradle:

// ViewModel
def lifecycle_version = "2.2.0"
implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"
// LiveData
implementation "androidx.lifecycle:lifecycle-livedata:$lifecycle_version"

def room_version = "2.2.5"

implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"

def paging_version = "2.1.2"
implementation "androidx.paging:paging-runtime:$paging_version"    

改造api:

interface RetrofitEndpoint {

    @GET("paged/list/endpoint")
    Call<List<YourObject>> getYourObjectList(@Query("query") String query, @Query("pageSize") int pageSize, Query("pageOffset") int pageOffset);
}

你的对象:

@Entity(tableName = "your_object")
public class YourObject implements Serializable {

@PrimaryKey(autoGenerate = true)
private int db_id;
...

道:

@Dao
public interface YourObjectDao{

    /**
     * Get your objects from the table.
     * -------------------------------
     * We  get update every time the database update.
     * 
     *
     * @return your object from the table
     */

    @Insert
    void insert(YourObject yourObject);

    @Insert
    void insertList(List<YourObject> yourObjectList);

    @Query("SELECT * FROM your_object")
    DataSource.Factory<Integer, YourObject> getAllResults();

    @Query("DELETE FROM your_object")
    void deleteAll();

}

数据库:

@androidx.room.Database(entities = {YourObject.class}, version = 1)
public abstract class Database extends RoomDatabase {

private static Database instance;
public abstract YourObjectDao get_your_object_dao();

public static synchronized Database getInstance(Context context) {
    if (instance == null) {
        instance = Room.databaseBuilder(context.getApplicationContext(),
                Database.class, DATABASE_NAME)
                .fallbackToDestructiveMigration()
                .addCallback(roomCallback)
                .build();
    }
    return instance;
}
}

YourObjectBoundaryCallback:

public class YourObjectBoundaryCallback extends PagedList.BoundaryCallback<YourObject> {
    private AppExecutors executors;
    private Database database;
    private YourObjectDao dao;
    private Integer page_number;

    public YourObjectBoundaryCallback (Application application, AppExecutors executors) {
        //super();
        this.executors = executors;
        database = Database.getInstance(application);
        dao = database.get_your_object_dao();
        page_number=1;
    }

    @Override
    public void onZeroItemsLoaded() {
        super.onZeroItemsLoaded();
        Log.d("log", "yourObjects onzeroitemsloaded");
        fetchYourObjects(page_number);
    }

    @Override
    public void onItemAtFrontLoaded(@NonNull YourObject itemAtFront) {
        super.onItemAtFrontLoaded(itemAtFront);
        Log.d("log", "yourObjects onItemAtFrontLoaded");
    }

    @Override
    public void onItemAtEndLoaded(@NonNull YourObject itemAtEnd) {
        super.onItemAtEndLoaded(itemAtEnd);
        Log.d("log", "yourObjects onItemAtEndLoaded");
        page_number=page_number+1;
        fetchYourObjects(page_number);
    }

    public void fetchYourObjects(int pageNumber) {
        RetrofitApi retrofitApi = RetrofitInstance.getRetrofitEndpoint();
        Call<List<YourObject>> call = retrofitApi.getYourObjectList(query, pageSize,pageNumber);
        call.enqueue(new Callback<List<YourObject>>() {
            @Override
            public void onResponse(Call<List<YourObject>> call, Response<List<YourObject>> response) {
                if (!response.isSuccessful()) {
                    Log.d("log", "YourObjects Response unsuccesful: " + response.code());
                    return;
                }
                Log.d("log", "YourObjects Response ok: " + response.code());
                List<YourObject> yourObjectsList = response.body();
                insertListToDb(yourObjectsList );
            }

            @Override
            public void onFailure(Call<List<YourObject>> call, Throwable t) {
                Log.d("log", "yourObjects onFailure: " + t.getMessage());
            }
        });
    }


    public void insertListToDb(List<YourObject> list) {
        Runnable runnable = () -> {
            dao.insertList(list);
        };
        Runnable diskRunnable = () -> database.runInTransaction(runnable);
        executors.diskIO().execute(diskRunnable);
    }
    }

YourObjects 存储库:

public class YourObjectsRepository {
private LiveData<PagedList<YourObject>> yourObjectsPagedList;
private YourObjectBoundaryCallback yourObjectsBoundaryCallback;
private AppExecutors executors;


public YourObjectsRepository (Application application, AppExecutors executors) {
    this.executors = executors;
    Database database = Database.getInstance(application);
    YourObjectDao dao = database.get_your_object_dao();
    yourObjectsBoundaryCallback= new YourObjectBoundaryCallback (application, executors);
    createYourObjectsPagedList(dao );

}

//this is configuration for your paged list, adjust per your requirements
private PagedList.Config getPagedListConfig(){
    return  (new PagedList.Config.Builder())
            .setEnablePlaceholders(false)
            .setPrefetchDistance(40)
            .setInitialLoadSizeHint(60)
            .setPageSize(20).build();
}

private void createYourObjectsPagedList(YourObjectDao dao){
    yourObjectsPagedList= new LivePagedListBuilder<>(dao.getAllResults(), getPagedListConfig())
      .setBoundaryCallback(yourObjectsBoundaryCallback).setFetchExecutor(executors.networkIO())
            .build();
}

public LiveData<PagedList<YourObject>> getYourObjectsPagedList() {
    return yourObjectsPagedList;
}
}

你的对象视图模型:

public class YourObjectsViewModel extends AndroidViewModel {

    private YourObjectsRepository repo;

    public YourObjectsViewModel (@NonNull Application application) {
        super(application);
        AppExecutors  executors = new AppExecutors();
        repo= new YourObjectsRepository (application, executors);
    }


    public LiveData<PagedList<YourObject>> getYourObjectsPagedList() {
        return repo.getYourObjectsPagedList();
    }
}

应用程序执行者:

public class AppExecutors {
private final Executor diskIO;
private final Executor networkIO;
private final Executor mainThread;
private final Executor others;
private final Executor paging;

    public AppExecutors(Executor diskIO, Executor networkIO, Executor mainThread, Executor others, Executor paging) {
        this.diskIO = diskIO;
        this.networkIO = networkIO;
        this.mainThread = mainThread;
        this.others = others;
        this.paging = paging;
    }
    public AppExecutors() {
        this(Executors.newSingleThreadExecutor(), Executors.newFixedThreadPool(3),
                new MainThreadExecutor(), Executors.newSingleThreadExecutor(),
                Executors.newFixedThreadPool(4));
    }

    public Executor diskIO() {
        return diskIO;
    }

    public Executor networkIO() {
        return networkIO;
    }

    public Executor mainThread() {
        return mainThread;
    }

    public Executor others() {
        return others;
    }

    public Executor paging() {
        return paging;
    }

    private static class MainThreadExecutor implements Executor {
        private Handler mainThreadHandler = new Handler(Looper.getMainLooper());
        @Override
        public void execute(@NonNull Runnable command) {
            mainThreadHandler.post(command);
        }
    }
}

在您的活动/片段中:

yourObjectsViewModel = new ViewModelProvider(this, ViewModelProvider.AndroidViewModelFactory.getInstance(getActivity().getApplication())).get(YourObjectsViewModel.class);
yourObjectsViewModel.getYourObjectPagedList().observe(getViewLifecycleOwner(), new Observer<PagedList<TopRatedMovie>>() {
        @Override
        public void onChanged(PagedList<YourObject> results) {
            Log.d("log", "  onChanged list size: " + results.size());
            yourAdapter.submitList(results);
           
        }
    });

在您的适配器中:

 public class YourPagedListAdapter extends PagedListAdapter<YourObject, 
 RecyclerView.ViewHolder> {

如果您有任何问题,请随时提问。


推荐阅读