2015-11-24 16 views
13

में चिपचिपा पाद लेख लागू मैं RecyclerView है और मैं अगले व्यवहार की जरूरत है:कैसे recyclerview

  • अगर वहाँ मदों की एक बहुत हैं (अधिक फिर स्क्रीन फिट बैठता है) - पाद लेख पिछले आइटम
  • है अगर कुछ आइटम/कोई आइटम - पाद लेख स्क्रीन नीचे

कृपया सलाह दें कि मैं इस व्यवहार को कैसे कार्यान्वित कर सकता हूं।

+0

आप स्क्रीन के नीचे करने के लिए एक और लेआउट के साथ अपनी अंतिम आइटम डाल सकते हैं और आप के शीर्ष पर अपनी recyclerview डाल सकते हैं यह भी होगा। इसलिए यह हमेशा नीचे रहेगा और आपके रीसायकलव्यू को – nomad

+0

नामांकित नहीं किया जाएगा, क्षमा करें, लेकिन इसकी अपेक्षा नहीं की जाती है: यदि मेरे पास अंतिम रीसाइक्लिंगव्यू आइटम पारदर्शी होगा - तो अंतिम आइटम (मेरे पाद लेख) पर उचित एनीमेशन नहीं होगा (अंतिम -1 आइटम के चिपचिपा होने की बजाय यह नीचे से दिखाई देगा)। मुझे आशा है कि आप मेरे दिमाग को समझ चुके हैं। – resource8218

+0

चेकआउट https://gist.github.com/mheras/0908873267def75dc746 और यह SO उत्तर: http://stackoverflow.com/a/29168617/3140227 –

उत्तर

6

आप इस व्यवहार को लागू करने के लिए RecyclerView.ItemDecoration का उपयोग कर सकते हैं।

public class StickyFooterItemDecoration extends RecyclerView.ItemDecoration { 

    /** 
    * Top offset to completely hide footer from the screen and therefore avoid noticeable blink during changing position of the footer. 
    */ 
    private static final int OFF_SCREEN_OFFSET = 5000; 

    @Override 
    public void getItemOffsets(Rect outRect, final View view, final RecyclerView parent, RecyclerView.State state) { 
     int adapterItemCount = parent.getAdapter().getItemCount(); 
     if (isFooter(parent, view, adapterItemCount)) { 
      //For the first time, each view doesn't contain any parameters related to its size, 
      //hence we can't calculate the appropriate offset. 
      //In this case, set a big top offset and notify adapter to update footer one more time. 
      //Also, we shouldn't do it if footer became visible after scrolling. 
      if (view.getHeight() == 0 && state.didStructureChange()) { 
       hideFooterAndUpdate(outRect, view, parent); 
      } else { 
       outRect.set(0, calculateTopOffset(parent, view, adapterItemCount), 0, 0); 
      } 
     } 
    } 

    private void hideFooterAndUpdate(Rect outRect, final View footerView, final RecyclerView parent) { 
     outRect.set(0, OFF_SCREEN_OFFSET, 0, 0); 
     footerView.post(new Runnable() { 
      @Override 
      public void run() { 
       parent.getAdapter().notifyDataSetChanged(); 
      } 
     }); 
    } 

    private int calculateTopOffset(RecyclerView parent, View footerView, int itemCount) { 
     int topOffset = parent.getHeight() - visibleChildsHeightWithFooter(parent, footerView, itemCount); 
     return topOffset < 0 ? 0 : topOffset; 
    } 

    private int visibleChildsHeightWithFooter(RecyclerView parent, View footerView, int itemCount) { 
     int totalHeight = 0; 
     //In the case of dynamic content when adding or removing are possible itemCount from the adapter is reliable, 
     //but when the screen can fit fewer items than in adapter, getChildCount() from RecyclerView should be used. 
     int onScreenItemCount = Math.min(parent.getChildCount(), itemCount); 
     for (int i = 0; i < onScreenItemCount - 1; i++) { 
      totalHeight += parent.getChildAt(i).getHeight(); 
     } 
     return totalHeight + footerView.getHeight(); 
    } 

    private boolean isFooter(RecyclerView parent, View view, int itemCount) { 
     return parent.getChildAdapterPosition(view) == itemCount - 1; 
    } 
} 

RecyclerView ऊंचाई के लिए match_parent स्थापित करने के लिए सुनिश्चित करें।

कृपया नमूना आवेदन https://github.com/JohnKuper/recyclerview-sticky-footer पर एक नजर है और यह कैसे काम करता http://sendvid.com/nbpj0806

