关闭

RecyclerView LinearLayoutManager GridLayoutManager StaggeredGridLayoutManager ScrollView 嵌套使用

1840人阅读 评论(1) 收藏 举报
分类:

android-support-v7-recyclerview.jar

android-support-v7-recyclerview-source.jar

xUtils-2.6.14.jar

xUtils-2.6.14-sources.jar

我们的recyclerView有多个layoutmanager,通过重写layoutmanager的方法就可以让recyclerView和ScrollView嵌套了。但是请注意,如果recyclerView很长那么强烈不建议去做嵌套,因为这样recyclerView会在展示的时候立刻展示所有内容,效率极低。

本文的两部分代码来自一个博主的博客,另一个是我自己写的,正好可以完全适用于现有的layoutmanager。大家需要的话可以试试,应该问题不大。

原博主的demo:https://github.com/Frank-Zhu/AndroidRecyclerViewDemo

 

1.LinearLayoutManager和ScrollView嵌套

package com.frankzhu.recyclerviewdemo;

import android.content.Context;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;

/**
 * Author:    ZhuWenWu
 * Version    V1.0
 * Date:      2015/2/26  14:15.
 * Description:
 * Modification  History:
 * Date             Author                Version            Description
 * -----------------------------------------------------------------------------------
 * 2015/2/26        ZhuWenWu            1.0                    1.0
 * Why & What is modified:
 */
public class FullyLinearLayoutManager extends LinearLayoutManager {

    private static final String TAG = FullyLinearLayoutManager.class.getSimpleName();

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

    public FullyLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
        super(context, orientation, reverseLayout);
    }

    private int[] mMeasuredDimension = new int[2];

    @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);

        Log.i(TAG, "onMeasure called. \nwidthMode " + widthMode
                + " \nheightMode " + heightSpec
                + " \nwidthSize " + widthSize
                + " \nheightSize " + heightSize
                + " \ngetItemCount() " + getItemCount());

        int width = 0;
        int height = 0;
        for (int i = 0; i < getItemCount(); i++) {
            measureScrapChild(recycler, i,
                    View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
                    View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
                    mMeasuredDimension);

            if (getOrientation() == HORIZONTAL) {
                width = width + mMeasuredDimension[0];
                if (i == 0) {
                    height = mMeasuredDimension[1];
                }
            } else {
                height = height + mMeasuredDimension[1];
                if (i == 0) {
                    width = mMeasuredDimension[0];
                }
            }
        }
        switch (widthMode) {
            case View.MeasureSpec.EXACTLY:
                width = widthSize;
            case View.MeasureSpec.AT_MOST:
            case View.MeasureSpec.UNSPECIFIED:
        }

        switch (heightMode) {
            case View.MeasureSpec.EXACTLY:
                height = heightSize;
            case View.MeasureSpec.AT_MOST:
            case View.MeasureSpec.UNSPECIFIED:
        }

        setMeasuredDimension(width, height);
    }

    private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec,
                                   int heightSpec, int[] measuredDimension) {
        try {
            View view = recycler.getViewForPosition(0);//fix 动态添加时报IndexOutOfBoundsException

            if (view != null) {
                RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams();

                int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec,
                        getPaddingLeft() + getPaddingRight(), p.width);

                int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec,
                        getPaddingTop() + getPaddingBottom(), p.height);

                view.measure(childWidthSpec, childHeightSpec);
                measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin;
                measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin;
                recycler.recycleView(view);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
        }
    }
}
2.GridLayoutManager和ScrollView进行嵌套

package com.frankzhu.recyclerviewdemo;

import android.content.Context;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;

/**
 * Author:    ZhuWenWu
 * Version    V1.0
 * Date:      2015/2/26  14:14.
 * Description:
 * Modification  History:
 * Date             Author                Version            Description
 * -----------------------------------------------------------------------------------
 * 2015/2/26        ZhuWenWu            1.0                    1.0
 * Why & What is modified:
 */
public class FullyGridLayoutManager extends GridLayoutManager {
    public FullyGridLayoutManager(Context context, int spanCount) {
        super(context, spanCount);
    }

