RecyclerView嵌套ScrollView

ScrollView 属性设置  

android:fillViewport="true"

RecyclerView属性设置

android:layout_height="wrap_content"

设置RecyclerView的LinearLayoutManager

LinearLayoutManager mLayoutManager =newJSFullyLinearLayoutManager(getActivity());

rvRecycler.setLayoutManager(mLayoutManager);


支持RecyclerView嵌套ScrollView的自定义LinearLayoutManager

public class JSFullyLinearLayoutManager extends LinearLayoutManager {

private static booleancanMakeInsetsDirty=true;

private staticFieldinsetsDirtyField=null;

private static final intCHILD_WIDTH=0;

private static final intCHILD_HEIGHT=1;

private static final intDEFAULT_CHILD_SIZE=100;

private final int[]childDimensions=new int[2];

private finalRecyclerViewview;

private intchildSize=DEFAULT_CHILD_SIZE;

private booleanhasChildSize;

private intoverScrollMode= ViewCompat.OVER_SCROLL_ALWAYS;

private finalRecttmpRect=newRect();

publicJSFullyLinearLayoutManager(Context context) {

super(context);

this.view=null;

}

publicJSFullyLinearLayoutManager(Context context, intorientation, booleanreverseLayout) {

super(context,orientation,reverseLayout);

this.view=null;

}

publicJSFullyLinearLayoutManager(RecyclerView view) {

super(view.getContext());

this.view= view;

this.overScrollMode= ViewCompat.getOverScrollMode(view);

}

publicJSFullyLinearLayoutManager(RecyclerView view, intorientation, booleanreverseLayout) {

super(view.getContext(),orientation,reverseLayout);

this.view= view;

this.overScrollMode= ViewCompat.getOverScrollMode(view);

}

public voidsetOverScrollMode(intoverScrollMode) {

if(overScrollMode < ViewCompat.OVER_SCROLL_ALWAYS|| overScrollMode > ViewCompat.OVER_SCROLL_NEVER)

throw newIllegalArgumentException("Unknown overscroll mode: "+ overScrollMode);

if(this.view==null)throw newIllegalStateException("view == null");

this.overScrollMode= overScrollMode;

ViewCompat.setOverScrollMode(view,overScrollMode);

}

public static intmakeUnspecifiedSpec() {

returnView.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED);

}

@Override

