zoukankan      html  css  js  c++  java
  • 自定义控件

    自定义控件中调用顺序为 onMeasure()->onLayout()->onDraw()

    需要调用全部构造方法

    1.onMeasure(int widthMeasureSpec, int heightMeasureSpec)

    (1)这个方法的作用是确定父控件与动态添加的子控件宽与高。

    (2)重写此方法需必须加上setMeasuredDimension(parentWidthPX, parentHeightPX);即父控件的宽高(pix值)

    (3)其它方法:childView.measure(childWidth, childHeight);  绘制子控件的宽高。 

    (4)这里面有一个重要的类:MeasureSpec。其中重要的属性与方法有:

             MeasureSpec封装了父布局传递给子布局的布局要求,每个MeasureSpec代表了一组宽度和高度的要求。一个MeasureSpec由大小和模式组成。

             它有三种模式:(Mode)

                        UNSPECIFIED(未指定),  父元素不对自元素施加任何束缚,子元素可以得到任意想要的大小;

                        EXACTLY(完全),父元素决定自元素的确切大小,子元素将被限定在给定的边界里而忽略它本身大小;//固定大小,与fill_parent

                        AT_MOST(至多),子元素至多达到指定大小的值。    //wrap_parent

            它常用的三个函数:

           1.static int getMode(int measureSpec):根据提供的测量值(格式)提取模式(上述三个模式之一)

          2.static int getSize(int measureSpec):根据提供的测量值(格式)提取大小值(这个大小也就是我们通常所说的大小)//pix

          3.static int makeMeasureSpec(int size,int mode):根据提供的大小值和模式创建一个测量值(格式)  ,方法体如下:  //因为measure(width, height) 中参数就是这个返回值

           public static int makeMeasureSpec(int size, int mode) {
    return size + mode;
    }

     

    2. onLayout()

      (1)这个方法的作用是控制控件的确切位置,即其中子控件放到父控件中哪个具体位置,其中就一个重要方法:

         childView.layout(left, top, right, bottom);  相对于父控件左上右下的具体px点

          (2)还有一个方法  view.getMeasureWidth() view.getMeasureHeight()  这两个是得到控件的实际大小,需要在view.measure()方法后才会有值

    3. onDraw()

    下面是一个自定义LinearLayout,主要作用为动态添加控件,支持自动换行

    @see  http://blog.csdn.net/long704480904/article/details/9011115

    Code:

    /**
     * 自定义LinearLayout
     * 主要作用 支持自动换行
     */
    public class CustomLayout extends ViewGroup {
        private int childWidth;    //子控件的宽
        private int childHeight;   //子控件的高

        private int columns;        //动态展示的列数

        //构造函数要写全,否则会报错
        public CustomLayout(Context context) {
            super(context);
        }

        public CustomLayout(Context context, AttributeSet attrs) {
            super(context, attrs);
        }

        public CustomLayout(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
        }

        /**
         * 设置需要添加的子控件的 宽、高 此处设置的宽高为最大值 具体显示的值需要看下面方法中mode设置
         *
         * @param childWidth MeasureSpec.makeMeasureSpec(size, mode) 中的size值
         * @see //MeasureSpec.makeMeasureSpec(size, mode)
         */
        public void setChild(int childWidth, int childHeight) {
            this.childWidth = childWidth;
            this.childHeight = childHeight;
        }

        /**
         * 控制子控件的换行
         * 参数中 relative to parent 是此LinearLayout相对于它的父控件,即此LinearLayout 的上一个控件
         *
         * @param changed   This is a new size or position for this view
         * @param leftPos   Left position, relative to parent
         * @param topPos    Top position, relative to parent
         * @param rightPos  Right position, relative to parent
         * @param bottomPos Bottom position, relative to parent
         */
        @Override
        protected void onLayout(boolean changed, int leftPos, int topPos, int rightPos, int bottomPos) {

            int childTotalWidth = childWidth;
            int childTotalHeight = childHeight;

            if (columns < 0) {
                columns = 1;
            }


            int curLeft = 0;     //当前 距离此控件 的左坐标   , 当换行是需要清零
            int curTop = 0;      //当前 距离此控件 的 上坐标  , 换行不需要清零
            int curColPos = 0;   //当前 绘制的第几列,用来控制换行的

            int count = getChildCount();
            for (int i = 0; i < count; i++) {
                final View childView = getChildAt(i);
                // 获取子控件Child的实际宽高值, 必须在此之前调用 child.measure() 方法,否则值为0
                int childActualWidth = childView.getMeasuredWidth();
                int childActualHeight = childView.getMeasuredHeight();

                // 计算子控件的顶点坐标,控制居中
                int left = curLeft + ((childTotalWidth - childActualWidth) / 2);
                int top = curTop + ((childTotalHeight - childActualHeight) / 2);

                // 布局子控件  这个方法可以固定指定子控件的左上右下的指定px位置,即可以通过column换行
                //重要方法, 左上右下是相对于此控件的
                childView.layout(left, top, left + childActualWidth, top + childActualHeight);

                if (curColPos >= (columns - 1)) {
                    curColPos = 0;
                    curLeft = 0;
                    curTop += (childTotalHeight + switchDipToPix(10));  //高度差为10dip
                } else {
                    curColPos++;
                    curLeft += childTotalWidth;
                }
            }
        }

        /**
         * 计算控件及子控件所占区域
         *
         * @param widthMeasureSpec  size与mode 相加, 这个控件 size 为 px值  mode  MeasureSpec.AT_MOST|EXACTLY,wrap_parent|fill_parent or 具体值
         * @param heightMeasureSpec
         */
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            // 创建测量参数, 下面的值是实际上控件的宽和高,因为MeasureSpec.AT_MOST的是wrap_parent的意思,即显示min_width,如果背景小于childWidth
            // 则有背景的时候,得到的width值为背景width,如果无背景,则显示最小的width
            // 如果 mode设置为MeasureSpec.EXACTLY  fill_parent的意思,则值等于前面赋予的size值,
            int cellWidthSpec = MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.AT_MOST);
            int cellHeightSpec = MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.AT_MOST);

            // 记录ViewGroup中Child的总个数
            int count = getChildCount();
            // 设置子空间Child的宽高
            for (int i = 0; i < count; i++) {
                View childView = getChildAt(i);
                childView.measure(cellWidthSpec, cellHeightSpec);
            }
            // 设置父容器控件所占区域大小
            int parentWidthPX = MeasureSpec.getSize(widthMeasureSpec);
            columns = parentWidthPX / childWidth;  //列数
            int lineCounts = 0;     //总共行数,用来计算整个LinearLayout的高度
            if (columns != 0) {
                lineCounts = (count / columns);
                if ((count % columns) != 0) {           //如果不能整除,证明还需要多一行
                    lineCounts++;
                }
            }
            int parentHeightPX = (lineCounts * (childHeight + switchDipToPix(10)));   //行间距 10dip
            //重写onMeasure方法 必须加上 setMeasuredDimension()这个方法来设置这个控件最终的宽高!
            setMeasuredDimension(parentWidthPX, parentHeightPX);

            // 不需要调用父类的方法
            // super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }

        private int switchDipToPix(int dipValue) {
            return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dipValue, getResources().getDisplayMetrics());
        }

    //    /**
    //     * 为控件添加边框
    //     */
    //    @Override
    //    protected void dispatchDraw(Canvas canvas) {
    //        // 获取布局控件宽高
    //        int width = getWidth();
    //        int height = getHeight();
    //        // 创建画笔
    //        Paint mPaint = new Paint();
    //        // 设置画笔的各个属性
    //        mPaint.setColor(Color.BLUE);
    //        mPaint.setStyle(Paint.Style.STROKE);
    //        mPaint.setStrokeWidth(10);
    //        mPaint.setAntiAlias(true);
    //        // 创建矩形框
    //        Rect mRect = new Rect(0, 0, width, height);
    //        // 绘制边框
    //        canvas.drawRect(mRect, mPaint);
    //        // 最后必须调用父类的方法
    //        super.dispatchDraw(canvas);
    //    }
    }

  • 相关阅读:
    团队博客-会议之初
    5.2 个人作业2
    5.1 如何利用Intellij Idea搭建python编译运行环境
    4.27 python神器——Anaconda的安装与优化配置
    4.26 AlertDialog(对话框)详解
    4.25 xmapp启动mysql出现Error: MySQL shutdown unexpectedly.
    4.24 Android Studio下应用签名的方法以及获取 MD5、SHA1(签名)、SHA256 值
    4.23 2020.2新版本idea创建javaEE web文件
    4.22 Android studio 通过获取验证码用户登陆成功
    4.21 Android 记住密码和自动登录界面的实现(SharedPreferences 的用法)
  • 原文地址:https://www.cnblogs.com/lianghui66/p/3205758.html
Copyright © 2011-2022 走看看