文档章节

自定义ViewGroup

Gnepux
 Gnepux
发布于 2016/12/27 16:43
字数 1205
阅读 36
收藏 1

关于View的基本知识和如何自定义View请见

自定义View - https://my.oschina.net/u/3026396/blog/812942

ViewGroup的职责是啥?

ViewGroup相当于一个放置View的容器,并且我们在写布局xml的时候,会告诉容器(凡是以layout为开头的属性,都是用于告诉容器的),我们的宽度layout_width、高度layout_height、对齐方式layout_gravity、内边距layout_maring等。所以ViewGroup的智能为:给childView计算出建议的宽和高的测量模式;决定childView的位置。(因为childView宽和高可以设置为wrap_content,这样只有childView才能计算出自己的宽和高,所以这里给出的建议宽和高)

View的职责是啥?

View的职责就是根据测量模式和ViewGroup给出的建议的宽和高,计算出自己的宽和高;同事还有个更重要的职责:在ViewGroup为其指定的区域内绘制自己的形态。

ViewGroup和LayoutParams之间的关系?

每个ViewGroup需要指定一个LayoutParams,用于确定它的childView支持哪些属性。不同的ViewGroup指定的LayoutParams是不同的,包含的属性也不尽相同。

View的3种测量模式

EXACTLY、AT_MOST、UNSPECIFIED

从API角度进行浅析

上面叙述了ViewGroup和View的职责,下面从API角度进行浅析。

View需要根据ViewGroup传入的测量值和模式,对自己宽高进行确定(onMeasure中完成),然后在onDraw中完成对自己的绘制。

ViewGroup需要给View传入view的测量值和模式(onMeasure中完成),而且对于此ViewGroup的父布局,自己也需要在onMeasure中完成对自己宽和高的确定。此外,需要在onLayout中完成对其childView的位置的指定。

Demo

定义一个ViewGroup,内部可以传入0到4个childView,分别依次显示在左上角、右上角、左下角、右下角。同时在中央绘制一个圆。

/**
 * Created by xupeng on 16/12/26.
 */

public class MyCustomViewGroup extends ViewGroup {

    private Paint mPaint;

    public MyCustomViewGroup(Context context) {
        super(context);
        init();
    }

    public MyCustomViewGroup(Context context, AttributeSet attrs) {
//        super(context, attrs);
        this(context);
    }

    private void init() {
        setWillNotDraw(false);
        mPaint = new Paint();
        mPaint.setColor(Color.RED);
    }

    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new MarginLayoutParams(getContext(), attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        /**
         * 获得此ViewGroup上级容器为其推荐的宽和高,以及计算模式
         */
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);

        // 计算出所有的children的宽和高
        measureChildren(widthMeasureSpec, heightMeasureSpec);

        /**
         * 记录如果是wrap_content设置的宽和高
         */
        int width = 0;
        int height = 0;

        int cCount = getChildCount();

        int cWidth = 0;
        int cHeight = 0;
        MarginLayoutParams cParams = null;

        // 用于计算左边两个childView的高度
        int lHeight = 0;

        // 用于计算右边两个childView的高度,最终高度取两者之间大值
        int rHeight = 0;

        // 用于计算上边两个childView的宽度
        int tWidth = 0;

        // 用于计算下边两个childView的宽度,最重宽度取两者之间大值
        int bWidth = 0;

        /**
         * 根据childView计算得出宽和高,以及设置的margin计算容器的宽和高,主要用于容器是wrap_content时
         */
        for (int i = 0; i < cCount; i++) {
            View childView = getChildAt(i);
            cWidth = childView.getMeasuredWidth();
            cHeight = childView.getMeasuredHeight();
            cParams = (MarginLayoutParams) childView.getLayoutParams();

            // 上面两个childView
            if (i == 0 || i == 1) {
                tWidth += cWidth + cParams.leftMargin + cParams.rightMargin;
            }

            // 下面两个childView
            if (i == 2 || i == 3) {
                bWidth += cWidth + cParams.leftMargin + cParams.rightMargin;
            }

            if (i == 0 || i == 2) {
                lHeight += cHeight + cParams.topMargin + cParams.bottomMargin;
            }

            if (i == 1 || i == 3) {
                rHeight += cHeight + cParams.topMargin + cParams.bottomMargin;
            }

        }

        width = Math.max(tWidth, bWidth);
        height = Math.max(lHeight, rHeight);

        setMeasuredDimension((widthMode == MeasureSpec.EXACTLY) ? widthSize : width,
                (heightMode == MeasureSpec.EXACTLY) ? heightSize : height);

//        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int cCount = getChildCount();
        int cWidth = 0;
        int cHeight = 0;
        MarginLayoutParams cParams = null;

        /**
         * 遍历所有childView根据其宽和高,以及margin进行布局
         */
        for (int i = 0; i < cCount; i++) {
            View childView = getChildAt(i);
            cWidth = childView.getMeasuredWidth();
            cHeight = childView.getMeasuredHeight();
            cParams = (MarginLayoutParams) childView.getLayoutParams();

            int cl = 0, ct = 0, cb = 0, cr = 0;

            switch (i) {
                case 0:
                    cl = cParams.leftMargin;
                    ct = cParams.topMargin;
                    break;
                case 1:
                    cl = getWidth() - cWidth - cParams.rightMargin;
                    ct = cParams.topMargin;
                    break;
                case 2:
                    cl = cParams.leftMargin;
                    ct = getHeight() - cHeight - cParams.bottomMargin;
                    break;
                case 3:
                    cl = getWidth() - cWidth - cParams.rightMargin;
                    ct = getHeight() - cHeight - cParams.bottomMargin;
                    break;
            }

            cr = cl + cWidth;
            cb = cHeight + ct;
            childView.layout(cl, ct, cr, cb);
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int width = getWidth();
        int measureWidth = getMeasuredWidth();
        requestLayout();
        int measureWidthAndState = getMeasuredWidthAndState();
        canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, 100 , mPaint);
//        invalidate();
    }
