在咱们的实际应用中, 常常须要用到自定义控件,好比自定义圆形头像,自定义计步器等等。但有时咱们不只须要自定义控件,举个例子,FloatingActionButton 你们都很经常使用,因此你们也很常常会有一种需求,点击某个 FloatingActionButton 弹出更多 FloatingActionButton ,这个需求的通常思路是写 n 个 button 而后再一个个的去设置动画效果。但这实在是太麻烦了,因此网上有个 FloatingActionButtonMenu 这个开源库,这就是利用到了自定义布局 「ViewGroup」,如今就让我给他家介绍下,如何自定义布局 「layout」。html
相比于自定义 View ,自定义 ViewGroup 的难点在于,子控件位置的肯定和布局大小的肯定。不像 单个 View 子要花粉好模式,测量好宽度就搞定了,ViewGroup 的长宽根据子 View 的数量和单个的大小变化而变化。这就是最大的坎,因此该如何肯定 ViewGroup 的大小呢?java
这里 我为你们设计一个 相似 LinearLayout 线性布局的 ViewGroup 做为范例。android
首先,若是是一个 LinearLayout 那么当设置 wrap_content 时,他就会以子空间中最宽的那个为它的宽度。同时在高度方面会是全部子控件高度的总和。因此咱们先写两个方法,分别用于测量 ViewGroup 的宽度和高度。编程
private int getMaxWidth(){ int count = getChildCount(); int maxWidth = 0; for (int i = 0 ; i < count ; i ++){ int currentWidth = getChildAt(i).getMeasuredWidth(); if (maxWidth < currentWidth){ maxWidth = currentWidth; } } return maxWidth; } private int getTotalHeight(){ int count = getChildCount(); int totalHeight = 0; for (int i = 0 ; i < count ; i++){ totalHeight += getChildAt(i).getMeasuredHeight(); } return totalHeight; } 复制代码
对于 ViewGroup 而言咱们能够粗略的分为两种模式:固定长宽模式(match_parent),自适应模式(wrap_content),根据这两种模式,就能够对 ViewGroup 的绘制进行划分。这里关于 measureChildren 这个方法,他是用于将全部的子 View 进行测量,这会触发每一个子 View 的 onMeasure 函数,可是你们要注意要与 measureChild 区分,measureChild 是对单个 view 进行测量markdown
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); measureChildren(widthMeasureSpec, heightMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec); int width = MeasureSpec.getSize(widthMeasureSpec); int heightMode= MeasureSpec.getMode(heightMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST){ int groupWidth = getMaxWidth(); int groupHeight= getTotalHeight(); setMeasuredDimension(groupWidth, groupHeight); }else if (widthMode == MeasureSpec.AT_MOST){ setMeasuredDimension(getMaxWidth(), height); }else if (heightMode == MeasureSpec.AT_MOST){ setMeasuredDimension(width, getTotalHeight()); } } 复制代码
整完上面这些东西,咱们的布局大小七十九已经出来了,然咱们在活动的布局文件里面加上它,并添加上几个子 View 而后运行一下,先看看效果:ide
<com.entry.android_view_user_defined_first.views.MyLinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@color/colorAccent"> <Button android:layout_width="100dp" android:layout_height="50dp" android:text="qwe"/> <Button android:layout_width="250dp" android:layout_height="150dp" android:text="qwe"/> <Button android:layout_width="200dp" android:layout_height="75dp" android:text="qwe"/> </com.entry.android_view_user_defined_first.views.MyLinearLayout> 复制代码
咱们看见布局出来了,大小好像也没啥问题,可是子 View 呢??! 这么没看见子 View 在看看代码,系统以前然咱们重写的 onLayout() 仍是空着的呀!!也就是说,子 View 的大小和位置根本就尚未进行过设定!让咱们来重写下 onLayout() 方法。函数
@Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int count = getChildCount(); int currentHeight = 0; for (int i = 0 ; i < count ; i++){ View view = getChildAt(i); int height = view.getMeasuredHeight(); int width = view.getMeasuredWidth(); view.layout(l, currentHeight, l + width, currentHeight + height); currentHeight += height; } } 复制代码
成功了有木有!布局
因为本文是本身学习过程的总结,若是文中有错误,但愿你们能在评论区指出学习
最后,祝你们编程愉快!动画