自定义viewGroup打造花式布局

维护维护在维护,改版改版复改版!
最近项目在改版,UI给了如下的一张新界面:


首先拿到界面时,美工说为了防止界面显得太长,当图片List小于4个的时候则就竖直排列,如果大于或等于5个的时候的第一到第五的子view会左右排列。思考了一会感觉也不是非常的复杂,于是就欣然动笔画起来伪代码:


这样大概画好之后,就有了一点的思绪,首先我们先无脑的自定义一个viewgroup再说:
<pre name="code" class="java">public class HomeMealLayout extends ViewGroup {


    private int childWidth;
    private int childHeight;
    private int childCount;


    private Context context;


    public HomeMealLayout(Context context) {
        this(context,null);
    }


    public HomeMealLayout(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }


    public HomeMealLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.context=context;
    }


然后我们要开始对子view的高度和viewgroup进行测量:
首先我们布局的图片大小和比例都是固定的,所以我们只需要宽度铺满,高度自适应的Imageview即可,然后我们的第一到第五的子View的高度和宽度是正常布局的1/2,所以我们首先确定正常布局下的childWidth,然后除以2则是1-5的子view的宽高。接下来我们看代码:
 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (!isInEditMode())
            mHelper.adjustChildren();
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        childCount = getChildCount();
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);


        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);


        if(childCount ==0){
            return;
        }else if(childCount <5){
            for (int i=0;i<childCount;i++){
                View child = getChildAt(i);
                measureChild(child,widthMeasureSpec,heightMeasureSpec);
                 childWidth =  child.getMeasuredWidth();
                 childHeight = child.getMeasuredHeight();
            }
        }else if(childCount >=5){
            for (int i=0;i<childCount;i++){
                View child = getChildAt(i);
                if(i>=1 && i<=4){
                    measureChild(child,widthMeasureSpec,heightMeasureSpec);
                    childWidth =  child.getMeasuredWidth();
                    childHeight = child.getMeasuredHeight();
                    MealLayoutParams lp =(MealLayoutParams) child.getLayoutParams();
                 
                    lp.width=childWidth/2;
                    lp.height=childHeight/2;
                    child.setLayoutParams(lp);
                }else {
                    measureChild(child,widthMeasureSpec,heightMeasureSpec);
                    childWidth =  child.getMeasuredWidth();
                    childHeight = child.getMeasuredHeight();
                }
            }
         
        }
        if(childCount<5){
            setMeasuredDimension(resolveSize(childWidth,widthMeasureSpec),resolveSize(childHeight*childCount,heightMeasureSpec));
        }else {
            setMeasuredDimension(resolveSize(childWidth,widthMeasureSpec),resolveSize(childHeight*(childCount-3),heightMeasureSpec));
        }




    }


 整体的逻辑还算比较清晰,MealLayoutParams 是继承的marginlayoutparams,这里大家只需要写marginlayoutparams即可,因为我们使用了marginlayoutparams 所以别忘了在viewgroup中加上这行代码: 

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


这样我们子view和父控件的宽高都已经测量好了,接下来就到最重要的部分了,我们要开始在onLayout中摆放我们的子view了,其实onlayout的逻辑跟onmeasure的逻辑相似,只是onlayout要开始具体的进行摆放子View,我们直接上代码:
  
@Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if(childCount<5){
            for(int i=0;i<childCount;i++){
                getChildAt(i).layout(0,space*(i-1)+childHeight*i,childWidth,space*i+childHeight*(i+1));
            }
        }else if(childCount>=5){


            for (int i=0;i<childCount;i++){
               if(i==0){
                   getChildAt(0).layout(0,0,childWidth,childHeight);
               }else if(i>=1 && i<5){
                   getChildAt(1).layout(0,space+childHeight,childWidth/2,childHeight/2+space+childHeight);
                   getChildAt(2).layout(childWidth/2+space,space+childHeight,childWidth,childHeight+space+childHeight/2);
                   getChildAt(3).layout(0,space*2+childHeight+childHeight/2,childWidth/2,childHeight+space*2+childHeight);
                   getChildAt(4).layout(childWidth/2+space,space*2+childHeight+childHeight/2,childWidth,childHeight+space*2+childHeight);
               }else {
                   getChildAt(i).layout(0,space*(i-2)+childHeight*(i-3),childWidth,space*(i-1)+(i-2)*childHeight);
               }
            }
        }
    }


这里的代码应该是比较清晰的,当childCount小于5的时候,我们就依次的向下排列,当childCount>=5的时候我们就要做判断,因为中间的子View只有4个,所以我也并没有做特殊的判断,直接全部写了上去,也比较清晰,至此我们的viewgroup已经写的差不多了,可以通过for循环来进行addview了,但是这明显不符合我们的习惯,所以我们要对他进行再一次的封装,但是因为别人的代码写得更好,而且逻辑梳理也比我强,所以发一波传送门,未来的大神:http://blog.csdn.net/zxt0601/article/details/52494665 里面讲解了如何对自定义的viewgroup进行封装,大家可以前去膜拜!
至此,这篇博客就算写完了。写的不好的地方,希望大家多多指正!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值