消防网站建设的风格,项目立项流程,wordpress 换空间域名,推广团队前言
作为一名安卓开发#xff0c;也被称为大前端#xff0c;做一个美观的界面#xff0c;是我们必备的基础技能#xff0c;可能在开发中我们最常用的是系统自带的View#xff0c;因为他能满足绝大部分需求#xff0c;难一点的我们也可以上Github上找个三方库使用#…前言
作为一名安卓开发也被称为大前端做一个美观的界面是我们必备的基础技能可能在开发中我们最常用的是系统自带的View因为他能满足绝大部分需求难一点的我们也可以上Github上找个三方库使用少数情况下会让我们进行自定义View当然这不代表着我们可以不去掌握其原理因为它是通往中高级程序员的必经之路也是大厂面试的热门知识只有熟练掌握其核心原理才能让我们在后续的开发中游刃有余。
由于这是开篇文章说的有点多笔者是想借着写博客的机会把那些最不经意的基础打牢一下并且加上自己的拙见与大家分享共同进步。
自定义View简介
自定义View是Android开发中的一种常见需求它允许开发者创建复杂的用户界面组件以满足特定的设计需求。自定义View的好处在于可以完全控制View的外观和行为。常见的是 extend View 和 extend ViewGroup 以及系统自带的View。
1. onMeasure onMeasure方法用于测量View的尺寸。它的主要任务是决定View的宽度和高度。以下是一个简单的自定义View示例它在onMeasure中实现了固定大小的测量逻辑。
示例代码
public class CustomView extends View {public CustomView(Context context) {super(context);}public CustomView(Context context, AttributeSet attrs) {super(context, attrs);}Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {// 期望的宽高int desiredWidth 200;int desiredHeight 200;// 获取父View提供的宽高int width MeasureSpec.getSize(widthMeasureSpec);int height MeasureSpec.getSize(heightMeasureSpec);// 测量宽度width MeasureSpec.getMode(widthMeasureSpec) MeasureSpec.EXACTLY ?width : desiredWidth;// 测量高度height MeasureSpec.getMode(heightMeasureSpec) MeasureSpec.EXACTLY ?height : desiredHeight;// 设置测量后的宽高setMeasuredDimension(width, height);}
}2. onDraw onDraw方法用于绘制View的内容。在此方法中使用Canvas绘制图形或文字。
示例代码
Override
protected void onDraw(Canvas canvas) {super.onDraw(canvas);Paint paint new Paint(); // 创建画笔paint.setColor(Color.BLUE); // 设置颜色为蓝色// 在中心绘制一个半径为100的圆canvas.drawCircle(getWidth() / 2, getHeight() / 2, 100, paint);
}3. onTouch onTouch方法用于处理触摸事件使View能够响应用户的触摸操作。
示例代码
Override
public boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:// 处理按下事件// 可以在这里改变View的状态或外观break;case MotionEvent.ACTION_MOVE:// 处理移动事件// 例如移动View或改变某些属性break;case MotionEvent.ACTION_UP:// 处理抬起事件// 可以在这里完成某个操作比如动画结束break;}return true; // 返回true表示事件已被处理
}4. 自定义属性 通过自定义属性可以使自定义View在XML中更加灵活。如果你想写一个自定义View的三方库自定义属性是必须掌握的。
定义
在res/values/attrs.xml中添加自定义属性
declare-styleable nameCustomViewattr namecustomColor formatcolor /attr namecustomSize formatdimension /
/declare-styleable使用
在自定义View的构造函数中读取这些属性
public CustomView(Context context, AttributeSet attrs) {super(context, attrs);TypedArray a context.getTheme().obtainStyledAttributes(attrs,R.styleable.CustomView,0, 0);try {// 读取自定义颜色属性默认为黑色int customColor a.getColor(R.styleable.CustomView_customColor, Color.BLACK);// 读取自定义尺寸属性默认为50dpfloat customSize a.getDimension(R.styleable.CustomView_customSize, 50);// 使用customColor和customSize进行后续逻辑} finally {a.recycle(); // 释放TypedArray资源!!!}
}5. 测量模式 在Android中自定义View的测量过程由三种测量模式决定EXACTLY、AT_MOST和UNSPECIFIED。
三种布局模式
在 Android 布局中父 View 可以通过不同的模式给子 View 传递尺寸限制常见的有以下三种模式
1. EXACTLY确切模式
定义父 View 给子 View 传递了一个确切的尺寸子 View 应该使用这个尺寸。适用场景一般用于设置为 match_parent 或具体的尺寸值时。 例如设置子 View 的宽度为父 View 的 100dp子 View 必须遵循这一具体尺寸。 2. AT_MOST最多模式
定义父 View 给子 View 传递了一个最大尺寸子 View 可以选择小于或等于这个尺寸。适用场景一般用于设置为 wrap_content子 View 根据内容大小自适应但不能超过父 View 的最大限制。 例如当子 View 选择包裹内容时它会根据内容大小自适应但不能超过父 View 给定的最大限制。 3. UNSPECIFIED未指定模式
定义父 View 没有给子 View 限制尺寸子 View 可以根据自身需求决定尺寸。适用场景一般用于需要自由尺寸的情况例如 ListView 或 ScrollView 中的子 View。 这种模式一般在自定义控件或特定场景下使用较少应用于常规布局。 源码解析
在自定义View的onMeasure方法中我们可以通过MeasureSpec类来解析这三种模式。MeasureSpec包含两个主要信息Mode模式 和 Size大小
MeasureSpec的源代码
public static final int UNSPECIFIED 0;
public static final int EXACTLY 1;
public static final int AT_MOST 2;private static final int MODE_SHIFT 30;
private static final int MODE_MASK 0x3 MODE_SHIFT;//使用掩码mask来提取高2位。
public static int getMode(int measureSpec) {return (measureSpec MODE_MASK); // MODE_MASK 0x3
}//通过掩码去除高2位获取低30位的值。
public static int getSize(int measureSpec) {return (measureSpec ~MODE_MASK); // ~MODE_MASK 0xFFFFFFFC
}测量模式存储形式
MeasureSpec是一个32位的整数(int 值 4个字节32bit)其中包含模式和大小信息。
高位位31到位30用于存储模式。低位位29到位0用于存储大小。
二进制表示
EXACTLY0b01000000000000000000000000000000只关心高两位
public static final int EXACTLY 1 MODE_SHIFT;AT_MOST0b10000000000000000000000000000000
public static final int AT_MOST 2 MODE_SHIFT;UNSPECIFIED0b00000000000000000000000000000000
public static final int UNSPECIFIED 0 MODE_SHIFT;各模式示例代码
EXACTLY确切模式
Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {// 获取父View提供的确切宽高int width MeasureSpec.getSize(widthMeasureSpec);int height MeasureSpec.getSize(heightMeasureSpec);// 使用父View提供的尺寸setMeasuredDimension(width, height);
}应用场景当父布局设置为match_parent时子View的宽高将完全匹配父布局。
AT_MOST最多模式
Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int widthMode MeasureSpec.getMode(widthMeasureSpec);int widthSize MeasureSpec.getSize(widthMeasureSpec);int heightMode MeasureSpec.getMode(heightMeasureSpec);int heightSize MeasureSpec.getSize(heightMeasureSpec);int width;int height;// 处理宽度if (widthMode MeasureSpec.AT_MOST) {// 计算子View的宽度最大不超过widthSizewidth Math.min(desiredWidth, widthSize);} else {width desiredWidth; // 使用期望宽度}// 处理高度if (heightMode MeasureSpec.AT_MOST) {// 计算子View的高度最大不超过heightSizeheight Math.min(desiredHeight, heightSize);} else {height desiredHeight; // 使用期望高度}setMeasuredDimension(width, height);
}应用场景父布局使用wrap_content子View可以根据内容自适应但不会超过父布局的最大值。
UNSPECIFIED未指定模式
Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {// 在此模式下子View可以自由选择尺寸setMeasuredDimension(desiredWidth, desiredHeight);
}应用场景适用于需要灵活大小的场景例如在ScrollView中子View的尺寸可以根据内容进行扩展。
6. 安卓自定义View刷新调用顺序图 ┌────────────────────┐│ 调用invalidate() │└────────────────────┘↓┌────────────────────────────┐│ 调用View.invalidate() │└────────────────────────────┘↓┌──────────────────────────────┐│ 调用ViewParent.invalidate() │ (若有父视图向上请求刷新)└──────────────────────────────┘↓┌──────────────────────────────┐│ 调用请求重绘机制UI线程刷新 │└──────────────────────────────┘↓┌────────────────────────┐│ 调用requestLayout() │ (如果布局变化调用此方法会触发onMeasure)└────────────────────────┘↓┌────────────────────────────┐│ 调用onMeasure() │└────────────────────────────┘↓┌────────────────────────────┐│ 调用setMeasuredDimension │ (设置最终宽高)└────────────────────────────┘↓┌────────────────────────┐│ 调用onLayout() │ (进行视图的布局)└────────────────────────┘↓┌──────────────────────────────┐│ 调用onDraw() │ (进行绘制操作)└──────────────────────────────┘↓┌──────────────────────────────┐│ 更新显示重新渲染视图 │└──────────────────────────────┘
简单来说就是三步走onMeasureonLayoutonDraw其中onLayout一般情况下普通视图不需要重写此方法除非视图具有子视图并需要自己进行布局。比如你如果想自定义一个某东搜索框下面的历史搜索记录布局的时候就必须重写onLayout了。
总结
方法作用何时重写onMeasure()测量视图的大小当视图的尺寸依赖于父视图的MeasureSpec或动态计算时onLayout()布局子视图的位置当视图是布局容器或需要动态布局子视图时onDraw()绘制视图的内容当需要自定义视图内容的绘制时几乎所有自定义视图都需要重写
7. 最后 基础只是理论概念要想牢记还得在实战中运用当然上面都会了就能和面试官吹牛逼了。再会
另外给喜欢记笔记的同学安利一款好用的云笔记软件对比大部分国内的这个算还不错的免费好用wolai _ 蓝 橙