पता लगाएँ कि स्क्रॉल करते समय RecyclerView नीचे सबसे अधिक स्थिति में पहुँचता है


106

मेरे पास यह कोड एक RecyclerView के लिए है।

    recyclerView = (RecyclerView)rootview.findViewById(R.id.fabric_recyclerView);
    recyclerView.setLayoutManager(layoutManager);
    recyclerView.addItemDecoration(new RV_Item_Spacing(5));
    FabricAdapter fabricAdapter=new FabricAdapter(ViewAdsCollection.getFabricAdsDetailsAsArray());
    recyclerView.setAdapter(fabricAdapter);

मुझे यह जानने की जरूरत है कि स्क्रॉल करते समय RecyclerView सबसे नीचे की स्थिति में कैसे पहुंचता है। क्या यह संभव है ? यदि हाँ, तो कैसे?




1
recyclerView.canScrollVertically (int दिशा); इस तरह का पैरामीटर क्या होना चाहिए कि हमें यहां से गुजरना पड़े?
एड्रियन

@ एड्रियन, यदि आप अभी भी इस प्रश्न में रुचि रखते हैं :)))) stackoverflow.com/a/48514857/6674369
एंड्री एंटोनोव

जवाबों:


186

इसे करने का एक सरल तरीका भी है

recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
    @Override
    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
        super.onScrollStateChanged(recyclerView, newState);

        if (!recyclerView.canScrollVertically(1)) {
            Toast.makeText(YourActivity.this, "Last", Toast.LENGTH_LONG).show();

        }
    }
});

दिशा पूर्णांक: -1 अप के लिए, नीचे के लिए 1, 0 हमेशा गलत होगा।


17
onScrolled () दो बार होने वाली पहचान को रोकता है।
इयान वम्बाई

मैं अपने पुनर्नवीनीकरण दृश्य में स्क्रॉललिस्ट को जोड़ने में असमर्थ हूं। OnScrollStateChanged में कोड निष्पादित नहीं करता है।
संदीप योहन

1
यदि आप इसे केवल एक बार ट्रिगर करना चाहते हैं (एक बार टोस्ट दिखाएं) तो स्टेटमेंट में बूलियन फ्लैग (एक क्लास मेंबर वैरिएबल) जोड़ें और इसे अंदर सच करने के लिए सेट करें जैसे: if (!recyclerView.canScrollVertically(1) && !mHasReachedBottomOnce) { mHasReachedBottomOnce = true Toast.makeText(YourActivity.this, "Last", Toast.LENGTH_LONG).show();}
bastami82

@ बस्तमी b२ मैं चाल का उपयोग करता हूँ जैसा कि आप टोस्ट के दो बार प्रिंट को रोकने के लिए सुझाव देते हैं लेकिन यह काम नहीं कर रहा है
विश्व प्रताप

4
बयान newState == RecyclerView.SCROLL_STATE_IDLEमें जोड़ें ifकाम करेगा।
ली चुन

73

बार-बार कॉल से बचने के लिए इस कोड का उपयोग करें

    recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);

            if (!recyclerView.canScrollVertically(1) && newState==RecyclerView.SCROLL_STATE_IDLE) {
                Log.d("-----","end");
                
            }
        }
    });

2
मैंने उत्तर देने की कोशिश की लेकिन यह मेरे लिए सरल और परिपूर्ण है। धन्यवाद
विश्व प्रताप

@Venkatesh स्वागत है।
मिलिंद चौधरी

एकमात्र जवाब जिसने मुझे कोई अन्य बग नहीं दिया
शराबी टी रेक्स

@ TheFluffyTRex धन्यवाद
मिलिंद चौधरी

सावधान दोस्तों! यह भी ट्रिगर हो जाएगा जब आप पुनर्नवीनीकरण में कुछ तत्व होने के दौरान स्वाइप करने की कोशिश करेंगे। यह TWICE को भी ट्रिगर करेगा कि न्यूस्टेट उपेक्षित है।
अम्मार मोहम्मद

47

बस अपने recyclerview पर एक addOnScrollListener () लागू करें। फिर स्क्रॉल श्रोता के अंदर नीचे दिए गए कोड को लागू करें।

RecyclerView.OnScrollListener mScrollListener = new RecyclerView.OnScrollListener() {
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            if (mIsLoading)
                return;
            int visibleItemCount = mLayoutManager.getChildCount();
            int totalItemCount = mLayoutManager.getItemCount();
            int pastVisibleItems = mLayoutManager.findFirstVisibleItemPosition();
            if (pastVisibleItems + visibleItemCount >= totalItemCount) {
                //End of list
            }
        }
    };

