Android基础相关

Android自定义控件-流式布局

2021-01-09  本文已影响0人  来lol里

自定义view主要分为组合式和继承View或者ViewGroup重写两种方式,流式布局是第二种继承ViewGroup的方式实现的。
一般这种自定义View主要是通过重写onMeasure和onLayout方法实现的,在onMeasure测量的时候首先要是区分父布局MeasureSpec.EXACTLY或者是MeasureSpec.AT_MOST,然后通过MeasureSpec.getSize的方法测量自己宽高,通过measureChild()测量子view的宽高。最后在onLayout中根据具体位置绘制view的

image.png

一些用的变量

   private List<View> curLineViews;//存在每行的view
    private List<List<View>> totalLineViews;//存储所有行的view
    private int curWidth//当前一行的宽度
            ,curHeigh//当前一行的高度
            ,mostWidth//记录当前的最宽值,如果父类不是EXACTLY模式,则宽度以我们最大宽度为准
            ,mostHigth//同上
            ;

下面我们看一下onMesure方法里,注释写的很清楚了,核心的方法就是在for循环里遍历子view,计算每个view相加的宽度是否超过限定的宽度,然后把子view存起来在onLayout方法里计算具体布局的位置,要注意的是单行view不满足换行的时候需要单独处理一下。

   @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthMode=MeasureSpec.getMode(widthMeasureSpec);
        int heighMode=MeasureSpec.getMode(heightMeasureSpec);
        int widthSize=MeasureSpec.getSize(widthMeasureSpec);
        int heighSize=MeasureSpec.getSize(heightMeasureSpec);

        curLineViews=new ArrayList<>();
        totalLineViews=new ArrayList<>();
        everyLinesHeigh=new ArrayList<>();

        //需要通过遍历子view 计算出一共需要的宽和高
        for (int i = 0; i <getChildCount() ; i++) {
            measureChild(getChildAt(i),widthMeasureSpec,heightMeasureSpec);
            //子控件的宽高
            int childWidth = getChildAt(i).getMeasuredWidth();
            int childHeight = getChildAt(i).getMeasuredHeight();
            //如果当前一行的宽度超过了最大宽度,则需要进行换行
            if(curWidth+childWidth>widthSize){

                totalLineViews.add(curLineViews);//存储当前这一行的数据
                curLineViews=new ArrayList<>();//清空备用行集合

                mostWidth=Math.max(mostWidth,curWidth);//获取当前行的宽度,如果比之前的宽 则取出备用
                mostHigth+=curHeigh;
                curWidth=0;
                everyLinesHeigh.add(curHeigh);
                curHeigh=0;
            }
            curWidth+=childWidth;
            curLineViews.add(getChildAt(i));//存储当前这行中的view
            curHeigh=Math.max(curHeigh,childHeight);//获取当前最高的一个元素作为当前的高度值


            if(i==getChildCount()-1){//所有子view都加上只有一行的情况

                totalLineViews.add(curLineViews);//存储当前这一行的数据
                curLineViews=new ArrayList<>();//清空备用行集合

                mostWidth=Math.max(mostWidth,curWidth);//获取当前行的宽度,如果比之前的宽 则取出备用
                mostHigth+=curHeigh;
                everyLinesHeigh.add(curHeigh);
            }
        }

        setMeasuredDimension(widthMode==MeasureSpec.EXACTLY?widthSize:mostWidth,
                heighMode==MeasureSpec.EXACTLY?heighSize:mostHigth);
    }

再来看一下onLayout里的方法,主要是计算每个子view放置的具体位置,核心方法在两个for循环,遍历每行的views,然后在每行的views里继续遍历每个view,具体完成每个childView的layout布局。

  protected void onLayout(boolean b, int i, int i1, int i2, int i3) {
        int curX= getPaddingLeft();
        int curY= getPaddingTop();
        for (int j = 0; j <totalLineViews.size() ; j++) {

            for (int k = 0; k <totalLineViews.get(j).size() ; k++) {// 遍历每一行的数据,分别赋值位置
                View child =totalLineViews.get(j).get(k);

                child.layout(
                          curX,
                          curY,
                        curX+child.getMeasuredWidth(),
                        curY+child.getMeasuredHeight()

                );
                curX+=child.getMeasuredWidth(); //下一个位置x累加

            }
            curY+=everyLinesHeigh.get(j);//换行Y累加
            curX=0; //换行清空x

        }
    }

以上只是简单的实现了效果,具体如margin等间距,可以在此基础上自己加上。我们看下效果


image.png
上一篇下一篇

猜你喜欢

热点阅读