自定义ViewGroup入门

    这段时间一直在学习自定义控件,之前就想完成这篇博客,后来看了鸿洋和爱哥的自定义控件系列,发现里面有很多很多的东西是我之前没有想到的,内容比较多,这篇博客就算是当做一篇入门笔记来写。我刚开始写博客,没什么文采,也是想到哪就写到哪,如果觉得比较混乱什么的,见谅。

   之前写过了一篇关于自定义View的博客,其实自定义ViewGroup和自定义View是有很多的相似性的,毕竟ViewGroup也是View的一种。但是ViewGroupView还是有区别的,光从名字就看出来不一样了(笑)。ViewGroup从名字上看就是一组View,更正式的点就是放View的一个容器,他的职责就是给他包含的Viewchildview计算出建议的宽和高,并决定childview的位置布局。为什么是建议的宽和高,是因为当我们设置为wrap_content的时候,只有childview自己才能计算出自己的宽和高。对于ViewGroup如何对childview进行布局,则要使用onlayout方法,下面会具体介绍. 

LayoutParams

 自定义ViewGroup的时候,经常使用到LayoutParams,这个又是什么东西。在刚开始学习的时候我也很困惑,不知道这玩意是干什么用的,后来才明白他的意思。我们可以想想我们之前用过哪些ViewGroup,比如LinearLayout,RelativeLayout这些都会有一些自己特定的属性,比如LinearLayoutlayout_gravityRelativeLayoutlayout_centerInParent这个就是我们用到的LayoutParams

  下面说一下自定义viewgroup的几个步骤,用鸿洋的一个例子

Android 自定义ViewGroup 实战篇 -> 实现FlowLayout


1.首先我们考虑使用什么LayoutParams,这里我们只用到Margin所有使用MarginLayoutParams就可以了。

2.重写onMeasure方法,这里的onMeasure方法就是负责测量子控件的模式和大小,然后根据子控件确定自己的宽和高,贴一下代码

/** 
     * 负责设置子控件的测量模式和大小 根据所有子控件设置自己的宽和高 
     */  
    @Override  
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)  
    {  
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
        // 获得它的父容器为它设置的测量模式和大小  
        int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);  
        int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);  
        int modeWidth = MeasureSpec.getMode(widthMeasureSpec);  
        int modeHeight = MeasureSpec.getMode(heightMeasureSpec);  
  
        Log.e(TAG, sizeWidth + "," + sizeHeight);  
  
        // 如果是warp_content情况下,记录宽和高  
        int width = 0;  
        int height = 0;  
        /** 
         * 记录每一行的宽度,width不断取最大宽度 
         */  
        int lineWidth = 0;  
        /** 
         * 每一行的高度,累加至height 
         */  
        int lineHeight = 0;  
  
        int cCount = getChildCount();  
  
        // 遍历每个子元素  
        for (int i = 0; i < cCount; i++)  
        {  
            View child = getChildAt(i);  
            // 测量每一个child的宽和高  
            measureChild(child, widthMeasureSpec, heightMeasureSpec);  
            // 得到child的lp  
            MarginLayoutParams lp = (MarginLayoutParams) child  
                    .getLayoutParams();  
            // 当前子空间实际占据的宽度  
            int childWidth = child.getMeasuredWidth() + lp.leftMargin  
                    + lp.rightMargin;  
            // 当前子空间实际占据的高度  
            int childHeight = child.getMeasuredHeight() + lp.topMargin  
                    + lp.bottomMargin;  
            /** 
             * 如果加入当前child,则超出最大宽度,则的到目前最大宽度给width,类加height 然后开启新行 
             */  
            if (lineWidth + childWidth > sizeWidth)  
            {  
                width = Math.max(lineWidth, childWidth);// 取最大的  
                lineWidth = childWidth; // 重新开启新行,开始记录  
                // 叠加当前高度,  
                height += lineHeight;  
                // 开启记录下一行的高度  
                lineHeight = childHeight;  
            } else  
            // 否则累加值lineWidth,lineHeight取最大高度  
            {  
                lineWidth += childWidth;  
                lineHeight = Math.max(lineHeight, childHeight);  
            }  
            // 如果是最后一个,则将当前记录的最大宽度和当前lineWidth做比较  
            if (i == cCount - 1)  
            {  
                width = Math.max(width, lineWidth);  
                height += lineHeight;  
            }  
  
        }  
        setMeasuredDimension((modeWidth == MeasureSpec.EXACTLY) ? sizeWidth  
                : width, (modeHeight == MeasureSpec.EXACTLY) ? sizeHeight  
                : height);  
  
} 

在方法中,首先得到宽和高的sizemode,然后遍历childview,测量childview的宽高,然后根据mode设置 setMeasuredDimension

3.重写onLayout方法,这个方法主要是用来对childview的位置和大小的制定

贴一下代码

