2017-09-04 32 views
9

मेरे पास RecyclerView है जो स्क्रॉलबार के साथ अलग-अलग ऊंचाई के आइटम के साथ है। आइटम की विभिन्न ऊंचाइयों के कारण, स्क्रॉलबार इसके लंबवत आकार को बदलता है, इस पर निर्भर करता है कि कौन से आइटम वर्तमान में प्रदर्शित होते हैं (स्क्रीनशॉट देखें)। मैंने एक उदाहरण प्रोजेक्ट बनाया है जो here समस्या प्रदर्शित करता है।पुनर्नवीनीकरण विभिन्न ऊंचाई के आइटम के साथ देखें: स्क्रॉलबार

  1. क्या किसी को भी यही समस्या है और इसे ठीक किया है?
  2. मैं स्क्रॉलबार ऊंचाई और स्थिति को अपने कार्यान्वयन के साथ आने की स्थिति को कैसे ओवरराइड कर सकता हूं?

संपादित करें: स्क्रॉलबार की स्थिति और ऊंचाई RecyclerViewscomputeVerticalScrollOffset, computeVerticalScrollRange और computeVerticalScrollExtent अधिभावी द्वारा नियंत्रित किया जा सकता है। मुझे कोई जानकारी नहीं है कि स्क्रॉलबार को गतिशील आइटम ऊंचाइयों के साथ ठीक से काम करने के लिए इन्हें कैसे कार्यान्वित किया जाए।

समस्या, मुझे लगता है कि RecyclerView वर्तमान में दिखाई देने वाली वस्तुओं के आधार पर सभी वस्तुओं की कुल ऊंचाई का अनुमान लगाता है और तदनुसार स्क्रॉलबार की स्थिति और ऊंचाई सेट करता है। इसे हल करने का एक तरीका सभी वस्तुओं की कुल ऊंचाई का बेहतर अनुमान देना है।

large scrollbar small scrollbar

+0

ऐसा लगता है कि समस्या यह है कि आपके रीसाइक्लिंग व्यू के अंदर स्क्रॉल व्यू है, है ना? – azizbekian

+0

@azizbekian नहीं, यह मामला नहीं है। – FWeigl

+0

क्या आपके पास लगातार पंक्तियों की संख्या है? मुझे नहीं लगता कि यदि आप परिवर्तनीय हैं तो आप इसे प्रबंधित कर सकते हैं। आरवी की प्रकृति को देखते हुए, आपके पास – crgarridos

उत्तर

3

इस स्थिति को संभालने का सबसे अच्छा तरीका किसी भी प्रकार के आइटम के आधार पर स्क्रॉल बार रेंज की गणना करना किसी भी तरह से हो सकता है। यह व्यावहारिक या वांछनीय नहीं हो सकता है। इसके बदले, यहां एक कस्टम रीसाइक्लर व्यू का एक सरल कार्यान्वयन है जिसे आप अपनी इच्छित चीज़ों को प्राप्त करने के लिए खेल सकते हैं। यह आपको दिखाएगा कि आप स्क्रॉल बार को नियंत्रित करने के लिए विभिन्न स्क्रॉल विधियों का उपयोग कैसे कर सकते हैं। यह प्रदर्शित वस्तुओं की संख्या के आधार पर अंगूठे के आकार को प्रारंभिक आकार में चिपकाएगा। याद रखने की मुख्य बात यह है कि स्क्रॉल रेंज मनमानी है लेकिन अन्य सभी माप (सीमा, ऑफसेट) को एक ही इकाइयों का उपयोग करना चाहिए।

computeVerticalScrollRange() के लिए प्रलेखन देखें।

यहां परिणाम का एक वीडियो है।

enter image description here

अद्यतन: कोड में कुछ समस्या को सुलझाने के अद्यतन किया गया है: अंगूठे का आंदोलन कम झटकेदार है और अंगूठे अब नीचे करने के लिए RecyclerView स्क्रॉल के रूप में तल पर आराम करने के लिए आ जाएगा । कोड के बाद दिए गए कुछ चेतावनी भी हैं।

MyRecyclerView.java (अद्यतन)

public class MyRecyclerView extends RecyclerView { 
    // The size of the scroll bar thumb in our units. 
    private int mThumbHeight = UNDEFINED; 