इस समाधान का एक बड़ा दोष यह है इसे सही ढंग से केवल notifyDataSetChanged() एक आवेदन पत्र भर (अंदर नहीं सजावट) के बाद काम करता है। अधिक विशिष्ट अधिसूचनाओं के साथ यह ठीक से काम नहीं करेगा और उनका समर्थन करने के लिए, इसे एक और तर्क की आवश्यकता है। इसके अलावा, आप लाइब्रेरी रीसाइक्लर्विव-स्टिकहेडर्स से eowise द्वारा अंतर्दृष्टि प्राप्त कर सकते हैं और इस समाधान को बेहतर बना सकते हैं।

+0

'getItemOffset' में' NotifyDataSetChanged() 'को कॉल करना एक बुरा विचार है, इसका परिणाम अनंत लूप हो सकता है। जो मेरे मामले में हुआ था। – AmeyaB

+0

यह एक बहुत अच्छा समाधान है, केवल एक चीज जिसे मुझे 'visualChildsHeightWithFooter' में जोड़ना था, प्रत्येक दृश्य के लिए शीर्ष और निचला मार्जिन – lelloman

+0

@AmeyaB समाधान के नीचे समस्या का समाधान करता है। –

-1

यदि आप रीसाइक्लर व्यू के बारे में नहीं भूल सकते हैं और ListView का उपयोग नहीं कर सकते हैं, तो इस लिंक को Is there an addHeaderView equivalent for RecyclerView? पर देखें, इसमें आपकी आवश्यकता है। यह हेडर के बारे में है, लेकिन यह काफी समान है, सिवाय इसके कि हेडर आपकी सूची की शुरुआत में है और पाद लेख अंत में है।

+2

आपके लिंक के लिए धन्यवाद! दुर्भाग्यवश, यदि कोई आइटम नहीं है तो स्क्रीन तल पर पाद लेख रखने के लिए कोई समाधान नहीं है। – resource8218

0

मुझे पता है, यह एक पुराना सवाल है, लेकिन मैं उन लोगों के लिए एक उत्तर जोड़ूंगा जो भविष्य में ऐसे निर्णय की खोज करेंगे। यह POSSIBLE स्क्रीन के निचले हिस्से में अंतिम आइटम रखने के लिए है यदि आपके पास केवल कुछ या कोई आइटम नहीं है और जब आपके पास कई आइटम हैं तो रीसाइक्लर्विव के साथ स्क्रॉल करने के लिए अंतिम आइटम बनाते हैं।

कैसे प्राप्त करें। आपके RecyclerView एडाप्टर को कई दृश्य प्रकार लागू करना चाहिए: विचार, जिन्हें सूची आइटम के रूप में दिखाया जाना चाहिए; देखें, जो पाद लेख के रूप में दिखाया जाना चाहिए; एक खाली दृश्य आप रीसाइक्लिंग व्यू को विभिन्न दृश्यों के साथ आइटम कैसे डाल सकते हैं, यह जांच सकते हैं: https://stackoverflow.com/a/29362643/6329995 अपनी मुख्य सूची और पाद लेख दृश्य के बीच एक खाली दृश्य खोजें। फिर खाली दृश्य के लिए ऑनबिंडव्यूहोल्डर में जांचें कि क्या आपकी मुख्य सूची दृश्य और पाद लेख दृश्य सभी स्क्रीन लेते हैं। यदि हां - खाली दृश्य ऊंचाई शून्य पर सेट करें, अन्यथा इसे ऊंचाई पर सेट करें जो आइटम और पाद लेख द्वारा नहीं लिया जाता है। बस इतना ही। जब आप पंक्तियों को हटाते/जोड़ते हैं तो आप गतिशील रूप से उस ऊंचाई को भी अपडेट कर सकते हैं। सूची को अपडेट करने के बाद बस खाली करें आइटम को खाली करें आइटम को खाली करें।

आप अपनी रीसाइक्लिंग व्यू ऊंचाई को match_parent या सटीक ऊंचाई पर भी सेट करेंगे, wrap_content नहीं!

उम्मीद है कि इससे मदद मिलती है।

+0

मुझे लगता है कि कुछ आइटम होने पर यह कैसे काम करता है, लेकिन जब बहुत सारी चीज़ें होती हैं तो आप कैसे सुनिश्चित करते हैं कि पाद लेख आइटम के नीचे है? –

1

