onScrolled вызывается без прокрутки пользователя (бесконечная прокрутка Recycler View)

Пробовал все связанные вопросы, но это не сработало :(

Я создаю бесконечную прокрутку с помощью Recycler View.

ПРОБЛЕМА: метод onScrolled всегда вызывается без прокрутки экрана пользователем.

Руководство по документации Android описывает это для метода onScrolled:

Случай 1) Метод обратного вызова, вызываемый при прокрутке RecyclerView. Это будет вызвано после завершения прокрутки.

Случай 2) Этот обратный вызов также будет вызываться, если видимый диапазон элементов изменится после расчета макета. В этом случае dx и dy будут равны 0.

Мой метод onScrolled вызывается без прокрутки пользователем из-за Случая 2. Я зарегистрировал dy на экране, и оказалось, что onScrolled вызывается с dy=0. Это происходит во втором случае.

Если я использую:

if (dy > 0){
    // The user is scrolling down
}

Получается, что мой метод onScrolled никогда не вызывается, сколько бы раз я не прокручивал Recycler View.

Мой код

RecyclerAdapterSmallVideos.java

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

    private List<VideosBigSize> videosBigSizeList;
    private Context mContext;

    final int VIEW_TYPE_ITEM = 0, VIEW_TYPE_LOADING = 1;
    ILoadMore loadMore;
    boolean isLoading;
    int visibleThreshold = 5;
    int lastVisibleItem, totalItemCount;

    public RecyclerAdapterSmallVideos(RecyclerView recyclerView, Context context, List<VideosBigSize> videosBigSizeList) {
        this.videosBigSizeList = videosBigSizeList;
        this.mContext = context;

        final LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);

                totalItemCount = linearLayoutManager.getItemCount();
                lastVisibleItem = linearLayoutManager.findLastVisibleItemPosition();
                if (!isLoading && totalItemCount <= (lastVisibleItem + visibleThreshold)) {

                    if (loadMore != null) {

                        // This is running without the user scrolling the Recycler View. 
                        loadMore.onLoadMore();
                    }
                    isLoading = true;

                }

            }
        });


    }


    @Override
    public int getItemViewType(int position) {
        return videosBigSizeList.get(position) == null ? VIEW_TYPE_LOADING: VIEW_TYPE_ITEM;
    }


    public void setLoadMore(ILoadMore loadMore) {
        this.loadMore = loadMore;
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {

        if (viewType == VIEW_TYPE_ITEM) {

            View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_small_video, parent, false);
            return new ViewHolderSmallVideos(view);
        } else if (viewType == VIEW_TYPE_LOADING) {

            View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_loading, parent, false);
            return new ViewHolderLoading(view);
        }
        return null;
    }


    @Override
    public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, final int position) {

        /*########### Case 1) The holder is a instance off ViewHolderSmallVideos (Items) ###########*/
        if (holder instanceof ViewHolderSmallVideos) {

            Log.i("Debugging", "onBindViewHolder: Binding instance of ViewHolder SmallVideos: " + holder.getAdapterPosition());

            VideosBigSize singleVideoBigSize = videosBigSizeList.get(position);

            ViewHolderSmallVideos viewHolderSmallVideos = (ViewHolderSmallVideos) holder;

            // Step 1) Images
            GlideApp.with(viewHolderSmallVideos.thumbNail.getContext())
                    .load(singleVideoBigSize.getThumnail_url())
                    .centerCrop()
                    .into(viewHolderSmallVideos.thumbNail); // Video Thumbnail

            // Step 2) Determine the Max Length for each TextView
            setTextMaxLength(viewHolderSmallVideos.videoTitle, viewHolderSmallVideos.channelName, mContext);

            // Step 3) Texts
            viewHolderSmallVideos.videoTitle.setText(singleVideoBigSize.getVideo_title());
            viewHolderSmallVideos.channelName.setText(singleVideoBigSize.getChannel_name());
            viewHolderSmallVideos.videoViews.setText(singleVideoBigSize.getVideo_views());
            viewHolderSmallVideos.videoUploadDate.setText(singleVideoBigSize.getVideo_upload_date());
            viewHolderSmallVideos.videoDuration.setText(singleVideoBigSize.getVideo_duration());

            // Step 4) Handle clicks
            viewHolderSmallVideos.constraintLayoutEachItem.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(mContext, String.valueOf(holder.getAdapterPosition()), Toast.LENGTH_SHORT).show();
                }
            });
        }

        /*########### Case 2) The holder is a instance off ViewHolderLoading (progressBar) ###########*/
        else if (holder instanceof ViewHolderLoading) {

            Log.i("Debugging", "onBindViewHolder: Binding instance of ViewHolder Loading: " + holder.getAdapterPosition());

            ViewHolderLoading viewHolderLoading = (ViewHolderLoading) holder;
            viewHolderLoading.progressBar.setIndeterminate(true);

        }

    }

    /**
     * Get the number of items in the adapter
     * @return the number of items in the adapter
     */
    @Override
    public int getItemCount() { return videosBigSizeList.size(); }


    public void setLoaded() {
        isLoading = false;
    }

    /**
     * Determine a maximum number of characters for the Title and Channel Name
     * If they are too big and the width of the screen is too small, the text
     * will get bigger than the thumbnail and then the layout will be disturbed.
     * @param videoTitle the title of the video
     * @param channelName the name of the channel who uploaded the video
     * @param context the activity context
     */
    private void setTextMaxLength(TextView videoTitle, TextView channelName, Context context) {

        DisplayMetrics displayMetrics = new DisplayMetrics();
        WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);

        if (windowManager != null) {

            // Get the current width of the screen in DP
            windowManager.getDefaultDisplay().getMetrics(displayMetrics);
            float dpWidth = displayMetrics.widthPixels / displayMetrics.density;

            // 1) If the device is 420dp or less;
            // 2) Limit the number of characters for the title and channel name
            if (dpWidth <= 420) {
                videoTitle.setFilters(new InputFilter[]{new InputFilter.LengthFilter(35)});
                channelName.setFilters(new InputFilter[]{new InputFilter.LengthFilter(25)});
            }
        }

    }

}

