Android中使用自定义ViewGroup的总结
发布时间 - 2026-01-10 22:31:59 点击率:次分类

自定义Layout可以分为两种情况。
- 自定义ViewGroup,创造出一些不同于LinearLayout,RelativeLayout等之类的ViewGroup。比如:API 14以后增加的GridLayout、design support library中的CoordinatorLayout等等。
- 自定义一些已经有的Layout然后加一些特殊的功能。比如:TableLayout以及percent support library中的PercentFrameLayout等等。
流程
自定义View的流程是:onMeasure()->onLayout()->onDraw()。自定义ViewGroup的时候一般是不要去实现onDraw的,当然也可能有特殊的需求,比如:CoordinatorLayout。
所以onMeasure和onLayout基本能做大部分我们接触的ViewGroup。但是仅仅的知道在onMeasure中测量ViewGroup的大小以及在onLayout中计算Child View的位置还是不够。
比如:怎么可以给ViewGroup的Child View设置属性?
一个例子。
写一个自定义的ViewGroup,增加一个属性控制Child View的大小(长宽)占ViewGroup的比例。
假设是一个LinearLayout,那么就先定义一个CustomLinearLayout。
public class CustomLinearLayout extends LinearLayout {
public CustomLinearLayout(Context context) {
super(context);
}
public CustomLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public CustomLinearLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
}
其它抛开不说,我们是需要增加属性用来控制Child View的大小。那么就先在value/attr.xml中定义那个属性(使用CustomLinearLayout_Layout来与CustomLinearLayout区分下,当然这个名字是随意的)。
<declare-styleable name="CustomLinearLayout_Layout"> <!-- 定义比例 --> <attr name="inner_percent" format="float"/> </declare-styleable>
ViewGroup调用addView()的时候最终都会调用到这个方法。
public void addView(View child, int index, LayoutParams params)
这个params代表的就是View的配置,ViewGroup.LayoutParams中就包含了width、height,LinearLayout.LayoutParams增加了weight属性等等。那么我们就应该实现一个LayoutParams。那么现在就是这样了。
public class CustomLinearLayout extends LinearLayout {
public CustomLinearLayout(Context context) {
super(context);
}
public CustomLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public CustomLinearLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public static class LayoutParams extends LinearLayout.LayoutParams {
private float innerPercent;
private static final int DEFAULT_WIDTH = WRAP_CONTENT;
private static final int DEFAULT_HEIGHT = WRAP_CONTENT;
public LayoutParams() {
super(DEFAULT_WIDTH, DEFAULT_HEIGHT);
innerPercent = -1.0f;
}
public LayoutParams(float innerPercent) {
super(DEFAULT_WIDTH, DEFAULT_HEIGHT);
this.innerPercent = innerPercent;
}
public LayoutParams(ViewGroup.LayoutParams p) {
super(p);
}
@TargetApi(Build.VERSION_CODES.KITKAT)
public LayoutParams(LinearLayout.LayoutParams source) {
super(source);
}
@TargetApi(Build.VERSION_CODES.KITKAT)
public LayoutParams(LayoutParams source) {
super(source);
this.innerPercent = source.innerPercent;
}
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
init(c, attrs);
}
private void init(Context context, AttributeSet attrs) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomLinearLayout_Layout);
innerPercent = a.getFloat(R.styleable.CustomLinearLayout_Layout_inner_percent, -1.0f);
a.recycle();
}
}
}
现在就可以在xml使用我们的属性了。
<?xml version="1.0" encoding="utf-8"?>
<com.egos.samples.custom_layout.CustomLinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="200dp"
android:layout_height="200dp"
android:id="@+id/test_layout"
android:background="#ffff0000"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:text="Egos"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:onClick="add"
android:background="#ff00ff00"
app:inner_percent="0.8"/>
</com.egos.samples.custom_layout.CustomLinearLayout>
只是然并软,并没有作用。
那么到底是怎么去控制Child View的大小呢?当然是在onMeasure控制的。addView会执行下面的代码。
requestLayout(); invalidate(true);
这样的话就会重新的走一遍onMeasure(),onLayout()了。实现onMeasure()的方法以后直接去处理Child View的大小,因为我继承的是LinearLayout,所以其实是会处理到measureChildBeforeLayout()。最终是在measureChildBeforeLayout的时候来处理Child View的大小。
@Override
protected void measureChildWithMargins(View child, int parentWidthMeasureSpec,
int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
// 在xml强制写成match_parent,然后在这里强制设置成
if (child != null && child.getLayoutParams() instanceof LayoutParams &&
((LayoutParams) child.getLayoutParams()).innerPercent != -1.0f) {
parentWidthMeasureSpec = MeasureSpec.makeMeasureSpec((int) (MeasureSpec.getSize(parentWidthMeasureSpec) *
((LayoutParams) child.getLayoutParams()).innerPercent), MeasureSpec.getMode(parentWidthMeasureSpec));
parentHeightMeasureSpec = MeasureSpec.makeMeasureSpec((int) (MeasureSpec.getSize(parentHeightMeasureSpec) *
((LayoutParams) child.getLayoutParams()).innerPercent), MeasureSpec.getMode(parentHeightMeasureSpec));
super.measureChildWithMargins(child, parentWidthMeasureSpec, widthUsed,
parentHeightMeasureSpec, heightUsed);
} else {
super.measureChildWithMargins(child, parentWidthMeasureSpec, widthUsed,
parentHeightMeasureSpec, heightUsed);
}
}
这样就可以实现最开始的需求了。
其实还有一些细节是需要处理的,下面的代码就是。
/**
* 当checkLayoutParams返回false的时候就会执行到这里的generateLayoutParams
*/
@Override
protected LinearLayout.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
return super.generateLayoutParams(lp);
}
/**
* 当addView的时候没有设置LayoutParams的话就会默认执行这里的generateDefaultLayoutParams
*/
@Override
protected LayoutParams generateDefaultLayoutParams() {
return new LayoutParams();
}
/**
* 写在xml中属性的时候就会执行这里的generateLayoutParams
*/
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
}
总结一下
- 自定义View和ViewGroup需要多多注意的都是onMeasure、onLayout、onDraw。
- 把ViewGroup自身的属性和Child View的属性区分开。
- 可以多多的参考support包中的代码,调试也非常的方便。
做Android开发,自身需要自定义View的地方确实是比较的多,只是大部分都会有相应的开源库。但是我们还是应该需要熟练的知道该如何自定义一个ViewGroup。
自己是一个比较健忘的人,所以写的详细点。
完整的代码戳这里。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
# 自定义viewgroup
# 安卓自定义viewgroup
# viewgroup用法
# Android ViewDragHelper完全解析 自定义ViewGroup神器
# Android控件PullRefreshViewGroup实现下拉刷新和上拉加载
# Android自定义ViewGroup(侧滑菜单)详解及简单实例
# Android自定义控件之继承ViewGroup创建新容器
# Android自定义ViewGroup实现标签流容器FlowLayout
# Android 重写ViewGroup 分析onMeasure()和onLayout()方法
# 自定义
# 就会
# 是一个
# 是在
# 就可以
# 的人
# 的是
# 都是
# 在这里
# 是怎么
# 两种
# 要去
# 也可
# 一遍
# 能有
# 该如何
# 能做
# 写在
# 就先
# 还有一些
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
Laravel如何安装使用Debugbar工具栏_Laravel性能调试与SQL监控插件【步骤】
Laravel如何生成PDF或Excel文件_Laravel文档导出工具与使用教程
今日头条AI怎样推荐抢票工具_今日头条AI抢票工具推荐算法与筛选【技巧】
JS中页面与页面之间超链接跳转中文乱码问题的解决办法
WordPress 子目录安装中正确处理脚本路径的完整指南
网站优化排名时,需要考虑哪些问题呢?
学生网站制作软件,一个12岁的学生写小说,应该去什么样的网站?
php嵌入式断网后怎么恢复_php检测网络重连并恢复硬件控制【操作】
Laravel如何处理和验证JSON类型的数据库字段
Laravel如何理解并使用服务容器(Service Container)_Laravel依赖注入与容器绑定说明
如何自定义建站之星模板颜色并下载新样式?
韩国服务器如何优化跨境访问实现高效连接?
如何在橙子建站上传落地页?操作指南详解
标准网站视频模板制作软件,现在有哪个网站的视频编辑素材最齐全的,背景音乐、音效等?
Win11怎么更改系统语言为中文_Windows11安装语言包并设为显示语言
网站制作公司哪里好做,成都网站制作公司哪家做得比较好,更正规?
Android中Textview和图片同行显示(文字超出用省略号,图片自动靠右边)
Laravel辅助函数有哪些_Laravel Helpers常用助手函数大全
如何解决hover在ie6中的兼容性问题
微信小程序 HTTPS报错整理常见问题及解决方案
如何用JavaScript实现文本编辑器_光标和选区怎么处理
Bootstrap整体框架之CSS12栅格系统
laravel怎么实现图片的压缩和裁剪_laravel图片压缩与裁剪方法
利用 Google AI 进行 YouTube 视频 SEO 描述优化
如何快速查询网址的建站时间与历史轨迹?
Java类加载基本过程详细介绍
Linux虚拟化技术教程_KVMQEMU虚拟机安装与调优
javascript中对象的定义、使用以及对象和原型链操作小结
Laravel广播系统如何实现实时通信_Laravel Reverb与WebSockets实战教程
高防服务器租用首荐平台,企业级优惠套餐快速部署
零基础网站服务器架设实战:轻量应用与域名解析配置指南
Win11怎样安装网易有道词典_Win11安装词典教程【步骤】
Laravel Livewire是什么_使用Laravel Livewire构建动态前端界面
如何在IIS7上新建站点并设置安全权限?
Laravel如何创建自定义Facades?(详细步骤)
Laravel如何实现模型的全局作用域?(Global Scope示例)
昵图网官方站入口 昵图网素材图库官网入口
百度浏览器ai对话怎么关 百度浏览器ai聊天窗口隐藏
Laravel如何实现邮箱地址验证功能_Laravel邮件验证流程与配置
如何用ChatGPT准备面试 模拟面试问答与职场话术练习教程
Python企业级消息系统教程_KafkaRabbitMQ高并发应用
edge浏览器无法安装扩展 edge浏览器插件安装失败【解决方法】
米侠浏览器网页图片不显示怎么办 米侠图片加载修复
用yum安装MySQLdb模块的步骤方法
教你用AI润色文章,让你的文字表达更专业
如何用AWS免费套餐快速搭建高效网站?
企业网站制作这些问题要关注
大学网站设计制作软件有哪些,如何将网站制作成自己app?
Python结构化数据采集_字段抽取解析【教程】
Laravel Seeder怎么填充数据_Laravel数据库填充器的使用方法与技巧