    // Where the RecyclerView cuts off the views when the RecyclerView is scrolled to top. 
    // For example, if 1/4 of the view at position 9 is displayed at the bottom of the RecyclerView, 
    // mTopCutOff will equal 9.25. This value is used to compute the scroll offset. 
    private float mTopCutoff = UNDEFINED; 

    public MyRecyclerView(Context context) { 
     super(context); 
    } 

    public MyRecyclerView(Context context, @Nullable AttributeSet attrs) { 
     super(context, attrs); 
    } 

    public MyRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) { 
     super(context, attrs, defStyle); 
    } 

    /** 
    * Retrieves the size of the scroll bar thumb in our arbitrary units. 
    * 
    * @return Scroll bar thumb height 
    */ 
    @Override 
    public int computeVerticalScrollExtent() { 
     return (mThumbHeight == UNDEFINED) ? 0 : mThumbHeight; 
    } 

    /** 
    * Compute the offset of the scroll bar thumb in our scroll bar range. 
    * 
    * @return Offset in scroll bar range. 
    */ 
    @Override 
    public int computeVerticalScrollOffset() { 
     return (mTopCutoff == UNDEFINED) ? 0 : (int) ((getCutoff() - mTopCutoff) * ITEM_HEIGHT); 
    } 

    /** 
    * Computes the scroll bar range. It will simply be the number of items in the adapter 
    * multiplied by the given item height. The scroll extent size is also computed since it 
    * will not vary. Note: The RecyclerView must be positioned at the top or this method 
    * will throw an IllegalStateException. 
    * 
    * @return The scroll bar range 
    */ 
    @Override 
    public int computeVerticalScrollRange() { 
     if (mThumbHeight == UNDEFINED) { 
      LinearLayoutManager lm = (LinearLayoutManager) getLayoutManager(); 
      int firstCompletePositionw = lm.findFirstCompletelyVisibleItemPosition(); 

      if (firstCompletePositionw != RecyclerView.NO_POSITION) { 
       if (firstCompletePositionw != 0) { 
        throw (new IllegalStateException(ERROR_NOT_AT_TOP_OF_RANGE)); 
       } else { 
        mTopCutoff = getCutoff(); 
        mThumbHeight = (int) (mTopCutoff * ITEM_HEIGHT); 
       } 
      } 
     } 
     return getAdapter().getItemCount() * ITEM_HEIGHT; 
    } 

    /** 
    * Determine where the RecyclerVIew display cuts off the list of views. The range is 
    * zero through (getAdapter().getItemCount() - 1) inclusive. 
    * 
    * @return The position in the RecyclerView where the displayed views are cut off. If the 
    * bottom view is partially displayed, this will be a fractional number. 
    */ 
    private float getCutoff() { 
     LinearLayoutManager lm = (LinearLayoutManager) getLayoutManager(); 
     int lastVisibleItemPosition = lm.findLastVisibleItemPosition(); 

     if (lastVisibleItemPosition == RecyclerView.NO_POSITION) { 
      return 0f; 
     } 

     View view = lm.findViewByPosition(lastVisibleItemPosition); 
     float fractionOfView; 

     if (view.getBottom() < getHeight()) { // last visible position is fully visible 
      fractionOfView = 0f; 
     } else { // last view is cut off and partially displayed 
      fractionOfView = (float) (getHeight() - view.getTop())/(float) view.getHeight(); 
     } 
     return lastVisibleItemPosition + fractionOfView; 
    } 

    private static final int ITEM_HEIGHT = 1000; // Arbitrary, make largish for smoother scrolling 
    private static final int UNDEFINED = -1; 
    private static final String ERROR_NOT_AT_TOP_OF_RANGE 
      = "RecyclerView must be positioned at the top of its range."; 
} 

चेतावनियां निम्न समस्याओं क्रियान्वयन के आधार पर संबोधित करने की आवश्यकता हो सकती है।

नमूना कोड केवल लंबवत स्क्रॉलिंग के लिए काम करता है। नमूना कोड यह भी मानता है कि RecyclerView की सामग्री स्थिर हैं। RecyclerView का समर्थन करने वाले डेटा के लिए कोई भी अपडेट स्क्रॉलिंग समस्याओं का कारण बन सकता है। यदि कोई परिवर्तन किया गया है जो RecyclerView की पहली पूर्ण स्क्रीन पर प्रदर्शित किसी भी दृश्य की ऊंचाई को प्रभावित करता है, तो स्क्रॉलिंग बंद हो जाएगी। नीचे दिए गए परिवर्तन शायद ठीक काम करेंगे। यह स्क्रॉलिंग ऑफसेट की गणना कैसे करता है इस कारण है।