/** 
     * 存储所有的View,按行记录 
     */  
    private List<List<View>> mAllViews = new ArrayList<List<View>>();  
    /** 
     * 记录每一行的最大高度 
     */  
    private List<Integer> mLineHeight = new ArrayList<Integer>();  
    @Override  
    protected void onLayout(boolean changed, int l, int t, int r, int b)  
    {  
        mAllViews.clear();  
        mLineHeight.clear();  
  
        int width = getWidth();  
  
        int lineWidth = 0;  
        int lineHeight = 0;  
        // 存储每一行所有的childView  
        List<View> lineViews = new ArrayList<View>();  
        int cCount = getChildCount();  
        // 遍历所有的孩子  
        for (int i = 0; i < cCount; i++)  
        {  
            View child = getChildAt(i);  
            MarginLayoutParams lp = (MarginLayoutParams) child  
                    .getLayoutParams();  
            int childWidth = child.getMeasuredWidth();  
            int childHeight = child.getMeasuredHeight();  
  
            // 如果已经需要换行  
            if (childWidth + lp.leftMargin + lp.rightMargin + lineWidth > width)  
            {  
                // 记录这一行所有的View以及最大高度  
                mLineHeight.add(lineHeight);  
                // 将当前行的childView保存,然后开启新的ArrayList保存下一行的childView  
                mAllViews.add(lineViews);  
                lineWidth = 0;// 重置行宽  
                lineViews = new ArrayList<View>();  
            }  
            /** 
             * 如果不需要换行,则累加 
             */  
            lineWidth += childWidth + lp.leftMargin + lp.rightMargin;  
            lineHeight = Math.max(lineHeight, childHeight + lp.topMargin  
                    + lp.bottomMargin);  
            lineViews.add(child);  
        }  
        // 记录最后一行  
        mLineHeight.add(lineHeight);  
        mAllViews.add(lineViews);  
  
        int left = 0;  
        int top = 0;  
        // 得到总行数  
        int lineNums = mAllViews.size();  
        for (int i = 0; i < lineNums; i++)  
        {  
            // 每一行的所有的views  
            lineViews = mAllViews.get(i);  
            // 当前行的最大高度  
            lineHeight = mLineHeight.get(i);  
  
            Log.e(TAG, "第" + i + "行 :" + lineViews.size() + " , " + lineViews);  
            Log.e(TAG, "第" + i + "行, :" + lineHeight);  
  
            // 遍历当前行所有的View  
            for (int j = 0; j < lineViews.size(); j++)  
            {  
                View child = lineViews.get(j);  
                if (child.getVisibility() == View.GONE)  
                {  
                    continue;  
                }  
                MarginLayoutParams lp = (MarginLayoutParams) child  
                        .getLayoutParams();  
  
                //计算childView的left,top,right,bottom  
                int lc = left + lp.leftMargin;  
                int tc = top + lp.topMargin;  
                int rc =lc + child.getMeasuredWidth();  
                int bc = tc + child.getMeasuredHeight();  
  
                Log.e(TAG, child + " , l = " + lc + " , t = " + t + " , r ="  
                        + rc + " , b = " + bc);  
  
                child.layout(lc, tc, rc, bc);  
                  
                left += child.getMeasuredWidth() + lp.rightMargin  
                        + lp.leftMargin;  
            }  
            left = 0;  
            top += lineHeight;  
        }  
  
}  

在方法中,也是遍历childview,然后计算childviewleft,top,right,bottom ,最后在调用child.layout方法传入参数



写在最后的话:

感觉自定义viewviewgroup还是挺相似的,步骤都差不多,了解了这些算是个入门。接下来打算把鸿洋的自定义控件系列跟着博客把代码全部撸一遍,加深一下了解。关于爱歌的自定义专栏,感觉还是很深奥,看起来比较费劲,往后面放一放。然后学习一下关于网络相关的内容,尝试使用流行的框架,加油吧


相关博客:

Android 自定义ViewGroup 实战篇 -> 实现FlowLayout

 Android 手把手教您自定义ViewGroup(一)                 

Android自定义控件其实很简单


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
智慧校园建设方案旨在通过融合先进技术,如物联网、大数据、人工智能等,实现校园的智能化管理与服务。政策的推动和技术的成熟为智慧校园的发展提供了基础。该方案强调了数据的重要性,提出通过数据的整合、开放和共享,构建产学研资用联动的服务体系,以促进校园的精细化治理。 智慧校园的核心建设任务包括数据标准体系和应用标准体系的建设,以及信息化安全与等级保护的实施。方案提出了一站式服务大厅和移动校园的概念,通过整合校内外资源,实现资源共享平台和产教融合就业平台的建设。此外,校园大脑的构建是实现智慧校园的关键,它涉及到数据中心化、数据资产化和数据业务化,以数据驱动业务自动化和智能化。 技术应用方面,方案提出了物联网平台、5G网络、人工智能平台等新技术的融合应用,以打造多场景融合的智慧校园大脑。这包括智慧教室、智慧实验室、智慧图书馆、智慧党建等多领域的智能化应用,旨在提升教学、科研、管理和服务的效率和质量。 在实施层面,智慧校园建设需要统筹规划和分步实施,确保项目的可行性和有效性。方案提出了主题梳理、场景梳理和数据梳理的方法,以及现有技术支持和项目分级的考虑,以指导智慧校园的建设。 最后,智慧校园建设的成功依赖于开放、协同和融合的组织建设。通过战略咨询、分步实施、生态建设和短板补充,可以构建符合学校特色的生态链,实现智慧校园的长远发展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值