#java #android #android-recyclerview
#java #Android #android-recyclerview
Вопрос:
Я пытаюсь реализовать разбивку на страницы с помощью recyclerview. Я не знаю почему, но данные обновляются только тогда, когда есть результаты в ответ, когда результатов нет, они не обновляются.У меня есть фильтр, сортировка и выбор местоположения; фильтр и сортировка работают нормально, но выбор местоположения не работает, поскольку it api возвращает пустой список для некоторых выборок.У меня есть SwipeRefreshLayout в макете также при повторном обновлении с помощью swipe refresh , после чего recyclerview обновляется.
Это мой слушатель разбиения на страницы и recyclerview
public abstract class PaginationScrollListener extends RecyclerView.OnScrollListener {
LinearLayoutManager layoutManager;
public PaginationScrollListener(LinearLayoutManager layoutManager) {
this.layoutManager = layoutManager;
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int visibleItemCount = layoutManager.getChildCount();
int totalItemCount = layoutManager.getItemCount();
int firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition();
if (!isLoading() amp;amp; !isLastPage()) {
if ((visibleItemCount firstVisibleItemPosition) >= totalItemCount
amp;amp; firstVisibleItemPosition >= 0
amp;amp; totalItemCount >= getTotalPageCount()) {
loadMoreItems();
}
}
}
protected abstract void loadMoreItems();
public abstract int getTotalPageCount();
public abstract boolean isLastPage();
public abstract boolean isLoading(); }
Это мой recyclerview
GridLayoutManager gridLayoutManager = new GridLayoutManager(getmContext(), AppConstants.GRID_2);
mProductRecyclerView.setLayoutManager(gridLayoutManager);
mProductRecyclerView.setItemAnimator(new DefaultItemAnimator());
PaginationScrollListener paginationScrollListener = new PaginationScrollListener(gridLayoutManager) {
@Override
protected void loadMoreItems() {
isLoading = true;
//Increment page index to load the next one
CURRENT_PAGE = 1;
loadNextPage();
}
@Override
public int getTotalPageCount() {
return TOTAL_PAGE;
}
@Override
public boolean isLastPage() {
return isLastPage;
}
@Override
public boolean isLoading() {
return isLoading;
}
};
productListAdapter = new ProductListAdapter(getmContext(), R.layout.b_item_home_product);
productListAdapter.setiAdapterItemCheckChangeListener(this);
mProductRecyclerView.setAdapter(productListAdapter);
mProductRecyclerView.addOnScrollListener(paginationScrollListener);
itemClickDisposable = productListAdapter.getItemClickSubject().subscribe(productId -> {
UIHelper.getInstance().switchActivity(getmActivity(), ProductInfoActivity.class, UIHelper.ActivityAnimations.LEFT_TO_RIGHT, productId.toString(), AppConstants.K_ID, false);
});
viewModel.getProductsLiveData().observe(getViewLifecycleOwner(), responseList -> {
switch (responseList.status) {
case LOADING:
if (CURRENT_PAGE <= TOTAL_PAGE) {
showHideBottomProgress(true);
} else {
isLastPage = true;
}
if (isFirstPage) {
showMainLoading(true);
}
break;
case ERROR:
swipeRefreshLayout.setRefreshing(false);
if (CURRENT_PAGE <= TOTAL_PAGE) {
showHideBottomProgress(false);
}
if (isFirstPage) {
showMainLoading(false);
} else {
isLoading = false;
}
showErrorPrompt(responseList.message);
break;
case SUCCESS:
swipeRefreshLayout.setRefreshing(false);
if (CURRENT_PAGE <= TOTAL_PAGE) {
showHideBottomProgress(false);
}
if (isFirstPage) {
showMainLoading(false);
} else {
isLoading = false;
}
productListAdapter.addProducts(responseList.response);
break;
}
});
Для загрузки страницы и следующей страницы с использованием этих методов
private void loadFirstPage() {
isFirstPage = true;
isLastPage = false;
resetProductsList();
viewModel.getProducts(getProductFilter());
}
private void loadNextPage() {
isFirstPage = false;
productFilter.setOffset(CURRENT_PAGE);
viewModel.getProducts(productFilter);
}
public void resetProductsList() {
productListAdapter.clear();
productFilter.setOffset(1);
}
Это мой класс адаптера
public class ProductListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int TYPE_SALE = 1, TYPE_POPULAR = 2, TYPE_LOADING = 3;
private Context mContext;
private int mResId;
private List<Product> products;
private PublishSubject<Integer> itemClickSubject = PublishSubject.create();
private IAdapterItemCheckChangeListener iAdapterItemCheckChangeListener;
private IAdapterItemClickListener iAdapterItemClickListener;
public ProductListAdapter(Context context, int resId, List<Product> data) {
mContext = context;
mResId = resId;
products = data;
}
public ProductListAdapter(Context context, int resId) {
mContext = context;
mResId = resId;
products = new ArrayList<>();
}
public List<Product> getProducts() {
return products;
}
public void addProducts(List<Product> newProducts) {
for (Product product : newProducts) {
addProduct(product);
}
}
public void addProduct(Product newProduct) {
products.add(newProduct);
notifyItemInserted(products.size() - 1);
}
public void setiAdapterItemCheckChangeListener(IAdapterItemCheckChangeListener iAdapterItemCheckChangeListener) {
this.iAdapterItemCheckChangeListener = iAdapterItemCheckChangeListener;
}
public void setiAdapterItemClickListener(IAdapterItemClickListener iAdapterItemClickListener) {
this.iAdapterItemClickListener = iAdapterItemClickListener;
}
public PublishSubject<Integer> getItemClickSubject() {
return itemClickSubject;
}
// Clean all elements of the recycler
public void clear() {
while (getItemCount() > 0) {
remove(getItem(0));
}
}
public void remove(Product product) {
int position = products.indexOf(product);
if (position > -1) {
products.remove(position);
notifyItemRemoved(position);
}
}
public Product getItem(int position) {
return products.get(position);
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
switch (viewType) {
case TYPE_SALE:
return new SaleHolder(LayoutInflater.from(mContext).inflate(R.layout.item_home_sale, parent, false));
case TYPE_LOADING:
return new ProgressItemHolder(LayoutInflater.from(mContext).inflate(R.layout.item_pagination, parent, false));
default:
return new ProductHolder(LayoutInflater.from(mContext).inflate(mResId, parent, false));
}
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
if (getItemViewType(position) == TYPE_POPULAR) {
((ProductHolder) holder).setUpProductHolder(products.get(position));
} else if (getItemViewType(position) == TYPE_LOADING) {
} else {
((SaleHolder) holder).setupSaleHolder(products.get(position));
}
}
@Override
public int getItemViewType(int position) {
if (products.get(position).getType() == AppConstants.PRODUCT_TYPE.TYPE_SALE) {
return TYPE_SALE;
} else {
return TYPE_POPULAR;
}
}
@Override
public int getItemCount() {
return products.size();
}
@Override
public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
super.onDetachedFromRecyclerView(recyclerView);
itemClickSubject.onComplete();
}
public class SaleHolder extends RecyclerView.ViewHolder {
public SaleHolder(@NonNull View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
private void setupSaleHolder(Product product) {
}
}
public class ProgressItemHolder extends RecyclerView.ViewHolder {
@BindView(R.id.progress_wheel)
ProgressWheel progressWheel;
@BindView(R.id.tv_error)
TextView errorMsg;
public ProgressItemHolder(@NonNull View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
private void bindHolder(NetworkState networkState) {
if (networkState != null amp;amp; networkState.getStatus() == NetworkState.Status.LOADING) {
progressWheel.setVisibility(View.VISIBLE);
progressWheel.spin();
} else {
progressWheel.setVisibility(View.GONE);
progressWheel.stopSpinning();
}
if (networkState != null amp;amp; networkState.getStatus() == NetworkState.Status.FAILED) {
errorMsg.setVisibility(View.VISIBLE);
errorMsg.setText(R.string.please_connect_to_your_network);
} else {
errorMsg.setVisibility(View.GONE);
}
}
}
public class ProductHolder extends RecyclerView.ViewHolder implements CompoundButton.OnCheckedChangeListener, View.OnClickListener {
@BindView(R.id.iv_product_preview_image)
ImageView productImage;
@BindView(R.id.tv_product_name)
TextView productName;
@BindView(R.id.tv_product_price)
MagicText productPrice;
@BindView(R.id.tv_average_rating)
TextView productRating;
@BindView(R.id.cb_wishlist)
CheckBox wishlist;
@BindView(R.id.tv_product_quality)
TextView productQuality;
@BindView(R.id.tv_discount)
TextView discount;
@BindView(R.id.container_original_price)
ConstraintLayout originalPrice;
@BindView(R.id.tv_original_price)
TextView priceWithoutDiscount;
@BindView(R.id.tv_brand_name)
TextView brandName;
public ProductHolder(@NonNull View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
RxView.clicks(itemView)
.map(unit -> getAdapterPosition())
.subscribe(itemClickSubject);
wishlist.setOnCheckedChangeListener(this);
itemView.setOnClickListener(this);
}
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (iAdapterItemCheckChangeListener == null) return;
if (buttonView.isPressed()) {
iAdapterItemCheckChangeListener.onCheckChange(isChecked, getAdapterPosition());
}
}
public void setUpProductHolder(Product product) {
try {
productImage.setImageDrawable(null);
if (product.getBrandLabel() == null) {
brandName.setText("");
} else {
brandName.setText(product.getBrandLabel());
}
productName.setText(mContext.getString(R.string.product_full_name, product.getName()));
wishlist.setChecked(product.getWishList() >= 1);
// productPrice.setText(mContext.getString(R.string.price_with_currency, product.getPrice()));
float avgRating = product.getAverageRating() != null ? product.getAverageRating() : AppConstants.DEFAULT_RATING;
String ratingTrimed = String.format(Locale.getDefault(), "%.1f", avgRating);
productRating.setText(mContext.getString(R.string.average_rating, avgRating));
productQuality.setText(product.getQuality() != null ? product.getQuality() : "");
if (product.getDiscount() > 0) {
String fullPrice = mContext.getString(R.string.price_in_currency, product.getFinalPrice());
productPrice.change(fullPrice, mContext.getResources().getColor(R.color.colorBlack), 16, "normal", product.getFinalPrice());
discount.setVisibility(View.VISIBLE);
discount.setText(mContext.getString(R.string.discount_with_percent, product.getDiscount()));
originalPrice.setVisibility(View.VISIBLE);
priceWithoutDiscount.setText(product.getPrice());
} else {
String fullPrice = mContext.getString(R.string.price_in_currency, product.getPrice());
productPrice.change(fullPrice, mContext.getResources().getColor(R.color.colorBlack), 16, "normal", product.getPrice());
discount.setVisibility(View.GONE);
originalPrice.setVisibility(View.GONE);
}
if (product.getFree() > 0) {
String fullPrice = mContext.getString(R.string.price_in_currency, product.getSpecialPrice());
productPrice.change(fullPrice, mContext.getResources().getColor(R.color.colorBlack), 16, "normal", product.getSpecialPrice().toString());
originalPrice.setVisibility(View.VISIBLE);
priceWithoutDiscount.setText(product.getPrice());
discount.setVisibility(View.VISIBLE);
discount.setText(mContext.getString(R.string.discount_with_percent, AppConstants.DISCOUNT_100));
}
Glide.with(mContext).load(product.getImages().get(0).getValue()).apply(new RequestOptions()
.error(R.drawable.no_image_available)
.centerCrop().placeholder(R.drawable.no_image_available)).diskCacheStrategy(DiskCacheStrategy.RESOURCE).into(productImage);
} catch (Exception e) {
Log.d(getClass().getSimpleName(), "error");
}
}
@Override
public void onClick(View v) {
if (iAdapterItemClickListener == null) return;
iAdapterItemClickListener.onAdapterItemClick(v, getAdapterPosition());
}
}
}
Здесь я реализую разбивку на страницы
Разбивка на страницы предварительного просмотра изображений
Редактировать 1: при загрузке первой страницы
{«скидка»: false, «лимит»: 10, «смещение»: 1, «заказ»: «DESC», «сортировка»: «рейтинг»} Ответ: при загрузке первой страницы
При загрузке следующей страницы
{«город»: «Казахстан», «скидка»: false, «лимит»: 10, «смещение»: 1, «заказ»: «DESC», «сортировка»:»рейтинг»}
Ответ №1:
Согласно части кодирования вашего класса адаптера, вы не обрабатываете пустой список.
В приведенном ниже методе вы добавляете продукты один за другим в список продуктов адаптера. когда список пуст, цикл не работает, и список не изменяется, поэтому значения совпадают с предыдущим ответом.
public void addProducts(List<Product> newProducts) {
for (Product product : newProducts) {
addProduct(product);
}
}
Добавьте приведенный ниже фрагмент кода в свой метод
public void addProducts(List<Product> newProducts) {
if(newProducts!= null amp;amp; !newProducts.isEmpty()){
for (Product product : newProducts)
addProduct(product);
}else{
// either you can set empty list
this.products = newProducts;
//or you can clear the list values
products.clear();
notifyDataSetChanged();
}
}
Комментарии:
1. Я также очищаю адаптер перед вызовом api
public void resetProductsList() { productListAdapter.clear(); productFilter.setOffset(1); }
, но recylerview, похоже, не обновляется после этого.2. Представление Recycler находится внутри фрагмента, если это поможет.
3. Вы пробовали код в ответе? Это поможет вам.
4. Нет все того же результата.
5. Чего вы именно хотите?
Ответ №2:
Я не совсем понимаю, в чем проблема. Это может быть проблема, связанная с потоком.
Проблема: экран разбивки на страницы был фрагментом, а счетчик местоположения был активен на панели инструментов. Я использовал
getActivity.findViewById()
, чтобы получить счетчик и заполнить данные внутри самого этого фрагмента. У меня был установлен прослушиватель для spinner, поэтому при каждом нажатии на элемент я вызывал api внутри фрагмента.
Я решил эту проблему, используя шину событий для связи с этим фрагментом. Теперь я заполняю счетчик внутри самой активности. Всякий раз, когда элемент щелкается из этого счетчика, я запускаю событие; это событие, на которое я подписался во фрагменте, где я реализую разбивку на страницы, поэтому вызывается API событий. Теперь все работает так, как и ожидалось.