android - Make RecyclerView height dynamic by wrapping specific number of it's child(not all child) -
i want make recyclerview height wrap specific number of it's child views. made work using custom layoutmanager:
public class wraptoitemcountlinearlayoutmanager extends android.support.v7.widget.linearlayoutmanager { public int numberofchild = 0; private static boolean canmakeinsetsdirty = true; private static field insetsdirtyfield = null; private static final int child_width = 0; private static final int child_height = 1; private static final int default_child_size = 100; private final int[] childdimensions = new int[2]; private final recyclerview view; private int childsize = default_child_size; private boolean haschildsize; private int overscrollmode = viewcompat.over_scroll_always; private final rect tmprect = new rect(); @suppresswarnings("unuseddeclaration") public wraptoitemcountlinearlayoutmanager(context context) { super(context); this.view = null; } @suppresswarnings("unuseddeclaration") public wraptoitemcountlinearlayoutmanager(context context, int orientation, boolean reverselayout) { super(context, orientation, reverselayout); this.view = null; } @suppresswarnings("unuseddeclaration") public wraptoitemcountlinearlayoutmanager(recyclerview view) { super(view.getcontext()); this.view = view; this.overscrollmode = viewcompat.getoverscrollmode(view); } @suppresswarnings("unuseddeclaration") public wraptoitemcountlinearlayoutmanager(recyclerview view, int orientation, boolean reverselayout) { super(view.getcontext(), orientation, reverselayout); this.view = view; this.overscrollmode = viewcompat.getoverscrollmode(view); } public void setoverscrollmode(int overscrollmode) { if (overscrollmode < viewcompat.over_scroll_always || overscrollmode > viewcompat.over_scroll_never) throw new illegalargumentexception("unknown overscroll mode: " + overscrollmode); if (this.view == null) throw new illegalstateexception("view == null"); this.overscrollmode = overscrollmode; viewcompat.setoverscrollmode(view, overscrollmode); } public static int makeunspecifiedspec() { return view.measurespec.makemeasurespec(0, view.measurespec.unspecified); } @override public void onmeasure(recyclerview.recycler recycler, recyclerview.state state, int widthspec, int heightspec) { final int widthmode = view.measurespec.getmode(widthspec); final int heightmode = view.measurespec.getmode(heightspec); final int widthsize = view.measurespec.getsize(widthspec); final int heightsize = view.measurespec.getsize(heightspec); final boolean haswidthsize = widthmode != view.measurespec.unspecified; final boolean hasheightsize = heightmode != view.measurespec.unspecified; final boolean exactwidth = widthmode == view.measurespec.exactly; final boolean exactheight = heightmode == view.measurespec.exactly; final int unspecified = makeunspecifiedspec(); if (exactwidth && exactheight) { // in case of exact calculations both dimensions let's use default "onmeasure" implementation super.onmeasure(recycler, state, widthspec, heightspec); return; } final boolean vertical = getorientation() == vertical; initchilddimensions(widthsize, heightsize, vertical); int width = 0; int height = 0; // it's possible scrap views in recycler bound old (invalid) adapter entities. // happens because invalidation happens after "onmeasure" method. workaround let's clear // recycler (it should not cause performance issues while scrolling "onmeasure" never // called whiles scrolling) recycler.clear(); final int stateitemcount = state.getitemcount(); final int adapteritemcount = numberofchild; // adapter contains actual data while state might contain old data (f.e. data before animation // done). want measure view actual data must use data adapter , not // state (int = 0; < adapteritemcount; i++) { if (vertical) { if (!haschildsize) { if (i < stateitemcount) { // should not exceed state count, otherwise we'll indexoutofboundsexception. such items // use calculated dimensions measurechild(recycler, i, widthsize, unspecified, childdimensions); } else { logmeasurewarning(i); } } height += childdimensions[child_height]; if (i == 0) { width = childdimensions[child_width]; } if (hasheightsize && height >= heightsize) { break; } } else { if (!haschildsize) { if (i < stateitemcount) { // should not exceed state count, otherwise we'll indexoutofboundsexception. such items // use calculated dimensions measurechild(recycler, i, unspecified, heightsize, childdimensions); } else { logmeasurewarning(i); } } width += childdimensions[child_width]; if (i == 0) { height = childdimensions[child_height]; } if (haswidthsize && width >= widthsize) { break; } } } if (exactwidth) { width = widthsize; } else { width += getpaddingleft() + getpaddingright(); if (haswidthsize) { width = math.min(width, widthsize); } } if (exactheight) { height = heightsize; } else { height += getpaddingtop() + getpaddingbottom(); if (hasheightsize) { height = math.min(height, heightsize); } } setmeasureddimension(width, height); if (view != null && overscrollmode == viewcompat.over_scroll_if_content_scrolls) { final boolean fit = (vertical && (!hasheightsize || height < heightsize)) || (!vertical && (!haswidthsize || width < widthsize)); viewcompat.setoverscrollmode(view, fit ? viewcompat.over_scroll_never : viewcompat.over_scroll_always); } } private void logmeasurewarning(int child) { if (buildconfig.debug) { log.w("", "wraptoitemcountlinearlayoutmanager can't measure child #" + child + ", used dimensions reused." + "to remove message either use #setchildsize() method or don't run recyclerview animations"); } } private void initchilddimensions(int width, int height, boolean vertical) { if (childdimensions[child_width] != 0 || childdimensions[child_height] != 0) { // initialized, skipping return; } if (vertical) { childdimensions[child_width] = width; childdimensions[child_height] = childsize; } else { childdimensions[child_width] = childsize; childdimensions[child_height] = height; } } @override public void setorientation(int orientation) { // might called before constructor of class called //noinspection constantconditions if (childdimensions != null) { if (getorientation() != orientation) { childdimensions[child_width] = 0; childdimensions[child_height] = 0; } } super.setorientation(orientation); } public void clearchildsize() { haschildsize = false; setchildsize(default_child_size); } public void setchildsize(int childsize) { haschildsize = true; if (this.childsize != childsize) { this.childsize = childsize; requestlayout(); } } private void measurechild(recyclerview.recycler recycler, int position, int widthsize, int heightsize, int[] dimensions) { final view child; try { child = recycler.getviewforposition(position); } catch (indexoutofboundsexception e) { if (buildconfig.debug) { log.w("", "wraptoitemcountlinearlayoutmanager doesn't work animations. consider switching them off", e); } return; } final recyclerview.layoutparams p = (recyclerview.layoutparams) child.getlayoutparams(); final int hpadding = getpaddingleft() + getpaddingright(); final int vpadding = getpaddingtop() + getpaddingbottom(); final int hmargin = p.leftmargin + p.rightmargin; final int vmargin = p.topmargin + p.bottommargin; // must make insets dirty in order calculateitemdecorationsforchild work makeinsetsdirty(p); // method should called before getxxxdecorationxxx() methods calculateitemdecorationsforchild(child, tmprect); final int hdecoration = getrightdecorationwidth(child) + getleftdecorationwidth(child); final int vdecoration = gettopdecorationheight(child) + getbottomdecorationheight(child); final int childwidthspec = getchildmeasurespec(widthsize, view.measurespec.at_most, hpadding + hmargin + hdecoration, p.width, canscrollhorizontally()); final int childheightspec = getchildmeasurespec(heightsize, view.measurespec.at_most, vpadding + vmargin + vdecoration, p.height, canscrollvertically()); child.measure(childwidthspec, childheightspec); dimensions[child_width] = getdecoratedmeasuredwidth(child) + p.leftmargin + p.rightmargin; dimensions[child_height] = getdecoratedmeasuredheight(child) + p.bottommargin + p.topmargin; // view recycled let's not keep old measured values makeinsetsdirty(p); recycler.recycleview(child); } private static void makeinsetsdirty(recyclerview.layoutparams p) { if (!canmakeinsetsdirty) { return; } try { if (insetsdirtyfield == null) { insetsdirtyfield = recyclerview.layoutparams.class.getdeclaredfield("minsetsdirty"); insetsdirtyfield.setaccessible(true); } insetsdirtyfield.set(p, true); } catch (nosuchfieldexception e) { onmakeinsertdirtyfailed(); } catch (illegalaccessexception e) { onmakeinsertdirtyfailed(); } } private static void onmakeinsertdirtyfailed() { canmakeinsetsdirty = false; if (buildconfig.debug) { log.w("", "wraptoitemcountlinearlayoutmanager can't make layoutparams insets dirty, decorations measurements might incorrect"); } } }
in way specified numberofchild wrap number of child in recyclerview. working until support library version 23.2.1 came fixing recyclerview wrap content option. layoutmanager not behaving likewise(it's wrapping recyclerview height child's height). need fix solve issue using latest support library version.
i've searched s/o solution couldn't find one. there have found , solve issue?
Comments
Post a Comment