栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

关于自定View的ViewGroup (嵌套View)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

关于自定View的ViewGroup (嵌套View)

最终代码在最后,每行都注释了,不想看写的文字的自己取(我写的是仿照流式布局形式写的,你可以借鉴这个思路写)显示效果在最后。

首先我先简单解释一下原理 , 上图片

ww2

接下来我将一段段的讲解,最终代码在最后,都有我写的注释。

首先写Android这么久了肯定知道,写一个控件有三种方式来控制控件的大小:

   match_parent 、wrap_content、(给定确切大小如200dp)

所以自定义View同样也会有这样三种模式,给定View的大小咱就先不说了,就先谈没给定大小, 怎么获得View的宽高。

自定义View跟ViewGroup有一定区别:View可以只走onMeasure(),onDraw 就可以运行,ViewGroup走onMeasure ->onLayou 可以运行;

所以测量ViewGroup 跟View的大小在onMeasure()方法内进行

 在onMeasure()方法内我们肯定要先计算View的大小,当然如果我们将所有的View的都添加进ViewGroup却大于ViewGroup的容器肯定不会显示超越ViewGroup的部分,所以这个要注意,当我们View未超过ViewGroup则可以按照我们的思路排序。排序完成后在确定ViewGroup的大小显示,如果是固定值那么我们获得的ViewGroup还是他的固定值。

 计算ViewGroup下的所有子View:

   因为我写的是一个流式的布局类似,所以我循环内得这样写

      首先我的显示第一行的内容,以行为单位显示(显示代码在OnLayout)所以我要计算每个孩子的宽高(孩子的宽相加用于判断当前是否超过了这一行的最大限制)(孩子的高保留最大的用于判断这行高度),如果这行已经占用大宽度已经不能容纳下一个View的时候我们要换行。换行就该把记录的List(放置添加的View的List)制空重新添加下一行,由于我们还是需要在OnLayout显示,所以我们要定义一个全局的List>去记录这一行的View,与这行的高度(LineHeight)。

int childCount = getChildCount();//获得孩子的个数以便操作循环

        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);//获得第i个孩子的View
            LayoutParams child = childView.getLayoutParams();//获得孩子布局的参数
            //widthMeasureSpec父布局的宽 getPaddingLeft() + getPaddingRight() 获取父布局边界值  child.width获得孩子View的宽
            int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, getPaddingLeft() + getPaddingRight(), child.width);
            //heightMeasureSpec的高 getPaddingLeft() + getPaddingRight() 获取边界值  child.height获得孩子View的高
            int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, getPaddingTop() + getPaddingBottom(), child.height);
            childView.measure(childWidthMeasureSpec, childHeightMeasureSpec);//给定某个孩子的大小
            //测量完成判此View是否还可以在此行放下
            //判断此行是否还可以放下一个子View,如果放不下换行,将数据清空。
            // selfWeight ViewGroup的总宽度
            int childMeasureWidth = childView.getMeasuredWidth();
            int childMeasureHeight = childView.getMeasuredHeight();
            if (childMeasureWidth + lineWidthUsed + mHorizontalSpacing > selfWidth) {
                
                allLines.add(lineViews);
                linHeight.add(thisLineHeight);
                
                parentNeededHeight = parentNeededHeight + thisLineHeight + mHorizontalSpacing;// 高度相加
                parentNeededWidth = Math.max(parentNeededWidth, lineWidthUsed + mHorizontalSpacing);//判断宽度那个宽选择那个
                Log.i("TAG1111", "孩子的控件 " + lineViews.size());

                lineViews = new ArrayList<>();//重置布局List
                lineWidthUsed = 0;//重置宽
                thisLineHeight = 0;//重置宽高
            }

            
            lineViews.add(childView);

            lineWidthUsed = lineWidthUsed + childMeasureWidth + mHorizontalSpacing;
            thisLineHeight = Math.max(thisLineHeight, childMeasureHeight);
            //判断如何换行

        }

    当判断完所有的孩子后我们会的到如下数据:

       (每一行的行高相加的结果、每一行作比较求的的最大行宽)

这是我们做判断是不是给了match_parent 或 确定值,如果没给那就用我们用我们计算的值,给了就用给定的值。这样就给规定好了ViewGroup的大小,同时孩子也都计算过自己的值了。

  int realWidth = (widthMode == MeasureSpec.EXACTLY) ? selfWidth : parentNeededWidth;//如果父布局给确切大小 selWeight,如果不是给我们算出来的Size;
        int realHeight = (heightMode == MeasureSpec.EXACTLY) ? selfHeight : parentNeededHeight;
        setMeasuredDimension(realWidth, realHeight);//给定父布局的大小

