RecyclerView не ссылается даже при изменении набора данных

#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 событий. Теперь все работает так, как и ожидалось.