मैं वजन के साथ एक लाइनरलेआउट का उपयोग कर रहा हूं। मैंने पाद लेख के लिए कई मान बनाए हैं, यह पूरी तरह से काम करता है।

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:tools="http://schemas.android.com/tools" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:background="@color/white" 
    android:orientation="vertical" 

<include layout="@layout/header" /> 

    <android.support.v7.widget.RecyclerView 
    android:id="@+id/recycleView" 
    android:layout_width="match_parent" 
    android:layout_height="0dp" 
    android:layout_weight="0.5" 
    tools:layout_height="0dp" 
    tools:listitem="@layout/row" /> 

<TextView 
    android:layout_width="match_parent" 
    android:layout_height="0dp" 
    android:layout_weight="@dimen/footer_weight" 
    android:padding="@dimen/extra_padding" 
    android:paddingEnd="@dimen/small_padding" 
    android:paddingLeft="@dimen/small_padding" 
    android:paddingRight="@dimen/small_padding" 
    android:paddingStart="@dimen/small_padding" 
    android:text="@string/contact" 
    android:textColor="@color/grey" /> 

</LinearLayout> 
0

डिमिट्री Korobeynikov पर Improvising और डाटासेट सूचित बुलाने की समस्या को हल करने के लिए बदल

public class StickyFooterItemDecoration extends RecyclerView.ItemDecoration { 

    @Override 
    public void getItemOffsets(Rect outRect, final View view, final RecyclerView parent, 
     RecyclerView.State state) { 

    int position = parent.getChildAdapterPosition(view); 
    int adapterItemCount = parent.getAdapter().getItemCount(); 
    if (adapterItemCount == RecyclerView.NO_POSITION || (adapterItemCount - 1) != position) { 
     return; 
    } 
    outRect.top = calculateTopOffset(parent, view, adapterItemCount); 
    } 


    private int calculateTopOffset(RecyclerView parent, View footerView, int itemCount) { 
    int topOffset = 
     parent.getHeight() - parent.getPaddingTop() - parent.getPaddingBottom() 
      - visibleChildHeightWithFooter(parent, footerView, itemCount); 
    return topOffset < 0 ? 0 : topOffset; 
    } 



    private int visibleChildHeightWithFooter(RecyclerView parent, View footerView, int itemCount) { 
    int totalHeight = 0; 
    int onScreenItemCount = Math.min(parent.getChildCount(), itemCount); 
    for (int i = 0; i < onScreenItemCount - 1; i++) { 
     RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) parent.getChildAt(i) 
      .getLayoutParams(); 
     int height = 
      parent.getChildAt(i).getHeight() + layoutParams.topMargin 
       + layoutParams.bottomMargin; 
     totalHeight += height; 
    } 
    int footerHeight = footerView.getHeight(); 
    if (footerHeight == 0) { 
     fixLayoutSize(footerView, parent); 
     footerHeight = footerView.getHeight(); 
    } 
    footerHeight = footerHeight + footerView.getPaddingBottom() + footerView.getPaddingTop(); 

    return totalHeight + footerHeight; 
    } 

    private void fixLayoutSize(View view, ViewGroup parent) { 
    // Check if the view has a layout parameter and if it does not create one for it 
    if (view.getLayoutParams() == null) { 
     view.setLayoutParams(new ViewGroup.LayoutParams(
      ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); 
    } 

    // Create a width and height spec using the parent as an example: 
    // For width we make sure that the item matches exactly what it measures from the parent. 
    // IE if layout says to match_parent it will be exactly parent.getWidth() 
    int widthSpec = View.MeasureSpec.makeMeasureSpec(parent.getWidth(), View.MeasureSpec.EXACTLY); 
    // For the height we are going to create a spec that says it doesn't really care what is calculated, 
    // even if its larger than the screen 
    int heightSpec = View.MeasureSpec 
     .makeMeasureSpec(parent.getHeight(), View.MeasureSpec.UNSPECIFIED); 

    // Get the child specs using the parent spec and the padding the parent has 
    int childWidth = ViewGroup.getChildMeasureSpec(widthSpec, 
     parent.getPaddingLeft() + parent.getPaddingRight(), view.getLayoutParams().width); 
    int childHeight = ViewGroup.getChildMeasureSpec(heightSpec, 
     parent.getPaddingTop() + parent.getPaddingBottom(), view.getLayoutParams().height); 

    // Finally we measure the sizes with the actual view which does margin and padding changes to the sizes calculated 
    view.measure(childWidth, childHeight); 

    // And now we setup the layout for the view to ensure it has the correct sizes. 
    view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight()); 
    } 
}