<?xml version="1.0" encoding="utf-8"?>
<com.gnepux.sdkusage.widget.MyCustomViewGroup xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_custom_view_group"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ffff00"
    tools:context="com.gnepux.sdkusage.activity.CustomViewGroupActivity">

    <com.gnepux.sdkusage.widget.MyCustomView
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:text="1"
        android:gravity="center"
        android:textColor="#ffffff"
        android:background="#ff0000"/>

    <com.gnepux.sdkusage.widget.MyCustomView
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:text="2"
        android:gravity="center"
        android:textColor="#ffffff"
        android:background="#00ff00"/>

    <com.gnepux.sdkusage.widget.MyCustomView
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:text="3"
        android:gravity="center"
        android:textColor="#ffffff"
        android:background="#0000ff"/>

    <com.gnepux.sdkusage.widget.MyCustomView
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:text="4"
        android:gravity="center"
        android:textColor="#ffffff"
        android:background="#ff00ff"/>

</com.gnepux.sdkusage.widget.MyCustomViewGroup>

 

参考链接:

View原理简介

http://blog.csdn.net/u011733020/article/details/50849475

帮你搞定Android自定义View

http://www.jianshu.com/p/84cee705b0d3

Android手把手教您自定义ViewGroup

http://blog.csdn.net/lmj623565791/article/details/38339817/

为什么自定义ViewGroup ondraw方法不会被调用

http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/1014/1765.html

关于getMeasuredHeight和getHeight区别(getMeasureHeight是xml或代码中制定的测量高度,setMeasuredHeight; getHeight是实际显示出来的高度,通过view.layout()方便可以改变其值)

http://blog.csdn.net/qq_29951983/article/details/50571840

© 著作权归作者所有

下一篇: 自定义View
Gnepux
粉丝 3
博文 96
码字总数 77557
作品 0
南京
私信 提问
你的自定义View是否真的支持Margin

简书 编程之乐 转载请注明原创出处! 前些日子重温了早已忘却的JavaEE的一些知识点,然后又捣鼓了Python,《Chrome扩展开发》和《JavaFX》相关的东西,花了点时间在上面,下面两个项目是学习...

编程之乐
2017/10/27
0
0
教你步步为营掌握自定义ViewGroup

一、自定义ViewGroup必须清楚的基本原理 在学习一个技术点的时候,我会先搞清楚它的基本原理,然后再动手编码,因为我希望对自己写的每一行代码最终将会怎样执行有精准的把握。否则,我写代码...

微笑的江豚
2016/09/05
9
0
笔记51 | Android自定义View(二)

地址 http://blog.csdn.net/xiangyong1521/article/details/78804104 http://www.jianshu.com/p/c84693096e41 自定义ViewGroup 自定义View的过程很简单,就那几步,可自定义ViewGroup可就没那......

项勇
2017/12/14
0
0
Android 手把手教您自定义ViewGroup(一)

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38339817 , 本文出自:【张鸿洋的博客】 最近由于工作的变动,导致的博客的更新计划有点被打乱,希望可以尽快脉动回来~...

微笑的江豚
2016/08/01
7
0
Android 自定义控件之继承 ViewGroup 创建新容器(四)

Android 自定义控件之继承 ViewGroup 创建新容器(四) 阅读 3311收藏 1522016-4-24 原文链接:http://blog.csdn.net/guiman/article/details/51225809 作者叫你如何自定义一个 ViewGroup —...

微笑的江豚
2016/08/01
8
0

没有更多内容

加载失败,请刷新页面

加载更多

以太坊私有链搭建

https://blog.csdn.net/Blockchain_lemon/article/details/80589123

Moks角木
41分钟前
1
0
自律给我自信-为什么要自律

为什么要自律 混一天和努力一天 看不到任何差别 3天看不到任何变化 7天也看不到任何效果 但是 1个月后, 会看到话题不同 3个月后, 会看到气场不同 6个月后, 会看到距离不同 3年后, 会看到...

周大壮
41分钟前
1
0
读书replay计划说明

突然脑袋一闪,我有了这样一个主意:通过写博客的方式,将我阅读的书中的内容replay出来。 我一般会找着我感兴趣的书去读,一般也会读书中我感兴趣的章节,或者当下对我有用的章节,所以这个...

wanxiangming
42分钟前
0
0
CentOS7安装xrdp环境可实现远程桌面访问

CentOS7安装xrdp环境可实现远程桌面访问 2018-07-14 06:39:28 分类:运维 阅读(2051) 评论(0) 在"Ubuntu系统安装xrdp桌面客户端及实现远程连接桌面"文章中有分享过在Ubuntu系统中安装XRDP环境...

linjin200
今天
5
0
ConfigurationProperties

package cn.enjoy.config; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @ConfigurationProperties(pr......

少年已不再年少
今天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部