    public FullyGridLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout) {
        super(context, spanCount, orientation, reverseLayout);
    }

    private int[] mMeasuredDimension = new int[2];

    @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);

        int width = 0;
        int height = 0;
        int count = getItemCount();
        int span = getSpanCount();
        for (int i = 0; i < count; i++) {
            measureScrapChild(recycler, i,
                    View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
                    View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
                    mMeasuredDimension);

            if (getOrientation() == HORIZONTAL) {
                if (i % span == 0) {
                    width = width + mMeasuredDimension[0];
                }
                if (i == 0) {
                    height = mMeasuredDimension[1];
                }
            } else {
                if (i % span == 0) {
                    height = height + mMeasuredDimension[1];
                }
                if (i == 0) {
                    width = mMeasuredDimension[0];
                }
            }
        }

        switch (widthMode) {
            case View.MeasureSpec.EXACTLY:
                width = widthSize;
            case View.MeasureSpec.AT_MOST:
            case View.MeasureSpec.UNSPECIFIED:
        }

        switch (heightMode) {
            case View.MeasureSpec.EXACTLY:
                height = heightSize;
            case View.MeasureSpec.AT_MOST:
            case View.MeasureSpec.UNSPECIFIED:
        }

        setMeasuredDimension(width, height);
    }

    private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec,
                                   int heightSpec, int[] measuredDimension) {
        if (position < getItemCount()) {
            try {
                View view = recycler.getViewForPosition(0);//fix 动态添加时报IndexOutOfBoundsException
                if (view != null) {
                    RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams();
                    int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec,
                            getPaddingLeft() + getPaddingRight(), p.width);
                    int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec,
                            getPaddingTop() + getPaddingBottom(), p.height);
                    view.measure(childWidthSpec, childHeightSpec);
                    measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin;
                    measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin;
                    recycler.recycleView(view);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
3.StaggeredGridLayoutManager和ScrollView进行嵌套

package com.kale.waterfalldemo.extra.RecyclerView;

import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.view.View;
import android.view.ViewGroup;

/**
 * @author Jack Tony
 * @brief 不规则排列(类似于瀑布流)的布局管理器
 * @date 2015/4/6
 */
public class ExStaggeredGridLayoutManager extends StaggeredGridLayoutManager {

    public ExStaggeredGridLayoutManager(int spanCount, int orientation) {
        super(spanCount, orientation);
    }

    // 尺寸的数组,[0]是宽,[1]是高
    private int[] measuredDimension = new int[2];

    // 用来比较同行/列那个item罪宽/高
    private int[] dimension;


    @Override

    public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) {
        // 宽的mode+size
        final int widthMode = View.MeasureSpec.getMode(widthSpec);
        final int widthSize = View.MeasureSpec.getSize(widthSpec);
        // 高的mode + size
        final int heightMode = View.MeasureSpec.getMode(heightSpec);
        final int heightSize = View.MeasureSpec.getSize(heightSpec);

        // 自身宽高的初始值
        int width = 0;
        int height = 0;
        // item的数目
        int count = getItemCount();
        // item的列数
        int span = getSpanCount();
        // 根据行数或列数来创建数组
        dimension = new int[span];

        for (int i = 0; i < count; i++) {
            measureScrapChild(recycler, i,
                    View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
                    View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED), measuredDimension);

           // 如果是竖直的列表,计算item的高,否则计算宽度
            //Log.d("LISTENER", "position " + i + " height = " + measuredDimension[1]);
            if (getOrientation() == VERTICAL) {
                dimension[findMinIndex(dimension)] += measuredDimension[1];
            } else {
                dimension[findMinIndex(dimension)] += measuredDimension[0];
            }
        }
        if (getOrientation() == VERTICAL) {
            height = findMax(dimension);
        } else {
            width = findMax(dimension);
        }
        

        switch (widthMode) {
            // 当控件宽是match_parent时,宽度就是父控件的宽度
            case View.MeasureSpec.EXACTLY:
                width = widthSize;
                break;
            case View.MeasureSpec.AT_MOST:
                break;
            case View.MeasureSpec.UNSPECIFIED:
                break;
        }
        switch (heightMode) {
            // 当控件高是match_parent时,高度就是父控件的高度
            case View.MeasureSpec.EXACTLY:
                height = heightSize;
                break;
            case View.MeasureSpec.AT_MOST:
                break;
            case View.MeasureSpec.UNSPECIFIED:
                break;
        }
        // 设置测量尺寸  
        setMeasuredDimension(width, height);
    }

    private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec,
            int heightSpec, int[] measuredDimension) {

        // 挨个遍历所有item
        if (position < getItemCount()) {
            try {
                View view = recycler.getViewForPosition(position);//fix 动态添加时报IndexOutOfBoundsException

                if (view != null) {
                    RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) view.getLayoutParams();
                    int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec, getPaddingLeft() + getPaddingRight(), lp.width);
                    int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec, getPaddingTop() + getPaddingBottom(), lp.height);
                    // 子view进行测量,然后可以通过getMeasuredWidth()获得测量的宽,高类似
                    view.measure(childWidthSpec, childHeightSpec);
                    // 将item的宽高放入数组中
                    measuredDimension[0] = view.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
                    measuredDimension[1] = view.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
                    recycler.recycleView(view);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private int findMax(int[] array) {
        int max = array[0];
        for (int value : array) {
            if (value > max) {
                max = value;
            }
        }
        return max;
    }

    /**
     * 得到最数组中最小元素的下标
     *
     * @param array
     * @return
     */
    private int findMinIndex(int[] array) {
        int index = 0;
        int min = array[0];
        for (int i = 0; i < array.length; i++) {
            if (array[i] < min) {
                min = array[i];
                index = i;
            }
        }
        return index;
    }
   
}