public voidonMeasure(RecyclerView.Recycler recycler,RecyclerView.State state, intwidthSpec, intheightSpec) {

final intwidthMode = View.MeasureSpec.getMode(widthSpec);

final intheightMode = View.MeasureSpec.getMode(heightSpec);

final intwidthSize = View.MeasureSpec.getSize(widthSpec);

final intheightSize = View.MeasureSpec.getSize(heightSpec);

final booleanhasWidthSize = widthMode != View.MeasureSpec.UNSPECIFIED;

final booleanhasHeightSize = heightMode != View.MeasureSpec.UNSPECIFIED;

final booleanexactWidth = widthMode == View.MeasureSpec.EXACTLY;

final booleanexactHeight = heightMode == View.MeasureSpec.EXACTLY;

final intunspecified =makeUnspecifiedSpec();

if(exactWidth && exactHeight) {

// in case of exact calculations for both dimensions let's use default "onMeasure" implementation

super.onMeasure(recycler,state,widthSpec,heightSpec);

return;

}

final booleanvertical = getOrientation() ==VERTICAL;

initChildDimensions(widthSize,heightSize,vertical);

intwidth =0;

intheight =0;

// it's possible to get scrap views in recycler which are bound to old (invalid) adapter entities. This

// happens because their invalidation happens after "onMeasure" method. As a workaround let's clear the

// recycler now (it should not cause any performance issues while scrolling as "onMeasure" is never

// called whiles scrolling)

recycler.clear();

final intstateItemCount = state.getItemCount();

final intadapterItemCount = getItemCount();

// adapter always contains actual data while state might contain old data (f.e. data before the animation is

// done). As we want to measure the view with actual data we must use data from the adapter and not from  the

// state

for(inti =0;i < adapterItemCount;i++) {

if(vertical) {

if(!hasChildSize) {

if(i < stateItemCount) {

// we should not exceed state count, otherwise we'll get IndexOutOfBoundsException. For such items

// we will use previously 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) {

// we should not exceed state count, otherwise we'll get IndexOutOfBoundsException. For such items

// we will use previously 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 booleanfit = (vertical && (!hasHeightSize || height < heightSize))

|| (!vertical && (!hasWidthSize || width < widthSize));

ViewCompat.setOverScrollMode(view,fit ? ViewCompat.OVER_SCROLL_NEVER: ViewCompat.OVER_SCROLL_ALWAYS);

}

}

private voidlogMeasureWarning(intchild) {

if(BuildConfig.DEBUG) {

Log.w("LinearLayoutManager","Can't measure child #"+ child +", previously used dimensions will be reused."+

"To remove this message either use #setChildSize() method or don't run RecyclerView animations");

}

}

private voidinitChildDimensions(intwidth, intheight, booleanvertical) {

if(childDimensions[CHILD_WIDTH] !=0||childDimensions[CHILD_HEIGHT] !=0) {

// already initialized, skipping

return;

}

if(vertical) {

childDimensions[CHILD_WIDTH] = width;

childDimensions[CHILD_HEIGHT] =childSize;

}else{

childDimensions[CHILD_WIDTH] =childSize;

childDimensions[CHILD_HEIGHT] = height;

}

}

@Override

public voidsetOrientation(intorientation) {

// might be called before the constructor of this class is called

//noinspection ConstantConditions

if(childDimensions!=null) {

if(getOrientation() != orientation) {

childDimensions[CHILD_WIDTH] =0;

childDimensions[CHILD_HEIGHT] =0;

}

}

super.setOrientation(orientation);

}

public voidclearChildSize() {

hasChildSize=false;

setChildSize(DEFAULT_CHILD_SIZE);

}

public voidsetChildSize(intchildSize) {

hasChildSize=true;

if(this.childSize!= childSize) {

this.childSize= childSize;

requestLayout();

}

}

private voidmeasureChild(RecyclerView.Recycler recycler, intposition, intwidthSize, intheightSize, int[] dimensions) {

finalView child;

try{

child = recycler.getViewForPosition(position);

}catch(IndexOutOfBoundsException e) {

if(BuildConfig.DEBUG) {

Log.w("LinearLayoutManager","LinearLayoutManager doesn't work well with animations. Consider switching them off",e);

}

return;

}

finalRecyclerView.LayoutParams p = (RecyclerView.LayoutParams) child.getLayoutParams();

final inthPadding = getPaddingLeft() + getPaddingRight();

final intvPadding = getPaddingTop() + getPaddingBottom();

final inthMargin = p.leftMargin+ p.rightMargin;

final intvMargin = p.topMargin+ p.bottomMargin;

// we must make insets dirty in order calculateItemDecorationsForChild to work

makeInsetsDirty(p);

// this method should be called before any getXxxDecorationXxx() methods

calculateItemDecorationsForChild(child,tmpRect);

final inthDecoration = getRightDecorationWidth(child) + getLeftDecorationWidth(child);

final intvDecoration = getTopDecorationHeight(child) + getBottomDecorationHeight(child);

final intchildWidthSpec =getChildMeasureSpec(widthSize,hPadding + hMargin + hDecoration,p.width,canScrollHorizontally());

final intchildHeightSpec =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;

// as view is recycled let's not keep old measured values

makeInsetsDirty(p);

recycler.recycleView(child);

}

private static voidmakeInsetsDirty(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 voidonMakeInsertDirtyFailed() {

canMakeInsetsDirty=false;

if(BuildConfig.DEBUG) {

Log.w("LinearLayoutManager","Can't make LayoutParams insets dirty, decorations measurements might be incorrect");

}

}

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,376评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,126评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,966评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,432评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,519评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,792评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,933评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,701评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,143评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,488评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,626评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,292评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,896评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,742评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,977评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,324评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,494评论 2 348

推荐阅读更多精彩内容

  • 记录一下ScrollVIew嵌套Recyclerview遇到的问题 1、先重写LinearLayoutManage...
    Evil_c2e1阅读 649评论 0 1
  • 需求总是在变,你的代码千万别写死? (就当做是工作不饱和吧,每天上午将bug fix 完之后,从下午就开始干坐到晚...
    Themores阅读 29,379评论 9 29
  • 这篇文章分三个部分,简单跟大家讲一下 RecyclerView 的常用方法与奇葩用法;工作原理与ListView比...
    LucasAdam阅读 4,379评论 0 27
  • 1.实现该功能需要在Eclipse工具的SDK路径下添加 android-5.0,1 android-5.1 an...
    ZebraWei阅读 1,637评论 0 3
  • 她吃着我的饭, 她穿着我的衣, 她睡着我的觉, 她做着我的梦, 她上着我的学, 她读着我的书, 她谈着我的恋, 她...
    油腻司机阅读 235评论 0 0