1
क्या आप कृपया mIsLading के बारे में थोड़ा समझा सकते हैं? आप इसे कहां स्थापित कर रहे हैं या मूल्य बदल रहे हैं।
मिगुएलहिंकापी

mLading सिर्फ एक बूलियन चर है जो यह दर्शाता है कि दृश्य व्यस्त है या नहीं। उदाहरण के लिए; यदि ऐप recyclerview को पॉप्युलेट कर रहा है, तो सूची का पॉप्युलेट होने के बाद mLading सच हो जाएगा और गलत हो जाएगा।
फेरी एम फेलिक्स

1
यह समाधान ठीक से काम नहीं करता है। पर एक नज़र डालें stackoverflow.com/a/48514857/6674369
एंड्री एंटोनोव

3
'findFirstVisibleItemPosition'पर विधि का समाधान नहीं कर सकते हैंandroid.support.v7.widget.RecyclerView
इमान मारशी

2
@ इमान मराशी, आपको कास्ट करने की आवश्यकता है (recyclerView.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition():। इस काम।
बिटवले

17

उत्तर कोटलिन में है, यह जावा में काम करेगा। यदि आप कॉपी और पेस्ट करते हैं तो IntelliJ को इसे आपके लिए परिवर्तित करना चाहिए।

recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener(){

    override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {

        // 3 lines below are not needed.
        Log.d("TAG","Last visible item is: ${gridLayoutManager.findLastVisibleItemPosition()}")
        Log.d("TAG","Item count is: ${gridLayoutManager.itemCount}")
        Log.d("TAG","end? : ${gridLayoutManager.findLastVisibleItemPosition() == gridLayoutManager.itemCount-1}")

        if(gridLayoutManager.findLastVisibleItemPosition() == gridLayoutManager.itemCount-1){
            // We have reached the end of the recycler view.
        }

        super.onScrolled(recyclerView, dx, dy)
    }
})

यह भी काम करेगा LinearLayoutManagerक्योंकि इसमें ऊपर इस्तेमाल किए गए समान तरीके हैं। अर्थात् findLastVisibleItemPosition()और getItemCount()( itemCountकोटलिन में)।


7
अच्छी बात है, लेकिन एक मुद्दा यह है कि अगर लूप मल्टीप्लाईट निष्पादित करेगा
विष्णु

क्या आपको उन्हीं मुद्दों का हल मिल गया है
विश्व प्रताप

1
@ विष्णु एक साधारण बूलियन वैरिएबल इसे ठीक कर सकता है।
डाका

11

उपरोक्त उत्तरों से मुझे सही समाधान नहीं मिल रहा था क्योंकि यह दो बार भी शुरू हो रहा था onScrolled

override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
            super.onScrolled(recyclerView, dx, dy)
           if( !recyclerView.canScrollVertically(RecyclerView.FOCUS_DOWN))
               context?.toast("Scroll end reached")
        }

2
लेकिन recyclerView.canScrollVertically(direction)दिशा किसी भी पूर्णांक + बनाम - तो यह 4 है अगर यह सबसे नीचे है या -12 अगर यह शीर्ष पर है।
एक्सनोलियन

6

इसे इस्तेमाल करे

मैंने उपरोक्त उत्तरों का उपयोग किया है यह हमेशा चलता है जब आप रिसाइकलर व्यू के अंत में जाएंगे,

यदि आप केवल एक बार जांचना चाहते हैं कि यह एक तल पर है या नहीं? उदाहरण: - अगर मेरे पास 10 आइटम की सूची है, जब भी मैं तल पर जाता हूं तो यह मुझे और फिर से प्रदर्शित करेगा यदि मैं ऊपर स्क्रॉल करता हूं तो यह फिर से प्रिंट नहीं होगा, और यदि आप अधिक सूची जोड़ते हैं और आप वहां जाते हैं तो यह फिर से प्रदर्शित होगा।

