(三)UI绘制流程-流式布局

来源:互联网 发布:java api1.6文档下载 编辑:程序博客网 时间:2024/06/15 07:41

版权声明:本文为博主原创文章,未经博主允许不得转载。

本文纯个人学习笔记,由于水平有限,难免有所出错,有发现的可以交流一下。

一.前言

前两篇对源码进行了一些分析,内容较多,在这边再稍微讲一下。

1.View 与 ViewGroup

ViewGroup 虽然继承于 View,但却是一个抽象类,我们通常把容器按 View 和 ViewGroup 来进行区分。ViewGroup 类似于 View 的数组,里面存放多个 View,比如 LayoutParams、RelativeLayout 等。ViewGroup 会传递给 View 建议 的测量规格(包括模式和大小),View 会根据 ViewGroup 传递的测量规格,结合自身的设置进行实际大小的测量。

二、LayoutParams

ViewGroup 需要指定一个 LayoutParams 来确定里面的 View 支持哪些属性在。 LinearLayout 中添加 View 的时候,可以写 layout_gravity,layout_weight 属性;RelativeLayout 中的 View 有layout_centerInParent 属性,却没有 layout_gravity,layout_weight。这些 View 支持的属性可以通过重写 ViewGroup 的 generateLayoutParams()方法来进行设置。

二、分析

这里写图片描述

1.generateLayoutParams()没有特殊要求,我们直接用 MarginLayoutParams(getContext(),attrs)。
2.onMeasure() 测量自身宽高的同时把每一行存放的 View 和行高进行保存,(在 onLayout()中也要用到每一行的元素,避免再次循环判断)
3.onLayout()把每行 View 进行摆放。

三、实现

1.每一行的封装类

    //每一行的封装类    protected class LineView {        //每一行的子View        private List<View> mViewLine;        //行高        private int lineHeight;        public LineView() {            mViewLine = new ArrayList<View>();            lineHeight = 0;        }        public List<View> getmViewLine() {            return mViewLine;        }        public void setmViewLine(List<View> mViewLine) {            this.mViewLine = mViewLine;        }        public int getLineHeight() {            return lineHeight;        }        public void setLineHeight(int lineHeight) {            this.lineHeight = lineHeight;        }    }

二、generateLayoutParams()

    @Override    public LayoutParams generateLayoutParams(AttributeSet attrs) {        return new MarginLayoutParams(getContext(),attrs);    }

三、onMeasure()

@Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        //获取父容器为 FlowLayout 设置的测量模式和大小的建议值        int iWidthSpecMode = MeasureSpec.getMode(widthMeasureSpec);        int iHeightSpecMode = MeasureSpec.getMode(heightMeasureSpec);        int iWidthSpecSize = MeasureSpec.getSize(widthMeasureSpec);        int iHeightSpecSize = MeasureSpec.getSize(heightMeasureSpec);        //记录测量的宽高        int measuredWith = 0;        int measuredHeight = 0;        //记录当前行的宽高        int iCurLineW = 0;        int iCurLineH = 0;        int childCount = getChildCount();        //记录当前子 View 的宽高        int iChildWith = 0;        int iChildHeight = 0;        //存储每行 View        LineView lineView = new LineView();        //循环遍历子 View,进行子 View 的测量已经 FlowLayout 宽高的计算        for (int i=0; i<childCount; i ++) {            View childView = getChildAt(i);            //对当前获取的子 View 进行测量            measureChild(childView, widthMeasureSpec, heightMeasureSpec);            //获取子 View 的 MarginLayoutParams,并计算出子 View 所要占用的宽高            MarginLayoutParams childLayoutParams = (MarginLayoutParams) childView.getLayoutParams();            iChildWith = childView.getMeasuredWidth() + childLayoutParams.leftMargin                     + childLayoutParams.rightMargin;            iChildHeight = childView.getMeasuredHeight() +childLayoutParams.topMargin +                    childLayoutParams.bottomMargin;            //当前行放不下子 View, 换行            if (iChildWith + iCurLineW > iWidthSpecSize) {                //测量高度叠加,测量宽度取最大值                measuredHeight += iCurLineH;                measuredWith = Math.max(measuredWith, iCurLineW);                //记录当前行高度,并保存到 lineViewList                lineView.setLineHeight(iCurLineH);                lineViewList.add(lineView);                //重新对当前行宽高进行赋值                iCurLineW = iChildWith;                iCurLineH = iChildHeight;                //新建一行,添加子 View                lineView = new LineView();                lineView.getmViewLine().add(childView);            } else {                //当前行放的下子 View, 记录信息                //添加子 View                lineView.getmViewLine().add(childView);                //当前行宽度叠加,当前行高度取最大值                iCurLineW += iChildWith;                iCurLineH = Math.max(iChildHeight, iCurLineH);            }        }        //添加最后一行数据        //测量高度叠加,测量宽度取最大值        measuredHeight += iCurLineH;        measuredWith = Math.max(measuredWith, iCurLineW);        //记录当前行高度        lineView.setLineHeight(iChildHeight);        lineViewList.add(lineView);        //测量的最终目的 setMeasuredDimension        setMeasuredDimension(iWidthSpecMode == MeasureSpec.EXACTLY ? iWidthSpecSize : measuredWith,                 iHeightSpecMode == MeasureSpec.EXACTLY ? iHeightSpecSize : measuredHeight);    }

四、onLayout()

    @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        //记录当前子 View 的四个边沿        int left,top,right,bottom;        //记录当前子 View 包括 Margin 的左边沿和上边沿        int curTop = 0;        int curLeft = 0;        //每一行数据        LineView lineView;        //遍历每一行数据        for (int i=0; i<lineViewList.size(); i++) {            //获取当前行的数据            lineView = lineViewList.get(i);            //遍历当前行所有 View            for (int j=0; j<lineView.getmViewLine().size(); j++) {                //获取当前子 View                View childView = lineView.getmViewLine().get(j);                //获取子 View 的 MarginLayoutParams,并计算出子 View 的四个边沿                MarginLayoutParams layoutParams = (MarginLayoutParams) childView.getLayoutParams();                left = curLeft + layoutParams.leftMargin;                top = curTop + layoutParams.topMargin;                right = left + childView.getMeasuredWidth();                bottom = top + childView.getMeasuredHeight();                //布局子 View                childView.layout(left, top, right, bottom);                //计算下一个子 View 的左边沿                curLeft = right + layoutParams.rightMargin;            }            //换行,重置子 View 包括 Margin 的左边沿和上边沿            curLeft = 0;            curTop += lineView.getLineHeight();        }        //由于程序会多次调用测量布局,所以布局完清空数据        //不清空数据,同一个 View 会被多次引用布局,以最后一次布局为主,        //可能导致所有子 View 的布局都在 FlowLayout 高度之外,导致界面没有东西        lineViewList.clear();    }

五、监听

    //监听类接口    public interface OnItemClickListener{        void onItemClick (View v, int index);    }    //监听类的实现    public void setOnItemClickListener(final OnItemClickListener listener){        int childCount = getChildCount();        for(int i = 0 ; i < childCount ; i++){            View childView = getChildAt(i);            final int finalI = i;            childView.setOnClickListener(new OnClickListener() {                @Override                public void onClick(View v) {                    listener.onItemClick(v, finalI);                }            });        }    }

六、附

代码链接:http://download.csdn.net/detail/qq_18983205/9848440
这里引用 v7 包,报错的可以参考 http://blog.csdn.net/qq_18983205/article/details/60874618

原创粉丝点击