viewGroup实现垂直和水平排列

首先需要了解 onLayout(),onMeasure(),方法

总而言之,对于viewGroup来说,onMeasure就是测绘本身的大小,如何测绘呢?先测绘所有子view的大小,子view也会递归的进行这个过程,然后onLayout就是放置子view的位置。附上源码。

package com.example.testviewgroup;

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.Nullable;

public class mViewGroup extends ViewGroup {
    private boolean HorV;

    public mViewGroup(Context context) {
        this(context, null);


    }

    public mViewGroup(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);

    }

    public mViewGroup(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {

        super(context, attrs, defStyleAttr);
       TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.mViewGroup);
        HorV= a.getBoolean(R.styleable.mViewGroup_viewGroup_HorV,true);//默认为水平
        a.recycle();
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        
        if(HorV) {
            int count = getChildCount();
            for (int i = 0; i < count; i++) {

                View v = getChildAt(i);

                if (v.getVisibility() != View.GONE) {

                    int childWidth = v.getMeasuredWidth();
                    int childHeight = v.getMeasuredHeight();

                    //开始摆放
                    v.layout(l, t, l + childWidth, t + childHeight);

                    //把左边的锚定位置往右移
                    //如果在垂直方向继续累加l偏移量,那么显示出来的三个子view呈现阶梯状。
                    l += childWidth;
                    //垂直方向累计坐标量
                    //  t += childHeight;
                }
            }
        }
        else{
            int count = getChildCount();
            for (int i = 0; i < count; i++) {
                CustomView v = (CustomView) getChildAt(i);
                if (v.getVisibility() != View.GONE) {
                    int childWidth = v.getMeasuredWidth();
                    int childHeight = v.getMeasuredHeight();
                    //开始摆放
                    v.layout(l, t, l + childWidth, t + childHeight);
                    //把左边的锚定位置往右移
                    //如果在垂直方向继续累加l偏移量,那么显示出来的三个子view呈现阶梯状。
                    //l += childWidth;
                    //垂直方向累计坐标量
                    t += childHeight;

                }
            }
        }// else end 也就是垂直的逻辑部分
    }

    @Override
    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
      if(HorV) {
          int measuredWidth = 0;
          int measuredHeight = 0;

          int count = getChildCount();
          for (int i = 0; i < count; i++) {
              View v = getChildAt(i);
              if (v.getVisibility() != View.GONE) {
                  measureChild(v, widthMeasureSpec, heightMeasureSpec);
                  measuredHeight = Math.max(measuredHeight, v.getMeasuredHeight());
                  measuredWidth += v.getMeasuredWidth();
              }
          }
          measuredWidth += getPaddingLeft() + getPaddingRight();
          measuredHeight += getPaddingTop() + getPaddingBottom();

          //可选
          //measuredWidth = Math.max(measuredWidth, getSuggestedMinimumWidth());
          //measuredHeight = Math.max(measuredHeight, getSuggestedMinimumHeight());

          //另外一种set度量值的方法
          //setMeasuredDimension(resolveSize(measuredWidth, widthMeasureSpec),resolveSize(measuredHeight, heightMeasureSpec));
          setMeasuredDimension(measuredWidth, measuredHeight);

      }
      else{
          int measuredWidth = 0;
          int measuredHeight = 0;
          int count = getChildCount();
          for (int i = 0; i < count; i++) {
              View v = getChildAt(i);
              if (v.getVisibility() != View.GONE) {
                  measureChild(v, widthMeasureSpec, heightMeasureSpec);
                  measuredWidth = Math.max(measuredWidth, v.getMeasuredWidth());
                  measuredHeight += v.getMeasuredHeight();
              }
          }
          measuredWidth += getPaddingLeft() + getPaddingRight();
          measuredHeight += getPaddingTop() + getPaddingBottom();

          //可选
          //measuredWidth = Math.max(measuredWidth, getSuggestedMinimumWidth());
          //measuredHeight = Math.max(measuredHeight, getSuggestedMinimumHeight());

          //另外一种set度量值的方法
          //setMeasuredDimension(resolveSize(measuredWidth, widthMeasureSpec),resolveSize(measuredHeight, heightMeasureSpec));

          setMeasuredDimension(measuredWidth, measuredHeight);

      }//else end 也就是垂直的逻辑部分`在这里插入代码片`
    }
}

