-
概述:
常用的布局类型并不能满足所有需求,这时就会用到ViewGroup。
ViewGroup作为一个放置View的容器,并且我们在写布局xml的时候,会告诉容器(凡是以layout为开头的属性,都是为用于告诉容器的),我们的宽度(layout_width)、高度(layout_height)、对齐方式(layout_gravity)等;当然还有margin等;于是乎,ViewGroup需要做的事情是:给childView计算出建议的宽和高和测量模式 ;决定childView的位置;为什么只是建议的宽和高,而不是直接确定呢,别忘了childView宽和高可以设置为wrap_content,这样只有childView才能计算出自己的宽和高。
博客名已经说明了这篇博客要将给出的内容,不用赘述。
先看效果图:

代码注释很详细,直接看代码即可,没贴源码,因为这是从一个项目里面抠出来的。
Demo
先写一个自定义LinearLayout,它的功能是自适应子控件:
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556<codeclass="language-java"hljs="">publicclassItemContainerextendsLinearLayout {privateintwidth;//组件宽privateintheight;//组件高privateintchildCount;privateintchildMarginLeft = SizeConvert.dip2px(getContext(),8);//子控件相对左边控件的距离privateintchildMarginHorizonTal = SizeConvert.dip2px(getContext(),10);//子控件相对最左、最右的距离privateintchildMarginTop = SizeConvert.dip2px(getContext(),8);//子控件相对顶部控件的距离privateintchildWidth;//子控件宽privateintchildHeight;//子控件高publicItemContainer(Context context) {super(context);}publicItemContainer(Context context, AttributeSet attrs) {super(context, attrs);}@OverrideprotectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);childCount = getChildCount();//得到子控件数量if(childCount>0) {childWidth = (width - childMarginLeft *4) /3;childHeight = SizeConvert.dip2px(getContext(),42);//给子控件的高度一个定值//根据子控件的高和子控件数目得到自身的高height = childHeight * ((childCount-1)/3+1) + childMarginHorizonTal *2+ childMarginTop*((childCount-1)/3);Log.d(childHeight,childHeight+);}else{//如果木有子控件,自身高度为0,即不显示height =0;}width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);Log.d(height,height+);//根据自身的宽度约束子控件宽度measureChildren(widthMeasureSpec, heightMeasureSpec);//设置自身宽度setMeasuredDimension(width, height);}@OverrideprotectedvoidonLayout(booleanchanged,intl,intt,intr,intb) {/*** 遍历所有子控件,并设置它们的位置和大小* 每行只能有三个子控件,且高度固定,宽度相同,且每行正好布满*/for(inti =0; i < childCount; i++) {View childView = getChildAt(i);//得到当前子控件childView.layout((i%3) * childWidth + (i%3+1)*childMarginLeft, (i /3)*childHeight + childMarginHorizonTal + (i /3)*childMarginTop, (i%3+1) * childWidth + (i%3+1)*childMarginLeft, (i /3+1)*childHeight + childMarginHorizonTal + (i /3)*childMarginTop);}}}</code>主活动完成的功能就是上面贴图演示的功能,让两个自定义ViewGroup能够添加删除子控件,子控件是在代码中动态搭建的,下面会给出方法:
活动:1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495<codeclass="language-java"hljs="">publicclassItemOperateActivityextendsBaseActivity {privateLinearLayout mContentNoItem;privateLinearLayout mContentItemRemove;privateItemContainer mItemContainerAdd;privateItemContainer mItemContainerRemove;@OverrideprotectedvoidonCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_item_operate);mContentNoItem = (LinearLayout) findViewById(R.id.linearlayout_attention_null);mContentItemRemove = (LinearLayout) findViewById(R.id.linearlayout_remove);mItemContainerAdd = (ItemContainer) findViewById(R.id.item_container_add);mItemContainerRemove = (ItemContainer) findViewById(R.id.item_container_remove);initItems(newString[]{随时定位, 客户拜访},newString[]{新增客户, 客户总量});}/*** 添加条目时需要调用的方法* @param name*/privatevoidaddItem(String name) {mContentNoItem.setVisibility(View.GONE);Button button =newButton(getApplicationContext());ViewGroup.LayoutParams params =newViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,0);button.setLayoutParams(params);button.setText(name);button.setGravity(Gravity.CENTER);button.setBackgroundResource(R.drawable.item_select_bg);button.setTextSize(13);button.setTextColor(Color.argb(255,47,79,79));//铅灰色button.setOnClickListener(newView.OnClickListener() {@OverridepublicvoidonClick(View v) {mItemContainerAdd.removeView(v);removeItem(((Button) v).getText().toString());if(mItemContainerAdd.getChildCount()==0) {mContentNoItem.setVisibility(View.VISIBLE);}}});mItemContainerAdd.addView(button);}/*** 删除条目时需要调用的方法* @param name*/privatevoidremoveItem(String name) {mContentItemRemove.setVisibility(View.VISIBLE);Button button =newButton(getApplicationContext());ViewGroup.LayoutParams params =newViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,0);button.setLayoutParams(params);button.setText(name);button.setGravity(Gravity.CENTER);button.setBackgroundResource(R.drawable.item_select_bg_below);button.setTextSize(13);button.setTextColor(Color.argb(255,47,79,79));//铅灰色button.setOnClickListener(newView.OnClickListener() {@OverridepublicvoidonClick(View v) {/*** 点击按钮时,删除*/mItemContainerRemove.removeView(v);addItem(((Button) v).getText().toString());if(mItemContainerRemove.getChildCount() ==0) {mContentItemRemove.setVisibility(View.GONE);}}});mItemContainerRemove.addView(button);}/*** 初始化子控件* @param itemsAdd 已添加的子控件名数组* @param itemsRemove 可添加的子控件名数组*/privatevoidinitItems(String[] itemsAdd, String[] itemsRemove) {for(String itemAdd : itemsAdd) {addItem(itemAdd);}for(String itemRemove : itemsRemove) {removeItem(itemRemove);}}}</code>布局:
123456789101112131415161718192021222324252627282930313233343536373839404142434445<codeclass="language-xml"hljs=""><!--?xml version=1.0encoding=utf-8?--><linearlayout android:layout_height="match_parent"android:layout_width="match_parent"android:orientation="vertical"xmlns:android="http://schemas.android.com/apk/res/android"><relativelayout android:background="@drawable/item_bg"android:layout_height="60dp"android:layout_width="match_parent"><linearlayout android:gravity="center"android:id="@+id/linearlayout_back"android:layout_alignparentleft="true"android:layout_centervertical="true"android:layout_height="wrap_content"android:layout_marginleft="5dp"android:layout_width="wrap_content"android:orientation="horizontal"><imagebutton android:background="@drawable/btn_back/"android:layout_height="wrap_content"android:layout_width="wrap_content"><textview android:layout_height="wrap_content"android:layout_width="wrap_content"android:text="报表"android:textsize="@dimen/head_left_text_size/"></textview></imagebutton></linearlayout><textview android:layout_centerinparent="true"android:layout_height="wrap_content"android:layout_width="wrap_content"android:text="订阅"android:textsize="@dimen/head_center_text_size/"></textview></relativelayout><linearlayout android:background="@drawable/item_bg"android:layout_height="wrap_content"android:layout_width="match_parent"android:orientation="horizontal"android:paddingbottom="12dp"android:paddingleft="8dp"android:paddingtop="12dp"><textview android:layout_height="wrap_content"android:layout_width="wrap_content"android:text="已添加"android:textsize="16dp/"><textview android:layout_height="wrap_content"android:layout_width="wrap_content"android:text="(点击删除)"android:textsize="13dp/"></textview></textview></linearlayout><com.test.shiweiwei.myproject.selfish_view.self_defined_view_group.itemcontainer android:background="@drawable/item_bg"android:id="@+id/item_container_add"android:layout_height="wrap_content"android:layout_width="match_parent"></com.test.shiweiwei.myproject.selfish_view.self_defined_view_group.itemcontainer><linearlayout android:background="@drawable/item_bg"android:gravity="center"android:id="@+id/linearlayout_attention_null"android:layout_height="wrap_content"android:layout_width="match_parent"android:orientation="vertical"android:paddingbottom="35dp"android:paddingtop="35dp"android:visibility="gone"><imageview android:layout_height="wrap_content"android:layout_width="wrap_content"android:src="@mipmap/attendance_null/"><textview android:layout_height="wrap_content"android:layout_width="wrap_content"android:text="无报表信息/"android:textsize="13dp"></textview></imageview></linearlayout><imageview android:layout_height="wrap_content/"android:layout_width="match_parent"><linearlayout android:id="@+id/linearlayout_remove"android:layout_height="wrap_content"android:layout_width="match_parent"android:orientation="vertical"><linearlayout android:background="@drawable/item_bg"android:layout_height="wrap_content"android:layout_margintop="@dimen/first_page_item_margin_top"android:layout_width="match_parent"android:orientation="horizontal"android:paddingbottom="12dp"android:paddingleft="8dp"android:paddingtop="12dp"><textview android:layout_height="wrap_content"android:layout_width="wrap_content"android:text="可添加"android:textsize="16dp/"></textview></linearlayout><com.test.shiweiwei.myproject.selfish_view.self_defined_view_group.itemcontainer android:background="@drawable/item_bg"android:id="@+id/item_container_remove"android:layout_height="wrap_content"android:layout_width="match_parent"></com.test.shiweiwei.myproject.selfish_view.self_defined_view_group.itemcontainer></linearlayout></imageview></linearlayout></code>drawable下的文件:
item_bg:12345<codeclass="language-xml"hljs=""><!--?xml version=1.0encoding=utf-8?--><solid android:color="@color/white"></solid><stroke android:color="@color/stroke_vertical"android:width="0.3dp"></stroke></shape></code>item_select_bg:
1234567<codeclass="language-xml"hljs=""><!--?xml version=1.0encoding=utf-8?--><solid android:color="@color/white"></solid><stroke android:color="@color/dark_brow"android:width="0.3dp"></stroke><corners android:radius="4dp"></corners><padding android:bottom="12dp"android:top="12dp"></padding></shape><!--?xml version=1.0encoding=utf-8?--></code>item_select_bg_below:
123456<codeclass="language-xml"hljs=""><!--?xml version=1.0encoding=utf-8?--><solid android:color="@color/white"></solid><stroke android:color="@color/stroke_vertical"android:width="0.3dp"></stroke><corners android:radius="4dp"></corners></shape></code>
相关推荐
AutoFlowLayout:多功能流式布局与网格布局控件
近期工作需要用到流式布局,网上也有很多关于这方面的资料。发现流式布局与网格布局的自定义很有意思,是学习自定义控件的一个很好的方式,所以就撸了个几百行代码的控件,既实用又具有学习价值。 一、AutoFlowLayout应用场景流式布局,在很多标签类的场景中可以用的;而网格布局在分类中以及自拍九宫格等场景很常见。如下所示: 如此使用频繁而又实现简单的控件,怎能不自己撸一个呢?控件,还是定制的好啊。
Android 自动换行自定义ViewGrop
项目中需要添加子View后在宽度不够的时候能够自动换行,自己写了个简单的自定义View public class AutoLineLayout extends ViewGroup { private int rowHeight = 100; private boolean centerInRow = true; public AutoLineLayout(Context
Android自动换行标签控件
使用RelativeLayout实现自动换行标签控件。
android 多个view自动换行
重绘groupview来实现多个textview或button的自动换行
android自定义控件实现自动换行效果
自定义ViewGroup 实现类似于电商平台上的商品属性显示效果
Android自定View流式布局根据文字数量换行
主要为大家详细介绍了Android自定View流式布局,根据文字数量换行,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
Android 自定义ViewGroup之实现FlowLayout-标签流容器
本篇文章讲的是Android 自定义ViewGroup之实现标签流式布局-FlowLayout,开发中我们会经常需要实现类似于热门标签等自动换行的流式布局的功能,网上也有很多这样的FlowLayout,但不影响我对其的学习。和往常一样,主要还是想总结一下自定义ViewGroup的开发过程以及一些需要注意的地方。按照惯例,我们先来看看效果图 一、写代码之前,有几个是问题是我们先要弄清楚的: 1、什
最全卷积总结篇(分组、深度可分离、空洞、转置等)
分组卷积(Group Convolution)是一种卷积操作方式,它最早在AlexNet中引入,并在深度学习中被广泛使用,尤其是在高效模型设计(如MobileNet和ResNeXt)中。分组卷积通过将输入特征通道分为若干组,并在每组内单独进行卷积操作,可以减少计算复杂度和参数量,同时有效利用模型的结构特性。分组卷积的工作原理通道分组:输入特征图的通道图为C_in,输出特征图的通道数为C_out,将每组的C_in和C_out分为G组,每组的通道数为C_in/G和C_out/G。组内卷积。
Android自动换行控件开发与优化实践
自动换行布局是Android UI开发中的常见需求,尤其在标签云、动态筛选等场景中至关重要。其核心原理基于ViewGroup的测量(onMeasure)和布局(onLayout)机制,通过智能计算子视图尺寸和屏幕可用宽度实现自适应排列。相比系统原生方案,优化后的自动换行控件在性能(如减少40%布局时间)和功能(支持动态间距调整、三种动画模式)方面具有显著优势。该技术特别适合电商App的商品标签、社交媒体的兴趣标签等需要动态展示多标签的场景。通过合理运用贪心算法和缓存机制,开发者可以构建出既高效又灵活的自动换
Wider Face+YOLOV7人脸检测
使用Wider Face数据集训练YOLOV7
std::map看来可以被淘汰了?可以用vector来替代,而且更好
vector的优势在于内存连续,可以先std::sort,然后std::lower_bound,或者std::binary_search。而std::map,insert的时候,需要new出内存。
android开发之添加标签与删除标签
老样子,先上效果图标签容器这个在github上已经有很多人实现了,既然有轮子那为什么还要重复制造轮子呢? 本人比较懒,直接参考了https://github.com/XuYongJun96/FlowLayout 在它的基础上扩展了添加标签与删除标签的功能。思路既然是标签容器,那当然就用到了ViewGroup了。 重写onMeasure与onLayout方法。 在onMeasure计算每个标签
避坑指南:ZYNQ MPSOC QSPI固化时MT25QU256闪存的特殊配置
本文针对Xilinx ZYNQ UltraScale+ MPSoC平台,详细解析了使用Vitis工具将BOOT.bin固化至Micron MT25QU256ABA QSPI NOR Flash时的特殊配置与避坑指南。重点阐述了Init文件的真实作用、电压与频率的精确匹配策略,以及针对该闪存型号的初始化序列,帮助工程师解决烧录失败、启动异常等常见问题,确保系统可靠启动。
android动态添加网格布局,AutoFlowLayout:多功能流式布局与网格布局控件
近期工作需要用到流式布局,网上也有很多关于这方面的资料。发现流式布局与网格布局的自定义很有意思,是学习自定义控件的一个很好的方式,所以就撸了个几百行代码的控件,既实用又具有学习价值。一、AutoFlowLayout应用场景流式布局,在很多标签类的场景中可以用的;而网格布局在分类中以及自拍九宫格等场景很常见。如下所示:如此使用频繁而又实现简单的控件,怎能不自己撸一个呢?控件,还是定制的好啊。二、Au...
Aspose Java 25.10 版本深度破解:基于字节码修改的实战指南
本文提供了Aspose Java 25.10版本基于字节码修改的实战指南。详细介绍了如何通过Javassist工具逆向分析并修改Aspose Cells、Words、Slides及PDF组件的许可证验证逻辑,从而实现绕过限制,并完成最终的打包与功能验证。
智能水下机器人设计: 基于MATLAB的水下机器人动力学建模和控制系统设计,包括水下环境感知、路径规划和任务执行等
智能水下机器人是指能够在水下自主完成各种任务的机器人,例如探测、勘察、救援等。它具有广阔的应用前景,可用于海洋环境监测、水下考古、海上作业等领域。基于MATLAB的水下机器人设计可以为科研人员和工程师提供强大的建模和仿真工具,帮助他们设计更智能、更高效的水下机器人。智能水下机器人是未来海洋科技发展的重要方向,它将为海洋科学研究、水下考古和海上作业等领域带来新的机遇。MATLAB是一种功能强大的数学和科学计算软件,它可以为智能水下机器人设计提供强大的工具和支持。
自定义流式布局+添加+清空
定义的流式布局的类(FlowView) import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.ViewGroup; //------- public class FlowView extends ViewGroup ...
Z-Image Turbo随机性增强插件的下载教程及使用方法 z-image生成图像太相似怎么办 Z-Image Turbo 整合包下载
Z-Image Turbo 是出了名的“seed 不敏感”,非常难做多样化图像创作。ComfyUi-ConditioningNoiseInjection 插件通过在conditioning(提示词编码)阶段加入可控噪声,成功解决了 Turbo 随机性不足的问题。它不仅能让 Turbo 产生更丰富的构图差异,还能保持主题稳定,是目前我测试过最有效的 Turbo 随机性增强工具。
898



被折叠的 条评论
为什么被折叠?