============================================================

下面以GridLayoutManager 为使用的案例:

item的xml

注意:这里的margin 是指每个item之间的 间距,你设置padding 是不管用的;

<?xml version="1.0" encoding="utf-8"?>
<com.redoor.rcs.view.CircleImageView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@color/red"
    android:layout_margin="2dp"
    android:id="@+id/recyclerview_item_riv"/>
    
<!-- com.redoor.rcs.view.CircleImageView -->

RecyclerView的xml

            <android.support.v7.widget.RecyclerView
                android:id="@+id/member_details_recyclerview"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:padding="0dp">
            </android.support.v7.widget.RecyclerView>

import android.R.integer;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.ViewHolder;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;

import com.****.R;
import com.****.view.CircleImageView;

public class RecyclerViewAdapterChatMembers extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
	private IOnItemClickListener ionItemClickListener=null;
	private SparseArray<Integer> sparseArray=null;
	private Context  atContext=null;

	private int itemHeight;
	
	public RecyclerViewAdapterChatMembers(
			IOnItemClickListener ionItemClickListener,
			SparseArray<Integer> sparseArray, Context atContext) {
		super();
		this.ionItemClickListener = ionItemClickListener;
		this.sparseArray = sparseArray;
		this.atContext = atContext;
		
		
	}

	/**
	 * @return
	 * 味儿了高度的自使用
	 */
	public int getItemHeight() {
		
		return itemHeight;
	}
	
	@Override
	public int getItemCount() {
		// TODO Auto-generated method stub
		return sparseArray.size();
	}

	@Override
	/** 创建 ViewHolder */
	public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
		View view=LayoutInflater.from(atContext).inflate(R.layout.item_recyclerview_circle_iv, parent,false);
		RecyclerViewHolder recyclerVH=new RecyclerViewHolder(view);//将渲染的view,放到构造器中;
		
		return recyclerVH;
	}
	
	private RecyclerViewHolder rcViewHolder=null;
	/** 绑定 ViewHolder 数据*/
	@Override
	public void onBindViewHolder(ViewHolder holder, final int position) {
		rcViewHolder=(RecyclerViewHolder) holder;
		rcViewHolder.memberIcon.setImageResource(sparseArray.get(position));
		rcViewHolder.memberIcon.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				ionItemClickListener.setOnItemclickListener(v, position);
				
			}
		});
		
	}

	private class RecyclerViewHolder extends RecyclerView.ViewHolder{
		
		/** 成员的圆头像 */
		public CircleImageView memberIcon;
		
		public RecyclerViewHolder(View itemView) {//参数就是布局生成的
			super(itemView);
			memberIcon=(CircleImageView) itemView.findViewById(R.id.recyclerview_item_riv);
			
		}
	}
	
	
	public interface IOnItemClickListener {
		void setOnItemclickListener(View v,int position);
	}
	
}

Activity的使用代码:

	private void initData() {
		membersArr=new SparseArray<Integer>();
		for (int i = 0; i < 13; i++) {
			membersArr.append(i, R.drawable.head_one_icon);
		}
		membersArr.append(membersArr.size(), R.drawable.contacts_add);
		
		
		recyclerViewAdapter=new RecyclerViewAdapterChatMembers(initRecyclerListener(), membersArr, cssContext);
		final int spanCount =5;
		
		//网格布局管理:
		CustomRecyclerViewGridLayoutManager gridLayoutManager=
				new CustomRecyclerViewGridLayoutManager(cssContext, spanCount);
		gridLayoutManager.setOrientation(GridLayoutManager.VERTICAL);
		
		recyclerViewMembers.setLayoutManager(gridLayoutManager);

		recyclerViewMembers.setAdapter(recyclerViewAdapter);
		
	}

	private IOnItemClickListener initRecyclerListener() {
		iOnItemClickListener=new IOnItemClickListener() {
			@Override
			public void setOnItemclickListener(View v, int position) {
				Utils.toast(cssContext, "position = "+position);
				
			}
		};
		return iOnItemClickListener;
	}


























