当前位置: 首页 > news >正文

[Android]自定义view - 详解

目录

为什么需要自定义view?

View的绘制流程

自定义View

OnMeasure

代码部分

三种测量模式

1. EXACTLY

2. AT_MOST

3. UNSPECIFIED

重写OnDraw方法

自定义属性


为什么需要自定义view?

Android 提供了大量现成控件,但当你需要实现独特的视觉表现、复杂的动画或者当前现成的控件已经没有办法去满足你的需求时,这时可以自定义view来完成我们所期望的需求;

View的绘制流程

  1. onMwasure:测量View宽高

  2. onLayout: 计算View的位置并布局

  3. onDraw: 绘制View

自定义View

这里有两个我们必须重写的方法:

public xxview(Context context) {
super(context);
}
public xxview(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}

第二个方法中有一个参数:AttributeSet

  • 你在布局文件里写 <com.example.xxView ... /> 并设置属性时,系统会把这些属性打包成一个 AttributeSet 对象传给构造函数;

  • 我们可以通过它,拿到在 XML 中声明的属性,从而在代码里初始化 View 的样式;

总结来说,就是拿到XML文件中的属性并且设立初始值;

OnMeasure

前文提到:这个方法用来测量自定义view的宽高尺寸;

那么为什么需要测量宽高尺寸呢?不是应该都在XML文件中定义过了么?

但其实,我们在XML文件中有三种定义,分别是wrap_content,match_parent还有精确值;前两者并没有指定具体的大小,所以这个时候需要我们手动去处理和设置尺寸;

当然,在View类中提供的onMeasure方法中,也有设置,但是如果你在XML文件中写的是wrap_content,那么最后显示的效果是充满父布局的,为什么会这样?稍后会有解答...

重写OnMeasure方法:

代码部分

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int wigth = xxonmeasure(100, widthMeasureSpec);
int height = xxonmeasure(200, heightMeasureSpec);
setMeasuredDimension(wigth,height);
}
protected int xxonmeasure(int defaultsize,int measureSpec) {
int size = defaultsize;
int mode = MeasureSpec.getMode(measureSpec);
int sizes= MeasureSpec.getSize(measureSpec);
switch (mode){
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY: {
size = sizes;
break;
}
case  MeasureSpec.UNSPECIFIED:{
size= defaultsize;
break;
}
}
return size;
}

在了解这些之前,我们先来看看测量模式:

三种测量模式

1. EXACTLY

  1. 含义:父容器已经指定了 确切的大小;

  2. 什么情况下:子 View 的 layout_width / layout_height 是具体值或者是 match_parent

  3. 那为什么match_parent是精确模式呢?因为父容器的大小已经确定,所以自然当前的情况也已经确定了;因为尺寸大小的确定是自上而下的;

2. AT_MOST

  1. 含义:父容器指定了一个 最大的值;

  2. 什么情况下:子 View 的 layout_width / layout_height 是 wrap_content

  3. 为什么?因为当前view设定的是包含当前内容即可,没有办法确定具体的值,我们只能获取到父容器的值,知道最大限制,所以此时我们的模式就是知道它的最大值;

3. UNSPECIFIED

  1. 含义:父容器对 View 的大小没有限制;

  2. 什么情况下:View 自己决定大小,比如ScrollView 的子元素高度可能是无限制的,能撑多大就多大;

  3. 这种情况比较少见;

在onMeasure方法中,有两个我们不太熟悉的参数: int widthMeasureSpec, int heightMeasureSpec

widthMeasureSpe包含是测量模式和测量的大小,其实准确来说是父view提供的参考大小,最终的大小我们需要自己处理的;

那么一个Int型的数据为什么能包含测量模式和大小呢?一个Int占用32个bit,三种测量模式只需要两个bit,所以前两个bit存的是测量模式,后30存的是测量的大小;我们可以直接用封装好的方法去获取:

  1. 获取模式

int mode = MeasureSpec.getMode(measureSpec);

  1. 获取大小 int sizes= MeasureSpec.getSize(measureSpec);

setMeasuredDimension(wigth,height);这行代码是设置确定好最终的尺寸;

现在重新阅读代码,去理解写的逻辑;

switch (mode){
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY: {
size = sizes;
break;
}
case  MeasureSpec.UNSPECIFIED:{
size= defaultsize;
break;
}
}

这里如果模式是MeasureSpec.AT_MOST,那么 size = sizes;就是说最终的大小是父容器的大小,当前你可以根据你自己想要的效果去设置不同的值;

重写OnDraw方法

用 canvas画板,我们还可以绘制其他的图形,颜色等,这里绘制了圆形;

@Override
protected void onDraw(@NonNull Canvas canvas) {
super.onDraw(canvas);
int r = getMeasuredWidth() / 2;
int X = getLeft() + r;
int Y = getTop() + r;
Paint paint = new Paint();
paint.setColor(Color.GREEN);
canvas.drawCircle(X, Y, r, paint);
}

canvas.drawCircle(X, Y, r, paint); 前两个参数是圆的横纵坐标,第三个参数是半径,第四个参数是对颜色等外观方面的设置

自定义属性

在valus下:申明我们的属性,format是说明类型,比如这里的类型就是尺寸类型比如sp,dp;

然后在XML文件里面去定义值;

在构造方法里去设置默认值;

public xxview(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.xxview);
int  defalutSize = a.getDimensionPixelSize(R.styleable.xxview_defaultsize, 100);
a.recycle();
}

TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.xxview);:用来从 XML 中解析你自定义的属性集合 xxview;

a.getDimensionPixelSize(R.styleable.xxview_defaultsize, 100);这里获取的就是 XML 里 <xxview app:defaultsize="150dp" /> 的值,如果 XML 没有写这个属性,就用 第二个参数 100 当默认值;

a.recycle();回收这个对象;

本次分享到此结束;

http://www.hskmm.com/?act=detail&tid=14592

相关文章:

  • 不定高元素动画实现方案(下)
  • 详细介绍:C 语言:第 20 天笔记:typedef(类型重命名规则、应用场景与实战案例)
  • Screaming Architecture:让架构自己说话
  • BOE(京东方)携手UNESCO联合主办WCBR“科学十年”分会 彰显中国科技企业可持续发展实力
  • 使用Cyclops.PdfKit根据pdf模板生成pdf文件
  • 一款文本编辑器的介绍
  • 随笔-决战保研篇
  • 科研人必知:293F与HEK293细胞在蛋白表达中的不同“超能力”
  • Redis Cluster
  • 如何使用C语言实现Vigenre密码加解密
  • 【F#学习】列表 List
  • Trae与Gitee MCP深度集成:AI编程工具链迎来重大升级
  • 【2025-09-22】加班感悟
  • OpenAI Codex 使用 智谱 API
  • 嵌入式ARM架构学习9——IIC - 教程
  • Day04---数据类型及面试题详解
  • 记-一次H3C交换机版本升级
  • 客服系统中的定时任务设计与实现
  • 使用Go语言实现高效定时任务功能
  • JavaScript获取NHK的附件文件
  • 承兑 背书 贴现区别
  • 洛谷题单指南-进阶数论-P3811 【模板】模意义下的乘法逆元
  • Interlocked.Increment学习
  • 基于解析法的四轴SCARA机器人正逆运动学代码
  • .Net-IIS 文件上传安全漏洞问题
  • 【F#学习】记录 Record
  • 【光照】[高光反射specular]以UnityURP为例
  • 游戏性能优化与逆向分析技术
  • 使用 feign 调用时对微服务实例进行选择
  • EI目录今年第3次更新!55本中国期刊被收录,附完整版下载