नोट: - इस विधि का उपयोग करें जब आप एपीआई को मारने में ऑफसेट के साथ व्यवहार करते हैं

  1. EndlessRecyclerViewScrollListener नाम से एक क्लास बनाएँ

        import android.support.v7.widget.GridLayoutManager;
        import android.support.v7.widget.LinearLayoutManager;
        import android.support.v7.widget.RecyclerView;
        import android.support.v7.widget.StaggeredGridLayoutManager;
    
        public abstract class EndlessRecyclerViewScrollListener extends RecyclerView.OnScrollListener {
            // The minimum amount of items to have below your current scroll position
            // before loading more.
            private int visibleThreshold = 5;
            // The current offset index of data you have loaded
            private int currentPage = 0;
            // The total number of items in the dataset after the last load
            private int previousTotalItemCount = 0;
            // True if we are still waiting for the last set of data to load.
            private boolean loading = true;
            // Sets the starting page index
            private int startingPageIndex = 0;
    
            RecyclerView.LayoutManager mLayoutManager;
    
            public EndlessRecyclerViewScrollListener(LinearLayoutManager layoutManager) {
                this.mLayoutManager = layoutManager;
            }
    
        //    public EndlessRecyclerViewScrollListener() {
        //        this.mLayoutManager = layoutManager;
        //        visibleThreshold = visibleThreshold * layoutManager.getSpanCount();
        //    }
    
            public EndlessRecyclerViewScrollListener(StaggeredGridLayoutManager layoutManager) {
                this.mLayoutManager = layoutManager;
                visibleThreshold = visibleThreshold * layoutManager.getSpanCount();
            }
    
            public int getLastVisibleItem(int[] lastVisibleItemPositions) {
                int maxSize = 0;
                for (int i = 0; i < lastVisibleItemPositions.length; i++) {
                    if (i == 0) {
                        maxSize = lastVisibleItemPositions[i];
                    }
                    else if (lastVisibleItemPositions[i] > maxSize) {
                        maxSize = lastVisibleItemPositions[i];
                    }
                }
                return maxSize;
            }
    
            // This happens many times a second during a scroll, so be wary of the code you place here.
            // We are given a few useful parameters to help us work out if we need to load some more data,
            // but first we check if we are waiting for the previous load to finish.
            @Override
            public void onScrolled(RecyclerView view, int dx, int dy) {
                int lastVisibleItemPosition = 0;
                int totalItemCount = mLayoutManager.getItemCount();
    
                if (mLayoutManager instanceof StaggeredGridLayoutManager) {
                    int[] lastVisibleItemPositions = ((StaggeredGridLayoutManager) mLayoutManager).findLastVisibleItemPositions(null);
                    // get maximum element within the list
                    lastVisibleItemPosition = getLastVisibleItem(lastVisibleItemPositions);
                } else if (mLayoutManager instanceof GridLayoutManager) {
                    lastVisibleItemPosition = ((GridLayoutManager) mLayoutManager).findLastVisibleItemPosition();
                } else if (mLayoutManager instanceof LinearLayoutManager) {
                    lastVisibleItemPosition = ((LinearLayoutManager) mLayoutManager).findLastVisibleItemPosition();
                }
    
                // If the total item count is zero and the previous isn't, assume the
                // list is invalidated and should be reset back to initial state
                if (totalItemCount < previousTotalItemCount) {
                    this.currentPage = this.startingPageIndex;
                    this.previousTotalItemCount = totalItemCount;
                    if (totalItemCount == 0) {
                        this.loading = true;
                    }
                }
                // If it’s still loading, we check to see if the dataset count has
                // changed, if so we conclude it has finished loading and update the current page
                // number and total item count.
                if (loading && (totalItemCount > previousTotalItemCount)) {
                    loading = false;
                    previousTotalItemCount = totalItemCount;
                }
    
                // If it isn’t currently loading, we check to see if we have breached
                // the visibleThreshold and need to reload more data.
                // If we do need to reload some more data, we execute onLoadMore to fetch the data.
                // threshold should reflect how many total columns there are too
                if (!loading && (lastVisibleItemPosition + visibleThreshold) > totalItemCount) {
                    currentPage++;
                    onLoadMore(currentPage, totalItemCount, view);
                    loading = true;
                }
            }
    
            // Call this method whenever performing new searches
            public void resetState() {
                this.currentPage = this.startingPageIndex;
                this.previousTotalItemCount = 0;
                this.loading = true;
            }
    
            // Defines the process for actually loading more data based on page
            public abstract void onLoadMore(int page, int totalItemsCount, RecyclerView view);
    
        }
    
  2. इस तरह इस वर्ग का उपयोग करें

         LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity());
            recyclerView.setLayoutManager(linearLayoutManager);
            recyclerView.addOnScrollListener(new EndlessRecyclerViewScrollListener( linearLayoutManager) {
                @Override
                public void onLoadMore(int page, int totalItemsCount, RecyclerView view) {
                    Toast.makeText(getActivity(),"LAst",Toast.LENGTH_LONG).show();
                }
            });
    

यह मेरे अंत में सही चल रहा है, अगर आपको कोई मुद्दा मिल रहा है तो मुझे सराहें