स्क्रॉलिंग ऑफसेट के लिए मूल मान निर्धारित करने के लिए, (परिवर्तनीय mTopCutOff), रीसाइक्लर व्यू को पहली बार शीर्ष पर स्क्रॉल किया जाना चाहिए computeVerticalScrollRange() आविष्कार किया गया है ताकि विचारों को मापा जा सके; अन्यथा, कोड "अवैध स्तर अपवाद" के साथ रुक जाएगा। यह RecyclerView पर स्क्रॉल किया गया है, तो यह विशेष रूप से एक अभिविन्यास परिवर्तन पर परेशानी है। इसके चारों ओर एक आसान तरीका स्क्रॉलिंग स्थिति की बहाली को रोकना होगा ताकि यह अभिविन्यास परिवर्तन पर शीर्ष पर डिफ़ॉल्ट हो।

(निम्नलिखित शायद सबसे अच्छा समाधान नहीं है ...)

var lm: LinearLayoutManager = object : LinearLayoutManager(this) { 
    override fun onRestoreInstanceState(state: Parcelable?) { 
     // Don't restore 
    } 
} 

मुझे आशा है कि इस मदद करता है। (बीटीडब्ल्यू, आपके एमसीवी ने इसे बहुत आसान बना दिया।)

+0

बहुत अच्छी तरह से काम करता है, बहुत बहुत धन्यवाद! इसके अलावा: "याद रखने की मुख्य बात यह है कि स्क्रॉल रेंज मनमाने ढंग से है", महान खोज, मैंने पिक्सेल में आकारों की गणना करने की कोशिश की। – FWeigl

+0

@ एस्कोरबिन उपहार के लिए धन्यवाद। मैं अपना उदाहरण काम करने के तरीके से थोड़ा असंतुष्ट था - अंगूठे सही समय पर नहीं उतरी थी और कुछ अन्य चीजें जो मुझे परेशान करती थीं। मेरे पास एक अपडेट है और यदि आप रुचि रखते हैं तो मेरे उत्तर में एक संपादन पोस्ट करेंगे। मैं आम तौर पर स्वीकार किए गए उत्तरों को अपडेट करने के लिए वापस जाना पसंद नहीं करता, लेकिन जो भी मैं पोस्ट करता हूं वह काफी अलग नहीं होता है और सहायक हो सकता है। – Cheticamp

+0

कूल, आगे देख रहे हैं। – FWeigl

1

मैं विशेषता android:scollBarSize="Xdp" गलत नहीं कर रहा हूँ, तो आप के लिए काम करना चाहिए। इसे अपने RecyclerView xml में जोड़ें।

इस तरह आप आकार तय करते हैं, और यह तय रहेगा।

+0

मिल गया है, दुर्भाग्यवश स्क्रॉलबार्स साइज स्क्रॉलबार के क्षैतिज आकार/चौड़ाई को परिभाषित करता है, न कि इसकी ऊंचाई। – FWeigl

1

स्क्रॉल प्रगति के मीट्रिक के रूप में आइटम की स्थिति का उपयोग करें। यह आपके स्क्रॉल सूचक को थोड़ा अजीब बनने का कारण बनता है, लेकिन कम से कम यह निश्चित आकार के बने रहेगा।

RecyclerView के लिए कस्टम स्क्रॉल संकेतक के कई कार्यान्वयन हैं। फास्ट स्क्रोलर के रूप में सबसे दोगुना।

पर पर आधारित है।

  • स्टोर वर्तमान
  • एनीमेशन के दौरान ऑफसेट ऑफसेट अंगूठे देखें की स्थिति के माध्यम से

    View#offset* कॉल
  • लेआउट सेट के दौरान: मूल रूप से, एक एक कस्टम दृश्य उपवर्ग, कि एनिमेट किया जाएगा बनाने के लिए, इसी तरह scrollview और DrawerLayout करने के लिए है वर्तमान ऑफसेट के आधार पर स्थिति।

शायद आप अब उस जादू को सीखना नहीं चाहते हैं, बस कुछ मौजूदा फास्ट स्क्रॉलिंग लाइब्रेरी (रीसाइक्लर व्यूफैस्टस्क्रॉलर या इसके क्लोन का उपयोग करें) का उपयोग करें।

संबंधित मुद्दे