zoukankan      html  css  js  c++  java
  • android--------自定义控件 之 ViewGroup

    前面几篇讲了自定义控件的组合控件,地址:http://www.cnblogs.com/zhangqie/p/8985612.html

    今天这篇博文主要来说说 自定义控件的 ViewGroup。

    什么是ViewGroup?

    ViewGroup是一种容器。它包含零个或以上的View及子View

    ViewGroup有什么作用?

    ViewGroup内部可以用来存放多个View控件,并且根据自身的测量模式,来测量View子控件,并且决定View子控件的位置。这在下面会逐步讲解它是怎么测量及决定子控件大小和位置的。

    自定义控件

    public class FlowLayoutb extends ViewGroup {
    
        private int horizontolSpacing;
        private int verticalSpacing;
    
        public FlowLayoutb(Context context) {
            super(context);
            init(context);
        }
    
        public FlowLayoutb(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            init(context);
        }
    
        private Line currentline;// 当前的行
        private int useWidth = 0;// 当前行使用的宽度
        private List<Line> mLines = new ArrayList<Line>();
        private int width;
    
        public FlowLayoutb(Context context, AttributeSet attrs) {
            super(context, attrs);
            init(context);
        }
    
        private void init(Context context) {
            horizontolSpacing = Util.dip2px(13, context);
            verticalSpacing = Util.dip2px(13, context);
        }
    
        // 测量 当前控件Flowlayout
        // 父类是有义务测量每个子View的
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            mLines.clear();
            currentline = null;
            useWidth = 0;
    
            /**
             * 获得此ViewGroup上级容器为其推荐的宽和高,以及计算模式
             */
            int widthMode = MeasureSpec.getMode(widthMeasureSpec);
            int heightMode = MeasureSpec.getMode(heightMeasureSpec);
            // 计算出所有的childView的宽和高
            measureChildren(widthMeasureSpec, heightMeasureSpec);
            width = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight();
            int height = MeasureSpec.getSize(heightMeasureSpec) - getPaddingBottom() - getPaddingTop(); // 获取到宽和高
            int childeWidthMode;
            int childeHeightMode;
            //  为了测量每个子View 需要指定每个子View测量规则
            childeWidthMode = widthMode == MeasureSpec.EXACTLY ? MeasureSpec.AT_MOST : widthMode;
            childeHeightMode = heightMode == MeasureSpec.EXACTLY ? MeasureSpec.AT_MOST : heightMode;
            int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(childeWidthMode, width);
            int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(childeHeightMode, height);
            currentline = new Line();// 创建了第一行
            for (int i = 0; i < getChildCount(); i++) {
                View child = getChildAt(i);
                // 测量每个子View
                child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
                int measuredWidth = child.getMeasuredWidth();
                useWidth += measuredWidth;// 让当前行加上使用的长度
                if (useWidth <= width) {
                    currentline.addChild(child);//这时候证明当前的子View是可以放进当前的行里,放进去
                    useWidth += horizontolSpacing;
                } else {
                    //换行
                    newLine(child);
                }
            }
    
            if (!mLines.contains(currentline)) {
                mLines.add(currentline);// 添加最后一行
            }
            int totalheight = 0;
            for (Line line : mLines) {
                totalheight += line.getHeight();
            }
            totalheight += verticalSpacing * (mLines.size() - 1) + getPaddingTop() + getPaddingBottom();
    
            System.out.println(totalheight);
            setMeasuredDimension(width + getPaddingLeft() + getPaddingRight(), resolveSize(totalheight, heightMeasureSpec));
        }
    
        private void newLine(View child) {
            mLines.add(currentline);// 记录之前的行
            currentline = new Line();// 创建新的一行
            currentline.addChild(child);
            useWidth = currentline.lineWidth;
        }
    
        private class Line {
            int height = 0; //当前行的高度
            int lineWidth = 0;
            private List<View> children = new ArrayList<View>();
    
            /**
             * 添加一个子View
             *
             * @param child
             */
            public void addChild(View child) {
                children.add(child);
                if (child.getMeasuredHeight() > height) {
                    height = child.getMeasuredHeight();
                }
                lineWidth += child.getMeasuredWidth();
            }
    
            public int getHeight() {
                return height;
            }
    
            /**
             * 返回子View的数量
             *
             * @return
             */
            public int getChildCount() {
                return children.size();
            }
    
            public void layout(int l, int t) {
                lineWidth += horizontolSpacing * (children.size() - 1);
                int surplusChild = 0;
                int surplus = width - lineWidth;
                if (surplus > 0 && children.size() > 0) {
                    surplusChild = surplus / children.size();
                }
                for (int i = 0; i < children.size(); i++) {
                    View child = children.get(i);
                    child.layout(l, t, l + child.getMeasuredWidth() + surplusChild, t + child.getMeasuredHeight());
                    l += child.getMeasuredWidth() + surplusChild;
                    l += horizontolSpacing;
                }
            }
    
        }
    
        // 分配每个子View的位置
        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            l += getPaddingLeft();
            t += getPaddingTop();
            for (int i = 0; i < mLines.size(); i++) {
                Line line = mLines.get(i);
                line.layout(l, t);  //交给每一行去分配
                t += line.getHeight() + verticalSpacing;
            }
        }
    
    }

    自定义ViewGroup的步骤:

    1. 继承ViewGroup,覆盖构造方法
    2. 重写onMeasure方法测量子控件和自身宽高
    3. 实现onLayout方法摆放子控件

    效果图:

     

    源码地址:https://github.com/DickyQie/android-custom-control

    参考资料:

    https://blog.csdn.net/shineflowers/article/details/48055879

    https://blog.csdn.net/zxt0601/article/details/50533658

    https://blog.csdn.net/shakespeare001/article/details/51089453

  • 相关阅读:
    springtools插件的安装
    Spring--Bean的配置
    BS模式与CS模式的区别
    AMD 和 CMD 的区别有哪些?
    js中import和require的区别
    6个最优秀的微信小程序UI组件库
    微信小程序获取formid
    NodeJs 实现简单WebSocket 即时通讯
    NodeJS怎么实现WebSocket功能
    NodeJs实现WebSocket——express-ws
  • 原文地址:https://www.cnblogs.com/zhangqie/p/8991196.html
Copyright © 2011-2022 走看看