यह बहुत मददगार था :)
देवराठ

4

मेरा कार्यान्वयन है, यह बहुत उपयोगी है StaggeredGridLayout

उपयोग:

private EndlessScrollListener scrollListener =
        new EndlessScrollListener(new EndlessScrollListener.RefreshList() {
            @Override public void onRefresh(int pageNumber) {
                //end of the list
            }
        });

rvMain.addOnScrollListener(scrollListener);

श्रोता कार्यान्वयन:

class EndlessScrollListener extends RecyclerView.OnScrollListener {
private boolean isLoading;
private boolean hasMorePages;
private int pageNumber = 0;
private RefreshList refreshList;
private boolean isRefreshing;
private int pastVisibleItems;

EndlessScrollListener(RefreshList refreshList) {
    this.isLoading = false;
    this.hasMorePages = true;
    this.refreshList = refreshList;
}

@Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
    super.onScrolled(recyclerView, dx, dy);
    StaggeredGridLayoutManager manager =
            (StaggeredGridLayoutManager) recyclerView.getLayoutManager();

    int visibleItemCount = manager.getChildCount();
    int totalItemCount = manager.getItemCount();
    int[] firstVisibleItems = manager.findFirstVisibleItemPositions(null);
    if (firstVisibleItems != null && firstVisibleItems.length > 0) {
        pastVisibleItems = firstVisibleItems[0];
    }

    if (visibleItemCount + pastVisibleItems >= totalItemCount && !isLoading) {
        isLoading = true;
        if (hasMorePages && !isRefreshing) {
            isRefreshing = true;
            new Handler().postDelayed(new Runnable() {
                @Override public void run() {
                    refreshList.onRefresh(pageNumber);
                }
            }, 200);
        }
    } else {
        isLoading = false;
    }
}

public void noMorePages() {
    this.hasMorePages = false;
}

void notifyMorePages() {
    isRefreshing = false;
    pageNumber = pageNumber + 1;
}

interface RefreshList {
    void onRefresh(int pageNumber);
}  }

कॉलबैक में 200 मिलीसेकंड की देरी क्यों जोड़ें?
मणि

@ मानी इस क्षण तक मुझे ठीक से याद नहीं है, लेकिन शायद मैंने इसे सिर्फ सहज प्रभाव के लिए किया है ...
अलेक्सी टिमोशेंको

4

इस सूत्र में अधिकांश अन्य उत्तरों से संतुष्ट नहीं होने के बाद, मैंने पाया कि मुझे लगता है कि कुछ बेहतर है और यहां कहीं भी नहीं है।

recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        if (!recyclerView.canScrollVertically(1) && dy > 0)
        {
             //scrolled to bottom
        }else if (!recyclerView.canScrollVertically(-1) && dy < 0)
        {
            //scrolled to bottom
        }
    }
});

यह सरल है और ऊपर या नीचे स्क्रॉल करने पर सभी परिस्थितियों में ठीक एक बार हिट होगा।


3

मैं भी इस प्रश्न की खोज कर रहा था, लेकिन मुझे वह उत्तर नहीं मिला, जिसने मुझे संतुष्ट किया हो, इसलिए मैं recyclerView का स्वयं का बोध बनाता हूं।

अन्य समाधान कम सटीक है तो मेरा है। उदाहरण के लिए: यदि अंतिम आइटम बहुत बड़ा है (पाठ का बहुत) तो अन्य समाधानों की कॉलबैक बहुत पहले आएगी, फिर recyclerView वास्तव में नीचे तक पहुंच जाएगी।

मेरा शोक इस मुद्दे को ठीक करता है।

class CustomRecyclerView: RecyclerView{

    abstract class TopAndBottomListener{
        open fun onBottomNow(onBottomNow:Boolean){}
        open fun onTopNow(onTopNow:Boolean){}
    }


    constructor(c:Context):this(c, null)
    constructor(c:Context, attr:AttributeSet?):super(c, attr, 0)
    constructor(c:Context, attr:AttributeSet?, defStyle:Int):super(c, attr, defStyle)


    private var linearLayoutManager:LinearLayoutManager? = null
    private var topAndBottomListener:TopAndBottomListener? = null
    private var onBottomNow = false
    private var onTopNow = false
    private var onBottomTopScrollListener:RecyclerView.OnScrollListener? = null


    fun setTopAndBottomListener(l:TopAndBottomListener?){
        if (l != null){
            checkLayoutManager()

            onBottomTopScrollListener = createBottomAndTopScrollListener()
            addOnScrollListener(onBottomTopScrollListener)
            topAndBottomListener = l
        } else {
            removeOnScrollListener(onBottomTopScrollListener)
            topAndBottomListener = null
        }
    }

