在很多商城我们会看到如下效果
//有些小伙伴可能对view和viewgroup有点傻傻分不清,简单说下,ViewGroup是用来装载view的容器,他也是继承view;view是可视化控件;比如我们系统的容器控件 LinearLayout就是继承viewGroup;TextView就是继承TextView;
-------------------------------------------------------------------------------分析实现逻辑-------------------------------------------------------
仔细观察 这个流式布局,就是对子控件如何摆放的问题,那每个view的摆放,需要知道他放的位置即view对应left top right bottom 四个参数就可以知道他摆放位置了。那我们如何知道呢,
通过上图我们可以把界面可以分成一行一行的,我们只需要知道每行的高,和每行view对象,就可以了,这些数据怎么获取呢?
我们可以重新onMeasure方法使用List<Integer>集合保存每行高,使用List<List<View>> listLineView 双层集合记录view
------------------------------------------------------------------------onMeasure代码------------------------------------------------------------------
private boolean isFlag; @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (isFlag) { return; } isFlag = true; //获取自己的宽高 int widthSize = MeasureSpec.getSize(widthMeasureSpec); //保存测量信息 每行的view ,每行的高度 saveMeasureInfo(widthMeasureSpec, heightMeasureSpec, widthSize); } private void saveMeasureInfo(int widthMeasureSpec, int heightMeasureSpec, int widthSize) { //当前view宽 高 int iChildWidth = 0; int iChildHeight = 0; //当前行宽 行高 因为存在多行 int iCurLineW = 0; int iCurLineH = 0; //获取子view数量用于 for循环 int childCount = getChildCount(); Log.e("zdh","-----------"+childCount); //单行信息容器 List<View> viewList = new ArrayList<>(); //for循环所有子view for (int i = 0; i < childCount; i++) { //获取子view View childAt = getChildAt(i); //测量自己 measureChild(childAt, widthMeasureSpec, heightMeasureSpec); //获取布局参数--》xml资源:MarginLayoutParams 这个是xml中 margin参数信息 MarginLayoutParams layoutParams = (MarginLayoutParams) childAt.getLayoutParams(); //获取实际宽度和高度(比如view使用了margin属性--》实际宽=宽+margin左距离+margin右距离) iChildWidth = childAt.getMeasuredWidth() + layoutParams.leftMargin + layoutParams.rightMargin; iChildHeight = childAt.getMeasuredHeight() + layoutParams.topMargin + layoutParams.bottomMargin; //是否需要换行 iCurLineW是当前行遍历view的宽 的总和 加上现在遍历的view的宽 比父容器的宽大就需要换行了 if (iCurLineW + iChildWidth > widthSize) { // //记录当前行最大宽度,高度类加 // measureWidth=Math.max(measureWidth,iChildWidth); // measureHeight+=iChildHeight; //首先是不换行的,所以执行换行 //保存这行数据 和宽高 listLineHeight.add(iCurLineH); listLineView.add(viewList); //记录新的行信息 iCurLineW = iChildWidth; iCurLineH = iChildHeight; //添加新行记录 viewList = new ArrayList<>(); viewList.add(childAt); } else { //不换行情况 代码首先会执行这里面 iCurLineW += iChildWidth; iCurLineH = Math.max(iCurLineH, iChildHeight); //添加到viewList里面 viewList.add(childAt); } //最后一行需要换行 if (i == childCount - 1) { listLineView.add(viewList); listLineHeight.add(iCurLineH); } } }
------------------------------------------------------------onLayout代码----------------------------------------------------------------------
@Override protected void onLayout(boolean changed, int l, int t, int r, int b) { //开始布局摆放 int left, top, right, bottom; //记录当前顶部高 和左边高度 int curTop = 0; int curLeft = 0; //开始迭代 for (int i = 0; i < listLineView.size(); i++) { List<View> viewList = listLineView.get(i); //遍历 高集合 for (int i1 = 0; i1 < viewList.size(); i1++) { View childView = viewList.get(i1); //获取margin 参数信息 MarginLayoutParams layoutParams = (MarginLayoutParams) childView.getLayoutParams(); left = curLeft + layoutParams.leftMargin; top = curTop + layoutParams.topMargin; right = left + childView.getMeasuredWidth(); bottom = top + childView.getMeasuredHeight(); //调用自身的layout进行布局 childView.layout(left, top, right, bottom); //左边部分累加 curLeft += childView.getMeasuredWidth() + layoutParams.leftMargin + layoutParams.rightMargin; } //换行置空左边标记 curLeft = 0; //累加顶部 curTop += listLineHeight.get(i); } //清空集合 listLineView.clear(); listLineHeight.clear(); }
------------------------------------------------------------注意获取view参数信息 需要重新下面方法----------------------------------
/** * 注意我们使用自定义view的margin参数数据 需要重新 generateLayoutParams方法获取布局参数 */ @Override public LayoutParams generateLayoutParams(AttributeSet attrs) { return new MarginLayoutParams(getContext(), attrs); }