0
0
查看评论

android开发游记:RecycleView 实现复杂首页布局三种方式

做过电商类应用的朋友可能都会遇到一个比较头疼的问题:复杂的首页布局如何实现。参考百度糯米,美团,bilibili等应用,都会发现其首页的布局相对复杂,例如下图bilibili的首页(第二张是demo实现的效果图),可以看到在同一个页面中先是有列表布局出现,然后出现了2列的网格布局,接着3列的网格布局...
  • liaoinstan
  • liaoinstan
  • 2016-09-26 16:01
  • 25174

暴力解决recycleview跟scrollview嵌套问题

scrollview 嵌套recyclerview 时,recyclerview不显示,这就需要我们自己计算recyclerview的高度,比如: ViewGroup.LayoutParams mParams = recyclerView.getLayoutParams(); m...
  • u012721933
  • u012721933
  • 2016-03-22 10:59
  • 5505

使用StaggeredGridLayoutManager实现交错式网格布局

一直在想着怎么描述这样的布局,毕竟用的是瀑布流的布局管理器,但是呢效果基本就是个网格布局,只是每列之间是上下错开的,还是觉得叫交错式网格布局吧。先看个效果。 效果看起来应该很好实现的。这里仅仅是记录下自己实现的思路,然后贴出一些重要的代码。
  • friendlychen
  • friendlychen
  • 2017-08-03 16:43
  • 1985

【TIP_2】防止StaggeredGridLayoutManager快速滚动后触发重新排列

已知图片宽高的情况下防止快速滚动后 StaggeredGridLayoutManager 触发重新排列的一个淫荡伎俩…
  • zmhzhangminhua
  • zmhzhangminhua
  • 2015-12-28 20:27
  • 3137

RecyclerView和StaggeredGridLayoutManager实现瀑布流向下滑动item跳动、留白的问题

由于项目需要最近第一次采用RecyclerView和StaggeredGridLayoutManager实现了瀑布流,原本以为官方的东西不会有什么问题,但是好事多磨,实现了以后上拉加载更多,都没有问题,但是下拉回显上面的item的时候出现跳动,回到最顶部的时候还会有留白。针对这个问题,网上搜索解决办...
  • Vitktory
  • Vitktory
  • 2017-07-19 17:31
  • 955

使用StaggeredGridLayoutManager实现瀑布流效果

使用StaggeredGridLayoutManager来实现瀑布流效果
  • duanymin
  • duanymin
  • 2015-04-10 14:42
  • 23365

RecylerView 实现流布局StaggeredGridLayoutManager使用

流布局》》》》
  • Imshuyuan
  • Imshuyuan
  • 2017-06-06 14:23
  • 927

Recycleview实现复杂页面 三种以上布局 瀑布流 多布局 scrollview嵌套recyclerView 显示不全 滑动冲突 之进阶终极篇

Recycleview实现复杂页面 三种以上布局 瀑布流 多布局 scrollview嵌套recyclerView 显示不全 滑动冲突 之进阶终极篇
  • iamdingruihaha
  • iamdingruihaha
  • 2017-01-29 10:41
  • 10808

RecyclerView复杂布局动态管理GridLayoutManager

@Override public void onAttachedToRecyclerView(final RecyclerView recyclerView) { super.onAttachedToRecyclerView(recyclerView); RecyclerView.La...
  • qq_27121303
  • qq_27121303
  • 2017-11-09 17:24
  • 129

RecyclerView GridLayoutManager实现复杂的列数变化的布局

遇到复杂的不规则列(即有的行显示的列数多,有的行显示的列数少,并且每列显示的内容页不一样)对于这种需求在recyclerview出来之前的解决方案,要么就是listview中嵌套girdview,要么就直接使用scrollview,勉强可以实现此效果,但是这两种实现都有非常大的缺陷–都不能进行ite...
  • tzl0322
  • tzl0322
  • 2017-08-28 14:10
  • 1414
    个人资料
    • 访问:206309次
    • 积分:3455
    • 等级:
    • 排名:第11441名
    • 原创:153篇
    • 转载:53篇
    • 译文:0篇
    • 评论:23条
    最新评论