android - CardView wrap its content dynamically -
i have following card in layout:
<android.support.v7.widget.cardview android:id="@+id/card1" android:layout_width="match_parent" android:layout_marginbottom="6dp"> <relativelayout android:id="@+id/content" android:layout_width="match_parent" android:layout_height="wrap_content"> <textview android:id="@+id/label" android:layout_width="match_parent" android:layout_height="wrap_content" /> <!-- [a recyclerview list linearlayout header] --> <include android:id="@+id/table" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_above="@+id/total_label" android:layout_below="@id/label" android:layout_marginbottom="4dp" android:layout_margintop="4dp" /> <!-- [a linearlayout 2 textviews] --> <include android:id="@+id/total_label" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignparentbottom="true" /> </relativelayout> </android.support.v7.widget.cardview> what try achieve click on label textview , hide other content card (let visible label component). again, clicking further label expand hidden content. can if set explicitly height of card i.e. 400dp, way if containing recyclerview used list have not many items (which same height , fixed) there empty space. how can card wrap content? (when use wrap_content in layout_height of card label visible).
this click handler card:
card.setonclicklistener(new view.onclicklistener() { @override public void onclick(view v) { resources r = getresources(); if (toggle) { tablecontainer.setvisibility(view.gone); totallabel.setvisibility(view.gone); linearlayout.layoutparams params = new linearlayout.layoutparams(viewgroup.layoutparams.match_parent, viewgroup.layoutparams.wrap_content); float pxtopmargin = typedvalue.applydimension(typedvalue.complex_unit_dip, getresources().getdimension(r.dimen.card_list_margin_top), r.getdisplaymetrics()); params.topmargin = (int) pxtopmargin; card.setlayoutparams(params); card.setvisibility(view.gone); card.setvisibility(view.visible); } else { tablecontainer.setvisibility(view.visible); totallabel.setvisibility(view.visible); float pxheight = typedvalue.applydimension(typedvalue.complex_unit_dip, 400, r.getdisplaymetrics()); linearlayout.layoutparams params = new linearlayout.layoutparams(viewgroup.layoutparams.match_parent, (int) pxheight); float pxtopmargin = typedvalue.applydimension(typedvalue.complex_unit_dip, getresources().getdimension(r.dimen.card_list_margin_top), r.getdisplaymetrics()); params.topmargin = (int) pxtopmargin; params.bottommargin = (int) pxtopmargin; card.setlayoutparams(params); card.setvisibility(view.gone); card.setvisibility(view.visible); } toggle = !toggle; } }); solution
ok problem create list using recyclerview has property:
a specific number of rows visible (i.e. 15) , if lower wrap content
i found other thread (comment se.solovyev) custom linear layout , edited little. result satisfying (although don't know if did bad in there, works).
package com.trafficbroker.trafficbroker.views; import android.content.context; import android.graphics.rect; import android.support.v4.view.viewcompat; import android.support.v7.widget.recyclerview; import android.util.log; import android.view.view; import java.lang.reflect.field; /** * {@link android.support.v7.widget.linearlayoutmanager} wraps content. note class * wrap content regardless of {@link android.support.v7.widget.recyclerview} layout parameters. * <p/> * it's impossible run add/remove animations child views have arbitrary dimensions (height * vertical orientation , width horizontal). if child views have fixed dimensions * {@link #setchildsize(int)} method might used let layout manager know how big going be. * if animations not used @ normal measuring procedure run , child views measured during * measure pass. */ public class wrappinglinearlayoutmanager extends android.support.v7.widget.linearlayoutmanager { 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 wrappinglinearlayoutmanager(context context) { super(context); this.view = null; } @suppresswarnings("unuseddeclaration") public wrappinglinearlayoutmanager(context context, int orientation, boolean reverselayout) { super(context, orientation, reverselayout); this.view = null; } @suppresswarnings("unuseddeclaration") public wrappinglinearlayoutmanager(recyclerview view) { super(view.getcontext()); this.view = view; this.overscrollmode = viewcompat.getoverscrollmode(view); } @suppresswarnings("unuseddeclaration") public wrappinglinearlayoutmanager(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 = (getitemcount() < 15 ? getitemcount() : 15);//getitemcount(); // max visible rows // 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("linearlayoutmanager", "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("linearlayoutmanager", "linearlayoutmanager 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, hpadding + hmargin + hdecoration, p.width, canscrollhorizontally()); final int childheightspec = getchildmeasurespec(heightsize, 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("linearlayoutmanager", "can't make layoutparams insets dirty, decorations measurements might incorrect"); } } }
i did example close yours could, key seems be
card.requestlayout(); here's had cardview
<android.support.v7.widget.cardview android:id="@+id/card1" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginbottom="6dp"> <relativelayout android:id="@+id/content" android:layout_width="match_parent" android:layout_height="wrap_content"> <textview android:id="@+id/label" android:layout_width="match_parent" android:layout_height="wrap_content" /> <!-- [a recyclerview list linearlayout header] --> <include layout="@layout/table_container" android:id="@+id/table" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/label" android:layout_marginbottom="4dp" android:layout_margintop="4dp" /> <!-- [a linearlayout 2 textviews] --> <include layout="@layout/total_label" android:id="@+id/total_label" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/table" /> </relativelayout> </android.support.v7.widget.cardview> here's had listener
card.setonclicklistener(new view.onclicklistener() { @override public void onclick(view v) { if (toggle) { tablecontainer.setvisibility(view.gone); totallabel.setvisibility(view.gone); //viewgroup.layoutparams params = tablecontainer.getlayoutparams(); //params.height = 1500; //tablecontainer.setlayoutparams(params); } else { tablecontainer.setvisibility(view.visible); totallabel.setvisibility(view.visible); //viewgroup.layoutparams params = tablecontainer.getlayoutparams(); //params.height = 1500; //tablecontainer.setlayoutparams(params); } toggle = !toggle; card.requestlayout(); } }); here's example of 1 of layouts go include tags.
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="wrap_content" android:layout_width="match_parent" > <textview android:id="@+id/total_labeltv" android:layout_width="match_parent" android:text="total label" android:background="#c0c0c0" android:layout_height="wrap_content"> </textview></relativelayout>
Comments
Post a Comment