美文网首页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