Подгрузка списка RecyclerView
В моем APi есть параметр offset, который отвечает за сдвиг элементов от начальной точки. Так же есть параметр limit, который отвечает за получаемую порцию данных. То есть при первоначальном запросе эти параметры выглядят так limit=20&offset=0. Мне нужно при скроллинге моего списка добавлять +20 к offset каждый раз, когда прокрутка достигнет последнего элемента. Находил множество вопросов по пагинации с ответами, но не смог прикрутить к моему проекту
Метод, который отвечает за погрузку
Call<MainModel> loadMore(
@Query("offset") int limit
);
Так как мне вызвать этот метод и где его вызывать? В активити?
Вот listenter (не заполнен до конца)
public class PaginationScrollListener extends RecyclerView.OnScrollListener {
LinearLayoutManager linearLayoutManager;
int visibleItemCount, totalItemCount, firstVisiblePosition;
public PaginationScrollListener(LinearLayoutManager linearLayoutManager) {
this.linearLayoutManager = linearLayoutManager;
}
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
visibleItemCount = linearLayoutManager.getChildCount();
totalItemCount = linearLayoutManager.getItemCount();
firstVisiblePosition = linearLayoutManager.findFirstVisibleItemPosition();
}
}
ListCatalogActivity
public class ListCatalogActivity extends AppCompatActivity implements AdapterView.OnItemSelectedListener, ListCatalog {
private ListCatalogPresenter presenter;
private Toolbar toolbarCatalogList;
private RecyclerView recyclerUpper, recyclerMain;
private Spinner spinner;
private String sortBy, sortType;
private ProgressBar progressBar;
private CategoryAdapter categoryAdapter;
private CatalogListAdapter catalogListAdapter;
private CompositeDisposable compositeDisposable = new CompositeDisposable();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list_catalog);
initView();
//Получаю список категорий
presenter = new ListCatalogPresenter();
presenter.attachView(this);
presenter.getCategories();
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
int id = item.getItemId();
if (id == android.R.id.home) {
finish();
}
return super.onOptionsItemSelected(item);
}
@Override
protected void onStop() {
super.onStop();
}
//Получаю список товаров
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
parent.getItemAtPosition(position);
switch (position) {
//Список товаров с сортировкой от дешевых к дорогим
case 0:
sortBy = "price";
sortType = "asc";
presenter.getList(sortBy, sortType);
break;
//Список товаров с сортировкой от дорогих к дешевым
case 1:
sortBy = "price";
sortType = "desc";
presenter.getList(sortBy, sortType);
break;
//Список товаров с сортировкой по популярности
case 2:
sortBy = "rating";
sortType = "desc";
presenter.getList(sortBy, sortType);
break;
}
}
@Override
public void onNothingSelected(AdapterView<?> parent) {}
@Override
public void getCategories(MainModel mainModel) {
categoryAdapter = new CategoryAdapter(ListCatalogActivity.this, mainModel);
recyclerUpper.setAdapter(categoryAdapter);
}
@Override
public void getList(MainModel mainModel, String sortBy, String sortType) {
catalogListAdapter = new CatalogListAdapter(ListCatalogActivity.this, mainModel);
recyclerMain.setAdapter(catalogListAdapter);
}
@Override
public void deleteCompositeDisposable(CompositeDisposable compositeDisposable) {
this.compositeDisposable = compositeDisposable;
presenter.deleteCompositeDisposable(compositeDisposable);
}
@Override
public void showProgress(boolean visibility) {
progressBar.setVisibility(visibility ? View.VISIBLE : View.GONE);
}
private void initView() {
//Toolbar
toolbarCatalogList = findViewById(R.id.toolbar_catalog);
setSupportActionBar(toolbarCatalogList);
getSupportActionBar().setDisplayShowHomeEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
//ProgressBar
progressBar = findViewById(R.id.progress_bar);
//Верхний горизонтальный recycler
recyclerUpper = findViewById(R.id.recycler_upper);
recyclerUpper.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
//Основной вертикальный recycler
recyclerMain = findViewById(R.id.recycler_main);
recyclerMain.setLayoutManager(new GridLayoutManager(this, 2));
//Выпадающее меню
spinner = findViewById(R.id.spinner);
ArrayAdapter<?> adapter = ArrayAdapter.createFromResource(this, R.array.spinner_items, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
spinner.setSelection(2);
spinner.setOnItemSelectedListener(this);
}
public void closeListCatalogActivity(View view){
finish();
}
}
Ответы (2 шт):
Google выпустила очень неплохой инструмент - Paging library. Ставите в зависимости вот такую штуку:
implementation "android.arch.paging:runtime:1.0.0"
Создаете адаптер аналогично такому:
class EmployeeAdapter extends PagedListAdapter<Employee, EmployeeViewHolder> {
protected EmployeeAdapter(DiffUtil.ItemCallback<Employee> diffUtilCallback) {
super(diffUtilCallback);
}
@NonNull
@Override
public EmployeeViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.employee, parent, false);
EmployeeViewHolder holder = new EmployeeViewHolder(view);
return holder;
}
@Override
public void onBindViewHolder(@NonNull EmployeeViewHolder holder, int position) {
holder.bind(getItem(position));
}
}
После чего создаете PagedList с помощью такого билдера:
PagedList<Employee> pagedList = new PagedList.Builder<>(dataSource, config)
.setBackgroundThreadExecutor(Executors.newSingleThreadExecutor())
.setMainThreadExecutor(new MainThreadExecutor())
.build();
Вам так же нужно создать DataSource. DataSource - это посредник между PagedList и Storage.
class MyPositionalDataSource extends PositionalDataSource<Employee> {
private final EmployeeStorage employeeStorage;
public MyPositionalDataSource(EmployeeStorage employeeStorage) {
this.employeeStorage = employeeStorage;
}
@Override
public void loadInitial(@NonNull LoadInitialParams params, @NonNull LoadInitialCallback<Employee> callback) {
Log.d(TAG, "loadInitial, requestedStartPosition = " + params.requestedStartPosition +
", requestedLoadSize = " + params.requestedLoadSize);
List<Employee> result = employeeStorage.getData(params.requestedStartPosition, params.requestedLoadSize);
callback.onResult(result, 0);
}
@Override
public void loadRange(@NonNull LoadRangeParams params, @NonNull LoadRangeCallback<Employee> callback) {
Log.d(TAG, "loadRange, startPosition = " + params.startPosition + ", loadSize = " + params.loadSize);
List<Employee> result = employeeStorage.getData(params.startPosition, params.loadSize);
callback.onResult(result);
}
}
И собственно вот так собираете всю эту схему:
// DataSource
MyPositionalDataSource dataSource = new MyPositionalDataSource(new EmployeeStorage());
// PagedList
PagedList.Config config = new PagedList.Config.Builder()
.setEnablePlaceholders(false)
.setPageSize(10)
.build();
PagedList<Employee> pagedList = new PagedList.Builder<>(dataSource, config)
.setMainThreadExecutor(new MainThreadExecutor())
.setBackgroundThreadExecutor(Executors.newSingleThreadExecutor())
.build();
// Adapter
adapter = new EmployeeAdapter(diffUtilCallback);
adapter.submitList(pagedList);
// RecyclerView
recyclerView.setAdapter(adapter);
Вот тут есть прям целая серия уроков по Paging Library. Рекомендую ознакомиться.
Я решил это следующим образом
В интерфейс добавил отдельный метод, который отвечает только за подгрузку
Single<MainModel> loadMore(
@Query("limit") int limit
);
И в активити при скроллинге до последнего элемента вызываю этот метод
recyclerMain.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
int visibleItemCount = gridLayoutManager.getChildCount();
int totalItemCount = gridLayoutManager.getItemCount();
int pastVisibleItems = gridLayoutManager.findFirstVisibleItemPosition();
if (pastVisibleItems + totalItemCount >= visibleItemCount){
int limit = +20;
presenter.loadMore(limit);
}
}
});