ProfileFragment.java

private void setupRecyclerView(final List<VideosBigSize> userAllVideosList) {

        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(mContext);
        mRecyclerView.setLayoutManager(linearLayoutManager);
        mRecyclerView.setNestedScrollingEnabled(false);
        final RecyclerAdapterSmallVideos adapterSmallVideos = new RecyclerAdapterSmallVideos(mRecyclerView, mContext, userAllVideosList);
        mRecyclerView.setAdapter(adapterSmallVideos);

        // Set Load more event
        adapterSmallVideos.setLoadMore(new ILoadMore() {
            @Override
            public void onLoadMore() {
                if (userAllVideosList.size() <= 50) {
                    mRecyclerView.post(new Runnable() {
                        @Override
                        public void run() {
                            userAllVideosList.add(null); 
                            adapterSmallVideos.notifyItemInserted(userAllVideosList.size() - 1);

                        }
                    });
                    new Handler().postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            userAllVideosList.remove(userAllVideosList.size() - 1);
                            adapterSmallVideos.notifyItemRemoved(userAllVideosList.size()); 

                            // Random more data
                            int index = userAllVideosList.size();
                            int end = index + 10;
                            for (int i = index; i < end; i++) {
                                // Add more data to userAllVideosList...
                                adapterSmallVideos.notifyItemInserted(i);

                            }

                            adapterSmallVideos.setLoaded();

                        }
                    }, 2000);
                }
                // Loaded everything already
                else {
                    Toast.makeText(mContext, "Load data completed!", Toast.LENGTH_SHORT).show();
                }
            }
        });

    }

Макет фрагмента моего профиля

fragment_profile.xml

Важно заметить, что:

1) Мой Recycler View находится внутри NesteScrollView, потому что мне нужно скрыть/показать панель инструментов. Помните, я установил mRecyclerView.setNestedScrollingEnabled(false), потому что он запускает NestedScrollView;

2) В моем макете есть еще вещи, кроме вида переработчика.

«ЭкранЭкран профиля 2


person Soon Santos    schedule 09.06.2018    source источник
comment
зачем тебе onScrolled? если просто для пейджинга, используйте библиотеку поддержки paging Google.   -  person pskink    schedule 10.06.2018
comment
и, конечно, вам нужно использовать PagedListAdapter в качестве базового класса   -  person pskink    schedule 11.06.2018
comment
Спасибо, попробую реализовать. Я не знал об этом классе. Но нужно ли мне брать данные из источника базы данных для реализации PagedListAdapter или я могу реализовать откуда угодно? У вас есть хороший учебник для этого, я видел, что в Интернете не так уж много вещей.   -  person Soon Santos    schedule 11.06.2018
comment
что "Database Source"? ты имеешь в виду DataSource?   -  person pskink    schedule 11.06.2018
comment
Извините, я имею в виду источник данных. Учебники, которые я видел, они создают для получения информации. Также я нахожу только учебники по kotlin, разве у вас нет хорошего по java?   -  person Soon Santos    schedule 11.06.2018
comment
Google для: PagedListAdapter -stackoverflow - есть несколько хороших руководств на medium.com, кстати, откуда вы берете свои данные?   -  person pskink    schedule 11.06.2018
comment
Я получу от Retrofit API, но свой конкретный API у меня будет только в сентябре. Поэтому я просто создаю случайные списки без важных данных для создания пользовательского интерфейса, а затем, после того, как я получу этот API, я его реализую.   -  person Soon Santos    schedule 11.06.2018


Ответы (1)


Для этого вам следует использовать библиотеку подкачки. нажмите, чтобы просмотреть лучший учебник :

И исходный код руководства на Github

Оказывается, поскольку библиотека подкачки — это новая функция, я находил только учебники по Kotlin. Затем я нашел этот, который, вероятно, является лучшим, написанным на Java. Реально спасибо автору, бился с этим целый месяц.

person Soon Santos    schedule 28.06.2018