我么在通过OnLayout显示以西我们想要的这种流式布局。

    两层for循环是判断用于显示具体的View,只要获得每哥View的测量好的宽高加上我们设置的间距就行。

    int curLeft = getPaddingLeft();//获得父母的padding
        int curTop = getPaddingTop();
        //布局依托与父布局
        int lineCount = allLines.size();
        
        for (int i = 0; i < lineCount; i++) {
            List lineView = allLines.get(i);
            int linHeights = linHeight.get(i);
            for (int j = 0; j < lineView.size(); j++) {
                View view = lineView.get(j);
                int left = curLeft;
                int top = curTop;
                int right = left + view.getMeasuredWidth();//getMeasuredWidth度量之后就有一个正确的值
                int bottom = top + view.getMeasuredHeight();
                view.layout(left, top, right, bottom);
                curLeft = right + mHorizontalSpacing;

            }
            curTop = curTop + linHeights + mHorizontalSpacing;
            curLeft = getPaddingLeft();
        }

最终代码如下

   如何写我已经写出来了,怎样显示就怎样改。 而且每行都有注释,相信你能看懂。

MyView.Java

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class MyView extends ViewGroup {
    private List> allLines; //记录所有行,一行行的储存,用于layout
    List linHeight = new ArrayList<>();//记录每一行的的行高,用于 layout
    //定义padding边距
    private int mHorizontalSpacing = 40;

    //代码new使用
    public MyView(Context context) {
        super(context);
    }

    //反射
    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    //主题style
    public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

    }

    private void init() {
         if (allLines != null) {
            allLines.clear();
        } else {
            allLines = new ArrayList<>();
        }
        linHeight = new ArrayList<>();
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {

        int curLeft = getPaddingLeft();//获得父母的padding
        int curTop = getPaddingTop();
        //布局依托与父布局
        int lineCount = allLines.size();
        
        for (int i = 0; i < lineCount; i++) {
            List lineView = allLines.get(i);
            int linHeights = linHeight.get(i);
            for (int j = 0; j < lineView.size(); j++) {
                View view = lineView.get(j);
                int left = curLeft;
                int top = curTop;
                int right = left + view.getMeasuredWidth();//getMeasuredWidth度量之后就有一个正确的值
                int bottom = top + view.getMeasuredHeight();
                view.layout(left, top, right, bottom);
                curLeft = right + mHorizontalSpacing;

            }
            curTop = curTop + linHeights + mHorizontalSpacing;
            curLeft = getPaddingLeft();
        }


    }

    //测量
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        init();
        List lineViews = new ArrayList<>();//保存一行有多少东西的List
        int lineWidthUsed = 0;//一行已使用了多长
        int thisLineHeight = 0;//这行的高度
        int selfWidth = MeasureSpec.getSize(widthMeasureSpec);//解析父布局的宽
        int selfHeight = MeasureSpec.getSize(heightMeasureSpec);//解析父布局的高

        int parentNeededWidth = 0; //记录父布局的宽
        int parentNeededHeight = 0;//记录父布局的高

        //度量孩子
        int childCount = getChildCount();//获得孩子的个数以便操作循环

        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);//获得第i个孩子的View
            LayoutParams child = childView.getLayoutParams();//获得孩子布局的参数
            //widthMeasureSpec父布局的宽 getPaddingLeft() + getPaddingRight() 获取父布局边界值  child.width获得孩子View的宽
            int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, getPaddingLeft() + getPaddingRight(), child.width);
            //heightMeasureSpec的高 getPaddingLeft() + getPaddingRight() 获取边界值  child.height获得孩子View的高
            int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, getPaddingTop() + getPaddingBottom(), child.height);
            childView.measure(childWidthMeasureSpec, childHeightMeasureSpec);//给定某个孩子的大小
            //测量完成判此View是否还可以在此行放下
            //判断此行是否还可以放下一个子View,如果放不下换行,将数据清空。
            // selfWeight ViewGroup的总宽度
            int childMeasureWidth = childView.getMeasuredWidth();
            int childMeasureHeight = childView.getMeasuredHeight();
            if (childMeasureWidth + lineWidthUsed + mHorizontalSpacing > selfWidth) {
                
                allLines.add(lineViews);
                linHeight.add(thisLineHeight);
                
                parentNeededHeight = parentNeededHeight + thisLineHeight + mHorizontalSpacing;// 高度相加
                parentNeededWidth = Math.max(parentNeededWidth, lineWidthUsed + mHorizontalSpacing);//判断宽度那个宽选择那个
                lineViews = new ArrayList<>();//重置布局List
                lineWidthUsed = 0;//重置宽
                thisLineHeight = 0;//重置宽高
            }

            
            lineViews.add(childView);

            lineWidthUsed = lineWidthUsed + childMeasureWidth + mHorizontalSpacing;
            thisLineHeight = Math.max(thisLineHeight, childMeasureHeight);
            //判断如何换行

        }
        
        allLines.add(lineViews);
        linHeight.add(thisLineHeight);

        int widthMode = MeasureSpec.getMode(widthMeasureSpec); //获得ViewGroup的宽
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);//获得ViewGroup的高
        
        int realWidth = (widthMode == MeasureSpec.EXACTLY) ? selfWidth : parentNeededWidth;//如果父布局给确切大小 selWeight,如果不是给我们算出来的Size;
        int realHeight = (heightMode == MeasureSpec.EXACTLY) ? selfHeight : parentNeededHeight;
        setMeasuredDimension(realWidth, realHeight);//给定父布局的大小
    }

MainActivity.xml




    

        

        
        
        
        

        
        

    

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/685788.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号