自定义流布局FloatLayout(一)

来源:互联网 发布:linux rws是什么意思 编辑:程序博客网 时间:2024/05/16 12:05

有没有遇到过这种效果,那么这种排布是怎么实现的呢

http://img.blog.csdn.net/20160203163306090?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

首先自定义一个类继承ViewGroup ,因为android提供的ViewGroup子类都不能实现这种效果

然后要有一个思路:就是ViewGroup的measure()方法的执行过程是怎么样的--如果有子view的话就先测量子view--然后再测量自己的宽高

1.在onMeasure()方法中获取到所有的子View ,然后对子view进行测量, 然后累加宽度,判断当宽度大于ViewGroup的款丢的时候,进行换行

2.把每行的子view都添加到一个集合line中, 最后再把所有的line都添加到集合lines中

3.在onLayout()方法中遍历所有的集合,对每一行的子view进行layout()操作,然后再换行的时候再把参数左 上 右 下重新累加赋值,继续执行layout()操作

具体的代码如下:

package zz.itcast.googleplay09.view;import java.util.ArrayList;import java.util.List;import zz.itcast.googleplay09.utils.UIUtils;import android.content.Context;import android.util.AttributeSet;import android.view.View;import android.view.ViewGroup;/** * 排行界面的流布局(特点,按行显示,如果当前行已经显示满了,才会开辟新行) * @author wangdh * */public class FlowLayout extends ViewGroup {        //标签的间隔:水平、垂直    private int hSpace = UIUtils.dip2px(13);    private int vSpace = UIUtils.dip2px(13);        public FlowLayout(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);    }    public FlowLayout(Context context, AttributeSet attrs) {        super(context, attrs);    }    public FlowLayout(Context context) {        super(context);    }    /**     * 分配子view位置(标签)     * 主要由左上角点,右下角点,确定控件位置     */    @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        //1. 分配每一行的位置        for (int i = 0; i < lines.size(); i++) {            Line currentLine = lines.get(i);            //分配每一个内容位置,都是只确定左上角点即可            currentLine.layout(l+getPaddingLeft(),t+getPaddingTop());            //非第一行,都需要添加行高,间距            t += currentLine.getLineHeight();            t += vSpace;        }    }    /**     * 确定FlowLayout的测量标准     * 父亲有义务测量孩子的大小(宽度、高度)     */    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        //1.测量孩子的大小(宽高的测量标准(宽度高度的mode、size))        //(1).孩子的测量标准,与父亲的测量标准有一定的对应关系。获取父亲的测量标准        //获取父亲 宽高的mode和size        int widthMode = MeasureSpec.getMode(widthMeasureSpec);        int widthSize = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight();        int heightMode = MeasureSpec.getMode(heightMeasureSpec);        int heightSize = MeasureSpec.getSize(heightMeasureSpec) - getPaddingTop() - getPaddingBottom();        //(2)获取孩子的宽度高度的mode        int childWidthMode = widthMode == MeasureSpec.EXACTLY ? MeasureSpec.AT_MOST :widthMode;        int childHeightMode = heightMode == MeasureSpec.EXACTLY ? MeasureSpec.AT_MOST :heightMode;        //(3)获取孩子的宽度高度的size(孩子的模式:At_most(size=1000)、未指定(未指定一般size=0,但是这里大小其实给多少都可以。))//        int childWidthSize = 1000;//        int childHeightSize = 1000;         int childWidthSize = widthSize;        int childHeightSize = heightSize;         //(4)测量孩子        for (int i = 0; i < getChildCount(); i++) {            System.out.println("当前孩子:"+i);            View child = getChildAt(i);            child.measure(MeasureSpec.makeMeasureSpec(childWidthSize, childWidthMode),                     MeasureSpec.makeMeasureSpec(childHeightSize, childHeightMode));            //获取孩子的宽度            int childMeasuredWidth = child.getMeasuredWidth();//            2. 将孩子放入行内            currentLineUseWidth +=childMeasuredWidth;            //如果当前已用宽度小于父亲宽度            if(currentLineUseWidth<widthSize){                //将孩子放入                currentLine.addChild(child);                System.out.println("1放入孩子");                //添加水平间隔                currentLineUseWidth+=hSpace;                if(currentLineUseWidth>=widthSize){                    //换行                    newLine();                    System.out.println("1换行");                }            }else{                //如果当前行内没有孩子,强制加入                if(currentLine.getChildCount()==0){                    currentLine.addChild(child);                    System.out.println("2放入孩子");                }                //换行                newLine();                //(避免换行时,漏掉孩子)                i -- ;                System.out.println("2换行");            }        }                //如果最后一行没有在集合内,添加        if(!lines.contains(currentLine)){            lines.add(currentLine);        }        //3.测量本身FlowLayout        int totalHeight = 0;        //添加行高        for (int i = 0; i < lines.size(); i++) {            totalHeight+=lines.get(i).getLineHeight();        }        //间距比行数少一个        totalHeight+=vSpace*(lines.size()-1);        setMeasuredDimension(widthSize+getPaddingLeft()+getPaddingRight(),                totalHeight+getPaddingTop()+getPaddingBottom());//        super.onMeasure(widthMeasureSpec, heightMeasureSpec);    }    /**     * 换行     */    private void newLine() {        //将当前行,放入集合,保存        lines.add(currentLine);        currentLine = new Line();        currentLineUseWidth = 0;    }    //当前行    private Line currentLine = new Line();        //行集合    private List<Line> lines= new ArrayList<FlowLayout.Line>();        //当前行已用宽度    private int currentLineUseWidth = 0;            //行:用于存放标签Textview    public class Line{                private List<View> childs = new ArrayList<View>();                //1.获取当前行剩余空间        //先获取已经使用的空间(子view的宽度+间隔(子view个数-1))        private int lineUseWidth = 0;        /**         * 添加孩子         * @param child         */        public void addChild(View child) {            childs.add(child);            //当前行高应该是最高孩子的高度            if(currentLineHeight<child.getMeasuredHeight()){                //当前行高=孩子的高度                currentLineHeight = child.getMeasuredHeight();            }            //(1)添加子view的宽度            lineUseWidth +=child.getMeasuredWidth();        }        public void layout(int l, int t) {            //(2)添加间隔            lineUseWidth += (childs.size()-1)*hSpace;//            System.out.println("lineUseWidth:"+lineUseWidth);            //(3)获取剩余空间            int leftUseWidth = getMeasuredWidth() -getPaddingLeft() - getPaddingRight() - lineUseWidth;            int avgLeftUseWidth = leftUseWidth/childs.size();//            System.out.println("childs.size():"+childs.size());//            System.out.println("avgLeftUseWidth:"+avgLeftUseWidth);                                    //通过行,分配每一个标签的位置            for (int i = 0; i < childs.size(); i++) {                View currentView = childs.get(i);                //确定具体位置                currentView.layout(l, t, l+currentView.getMeasuredWidth()+avgLeftUseWidth, t+currentView.getMeasuredHeight());                //currentView.layout(l, t, getMeasuredWidth(), getMeasuredHeight());                //非第一行的left不一样                l += currentView.getMeasuredWidth()+avgLeftUseWidth;//子view的宽度(+剩余空间的宽度)                l += hSpace;            }                    }        private int currentLineHeight = 0;        /**         * 当前行高度         * @return         */        public int getLineHeight() {                        return currentLineHeight;        }        /**         * 当前行有几个孩子         * @return         */        public int getChildCount() {            return childs.size();        }            }    }

那么这个空间就算是完成了,可以直接new出来,然后在代码中动态的添加TextView,效果还是很炫的


注意在每个子view的layout()方法的时候,修改一行代码,那么效果就会变得更加绚丽

http://img.blog.csdn.net/20160203164754334?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

  //通过行,分配每一个标签的位置            for (int i = 0; i < childs.size(); i++) {                View currentView = childs.get(i);                //确定具体位置            //currentView.layout(l, t, l+currentView.getMeasuredWidth()+avgLeftUseWidth, t+currentView.getMeasuredHeight());把这一行注释掉,打开下面一行                currentView.layout(l, t, getMeasuredWidth(), getMeasuredHeight());                //非第一行的left不一样                l += currentView.getMeasuredWidth()+avgLeftUseWidth;//子view的宽度(+剩余空间的宽度)                l += hSpace;            }

具体的原理跟上面的类似,至于textView的背景颜色,就可以在代码中动态的设置随机的十六进制颜色值,至于具体操作放到下一次博客里面了

0 0
原创粉丝点击