这里又增加了一个新的需求,就是我们假设我们自定义的CustView控件增加了优先级属性,如何展示实现根据优先级显示?比如 最右边的控件优先级最高,它增大的话,左边的控件就会被挤走。最中间的优先级最高,它如果很大,导致剩下的空间不够,应该把两边的挤走?


先说思路,思路如下:给子view组件注册一个自定义优先级属性和一个是否可见的属性(默认可见)。然后提供对应的set,get方法。

在viewGroup里的写一个优先级判断方法。先取出可显示区域的大小(这里假设是水平,取出显示的width),然后从优先级搞到低逐个判断是否可以显示,若可以显示,可显示区域减去该控件所占的区域,并将控件可见属性CanLook设置为true,否则,则设置为false。然后在onLayuot阶段是在判断控件是否可见。就可以完成了。

完整的控件代码

package com.example.testviewgroup;

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.Nullable;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;

public class MyViewGroup extends ViewGroup {
    private boolean HorV;//水平还是垂直 true为水平

    public MyViewGroup(Context context) {
        this(context, null);


    }

    public MyViewGroup(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);

    }

    public MyViewGroup(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {

        super(context, attrs, defStyleAttr);
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.mViewGroup);
        HorV = a.getBoolean(R.styleable.mViewGroup_viewGroup_HorV, true);//默认为水平
        a.recycle();
    }
    //新增需求,要对view实现优先级设置


    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        //初始化,对优先级做一个处理。
        initPrior();
        if (HorV) {
            int count = getChildCount();
            for (int i = 0; i < count; i++) {

                //View v = getChildAt(i);
                CustomView v = (CustomView) getChildAt(i);
                if (v.getVisibility() != View.GONE && v.getCanseen() && v.getCanseen()) {

                    int childWidth = v.getMeasuredWidth();
                    int childHeight = v.getMeasuredHeight();

                    //开始摆放
                    v.layout(l, t, l + childWidth, t + childHeight);

                    //把左边的锚定位置往右移
                    //如果在垂直方向继续累加l偏移量,那么显示出来的三个子view呈现阶梯状。
                    l += childWidth;
                    //垂直方向累计坐标量
                    //  t += childHeight;
                }
            }
        } else {
            int count = getChildCount();
            for (int i = 0; i < count; i++) {
                CustomView v = (CustomView) getChildAt(i);
                if (v.getVisibility() != View.GONE && v.getCanseen()) {
                    int childWidth = v.getMeasuredWidth();
                    int childHeight = v.getMeasuredHeight();
                    //开始摆放
                    v.layout(l, t, l + childWidth, t + childHeight);
                    //把左边的锚定位置往右移
                    //如果在垂直方向继续累加l偏移量,那么显示出来的三个子view呈现阶梯状。
                    //l += childWidth;
                    //垂直方向累计坐标量
                    t += childHeight;

                }
            }
        }// else end 也就是垂直的逻辑部分
    }

    @Override
    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        if (HorV) {
            int measuredWidth = 0;
            int measuredHeight = 0;

            int count = getChildCount();
            for (int i = 0; i < count; i++) {
                View v = getChildAt(i);
                if (v.getVisibility() != View.GONE) {
                    measureChild(v, widthMeasureSpec, heightMeasureSpec);
                    measuredHeight = Math.max(measuredHeight, v.getMeasuredHeight());
                    measuredWidth += v.getMeasuredWidth();
                }
            }
            measuredWidth += getPaddingLeft() + getPaddingRight();
            measuredHeight += getPaddingTop() + getPaddingBottom();

            //可选
            //measuredWidth = Math.max(measuredWidth, getSuggestedMinimumWidth());
            //measuredHeight = Math.max(measuredHeight, getSuggestedMinimumHeight());

            //为了使用优先级在屏幕里,我们需要将viewGroup的大小设置为屏幕和viewGroup二者的最小值。
            DisplayMetrics mDisplayMetrics = getResources().getDisplayMetrics();
            int width = mDisplayMetrics.widthPixels;
            int height = mDisplayMetrics.heightPixels;

            //另外一种set度量值的方法
            //setMeasuredDimension(resolveSize(measuredWidth, widthMeasureSpec),resolveSize(measuredHeight, heightMeasureSpec));
            setMeasuredDimension(Math.min(measuredWidth, width), Math.min(measuredHeight, height));

        } else {
            int measuredWidth = 0;
            int measuredHeight = 0;
            int count = getChildCount();
            for (int i = 0; i < count; i++) {
                View v = getChildAt(i);
                if (v.getVisibility() != View.GONE) {
                    measureChild(v, widthMeasureSpec, heightMeasureSpec);
                    measuredWidth = Math.max(measuredWidth, v.getMeasuredWidth());
                    measuredHeight += v.getMeasuredHeight();
                }
            }
            measuredWidth += getPaddingLeft() + getPaddingRight();
            measuredHeight += getPaddingTop() + getPaddingBottom();

            //可选
            //measuredWidth = Math.max(measuredWidth, getSuggestedMinimumWidth());
            //measuredHeight = Math.max(measuredHeight, getSuggestedMinimumHeight());

            //另外一种set度量值的方法
            //setMeasuredDimension(resolveSize(measuredWidth, widthMeasureSpec),resolveSize(measuredHeight, heightMeasureSpec));

            //新增需求,获取activty的大小,若viewgroup大小超过他们,则截断超出的部分。
            DisplayMetrics mDisplayMetrics = getResources().getDisplayMetrics();
            int width = mDisplayMetrics.widthPixels;
            int height = mDisplayMetrics.heightPixels;

            setMeasuredDimension(Math.min(measuredWidth, width), Math.min(measuredHeight, height));


        }//else end 也就是垂直的逻辑部分
    }


    //做优先级的判断的代码
    private void initPrior() {
        if (HorV == true) {
            int width = getWidth();
            List<CustomView> list = new ArrayList<>();
            for (int i = 0; i < getChildCount(); i++) {
                CustomView v = (CustomView) getChildAt(i);
                list.add(v);

            }//以 key:prior优先级 - value:i下标  的形式存储一下。

            Collections.sort(list, new Comparator<CustomView>() {

                @Override
                public int compare(CustomView v1, CustomView v2) {
                    // 按照学生的年龄进行升序排列
                    if (v2.getPrior() > v1.getPrior()) {
                        return 1;
                    }
                    if (v1.getPrior() == v2.getPrior()) {
                        return 0;
                    }
                    return -1;
                }
            });
            Iterator iter = list.iterator();
            while (iter.hasNext()) {
                CustomView v = (CustomView) iter.next();
                if (width - v.getMeasuredWidth() >= 0) {

                    width -= v.getMeasuredWidth();
                    v.setCanSeen(true);
                }//可以放
                else {
                    v.setCanSeen(false);

                }

            }
        }//HorV == true 水平排放的逻辑部分
        else {
            int height = getHeight();
            List<CustomView> list = new ArrayList<>();
            for (int i = 0; i < getChildCount(); i++) {
                CustomView v = (CustomView) getChildAt(i);
                list.add(v);

            }//以 key:prior优先级 - value:i下标  的形式存储一下。

            Collections.sort(list, new Comparator<CustomView>() {

                @Override
                public int compare(CustomView v1, CustomView v2) {
                    // 按照学生的年龄进行升序排列
                    if (v2.getPrior() > v1.getPrior()) {
                        return 1;
                    }
                    if (v1.getPrior() == v2.getPrior()) {
                        return 0;
                    }
                    return -1;
                }
            });
            Iterator iter = list.iterator();
            while (iter.hasNext()) {
                CustomView v = (CustomView) iter.next();
                if (height - v.getMeasuredHeight() >= 0) {

                    height -= v.getMeasuredHeight();
                    v.setCanSeen(true);
                }//可以放
                else {
                    v.setCanSeen(false);

                }

            }
        }//HorV == false 垂直排放的逻辑部分

    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值