    private fun createBottomAndTopScrollListener() = object :RecyclerView.OnScrollListener(){
        override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
            checkOnTop()
            checkOnBottom()
        }
    }

    private fun checkOnTop(){
        val firstVisible = linearLayoutManager!!.findFirstCompletelyVisibleItemPosition()
        if(firstVisible == 0 || firstVisible == -1 && !canScrollToTop()){
            if (!onTopNow) {
                onTopNow = true
                topAndBottomListener?.onTopNow(true)
            }
        } else if (onTopNow){
            onTopNow = false
            topAndBottomListener?.onTopNow(false)
        }
    }

    private fun checkOnBottom(){
        var lastVisible = linearLayoutManager!!.findLastCompletelyVisibleItemPosition()
        val size = linearLayoutManager!!.itemCount - 1
        if(lastVisible == size || lastVisible == -1 && !canScrollToBottom()){
            if (!onBottomNow){
                onBottomNow = true
                topAndBottomListener?.onBottomNow(true)
            }
        } else if(onBottomNow){
            onBottomNow = false
            topAndBottomListener?.onBottomNow(false)
        }
    }


    private fun checkLayoutManager(){
        if (layoutManager is LinearLayoutManager)
            linearLayoutManager = layoutManager as LinearLayoutManager
        else
            throw Exception("for using this listener, please set LinearLayoutManager")
    }

    private fun canScrollToTop():Boolean = canScrollVertically(-1)
    private fun canScrollToBottom():Boolean = canScrollVertically(1)
}

फिर आपकी गतिविधि / खंड में:

override fun onCreate() {
    customRecyclerView.layoutManager = LinearLayoutManager(context)
}

override fun onResume() {
    super.onResume()
    customRecyclerView.setTopAndBottomListener(this)
}

override fun onStop() {
    super.onStop()
    customRecyclerView.setTopAndBottomListener(null)
}

आशा है कि यह किसी को मार देगा ;-)


1
दूसरों से समाधान का कौन सा हिस्सा आपको संतुष्ट नहीं करता है? आपको अपना उत्तर सुझाने से पहले इसे पहले समझ लेना चाहिए
ज़म सनक

@ZamSunk कॉस अन्य समाधान कम सटीक है तो मेरा है। उदाहरण के लिए यदि अंतिम आइटम बहुत कम है (पाठ का एक बहुत) तो अन्य समाधानों की कॉलबैक पहले आएगी, फिर recyclerView वास्तव में नीचे तक पहुंच जाएगी। मेरा शोक इस मुद्दे को ठीक करता है।
एंड्री एंटोनोव

0

यह मेरा समाधान है:

    val onScrollListener = object : RecyclerView.OnScrollListener() {

    override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) {
        directionDown = dy > 0
    }

    override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
        if (recyclerView.canScrollVertically(1).not()
                && state != State.UPDATING
                && newState == RecyclerView.SCROLL_STATE_IDLE
                && directionDown) {
               state = State.UPDATING
            // TODO do what you want  when you reach bottom, direction
            // is down and flag for non-duplicate execution
        }
    }
}

आपको यह कहाँ मिला state ?
राडित्य गुमाय

राज्य मेरा एक प्रतीक है, यह उस स्क्रीन के लिए राज्यों की जाँच करने के लिए ध्वज है (मैंने अपने ऐप पर
डेटाबाइंडिंग के

यदि कोई इंटरनेट कनेक्टिविटी नहीं है तो आप कॉलबैक कैसे लागू करेंगे?
aks4125

0

हम स्थिति प्राप्त करने के लिए इंटरफ़ेस का उपयोग कर सकते हैं

इंटरफ़ेस: श्रोता के लिए एक इंटरफ़ेस बनाएँ

public interface OnTopReachListener { void onTopReached(int position);}

गतिविधि:

mediaRecycleAdapter = new MediaRecycleAdapter(Class.this, taskList); recycle.setAdapter(mediaRecycleAdapter); mediaRecycleAdapter.setOnSchrollPostionListener(new OnTopReachListener() {
@Override
public void onTopReached(int position) {
    Log.i("Position","onTopReached "+position);  
}
});

एडाप्टर:

public void setOnSchrollPostionListener(OnTopReachListener topReachListener) {
    this.topReachListener = topReachListener;}@Override public void onBindViewHolder(MyViewHolder holder, int position) {if(position == 0) {
  topReachListener.onTopReached(position);}}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.