美文网首页Android基础相关
Android自定义控件-流式布局

Android自定义控件-流式布局

作者: 来lol里 | 来源:发表于2021-01-09 18:55 被阅读0次

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

  • EXACTLY(明确)
    父view决定子view的确切大小,子被限定在给定的边界里,忽略本身想要的大小。当设置width或height为100dp时候,子view最多只能100dp, 当设置width或height为match_parent时,模式为EXACTLY,因为子view会占据剩余容器的空间,所以它大小是确定的。
  • AT_MOST(至少)
    子view最大可以达到的指定大小 ,当设置为wrap_content时,模式为AT_MOST, 表示子view的大小最多是多少,这样子view会根据这个上限来设置自己的尺寸。
  • UNSPECIFIED(未指定)
    父view不限制子view大小,如系统的scrollview,自定义使用较少。
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

相关文章

网友评论

    本文标题:Android自定义控件-流式布局

    本文链接:https://www.haomeiwen.com/subject/hwscaktx.html