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

Android学习总结之自定义view设计模式理解

面试题 1:请举例说明自定义 View 中模板方法模式的应用

考点分析

此问题主要考查对模板方法模式的理解,以及该模式在 Android 自定义 View 生命周期方法里的实际运用。

回答内容

模板方法模式定义了一个操作的算法骨架,把一些步骤的实现延迟到子类。在 Android 自定义 View 中,View 类提供了一系列生命周期方法,像 onMeasure()onLayout()onDraw() 等,这些构成了绘制 View 的算法骨架,开发者可重写这些方法实现特定逻辑。

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.View;// 自定义圆形 View 类,继承自 View
public class CustomCircleView extends View {// 用于绘制的画笔对象private Paint paint;// 构造函数,接收上下文参数public CustomCircleView(Context context) {super(context);// 初始化画笔init();}// 初始化画笔的方法private void init() {// 创建一个新的画笔对象paint = new Paint();// 设置画笔颜色为蓝色paint.setColor(Color.BLUE);// 设置画笔样式为填充paint.setStyle(Paint.Style.FILL);}// 重写 onMeasure 方法,用于测量 View 的大小@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {// 期望的大小,可根据实际情况调整int desiredSize = 200;// 获取宽度的测量模式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.EXACTLY) {// 如果是精确模式,使用测量大小width = widthSize;} else if (widthMode == MeasureSpec.AT_MOST) {// 如果是最大模式,取期望大小和测量大小的最小值width = Math.min(desiredSize, widthSize);} else {// 如果是未指定模式,使用期望大小width = desiredSize;}// 根据高度的测量模式确定最终高度if (heightMode == MeasureSpec.EXACTLY) {// 如果是精确模式,使用测量大小height = heightSize;} else if (heightMode == MeasureSpec.AT_MOST) {// 如果是最大模式,取期望大小和测量大小的最小值height = Math.min(desiredSize, heightSize);} else {// 如果是未指定模式,使用期望大小height = desiredSize;}// 设置测量好的宽度和高度setMeasuredDimension(width, height);}// 重写 onDraw 方法,用于绘制 View 的内容@Overrideprotected void onDraw(Canvas canvas) {// 获取 View 宽度的一半,作为圆心的 x 坐标int centerX = getWidth() / 2;// 获取 View 高度的一半,作为圆心的 y 坐标int centerY = getHeight() / 2;// 取圆心 x 和 y 坐标的最小值作为半径int radius = Math.min(centerX, centerY);// 使用画笔在画布上绘制圆形canvas.drawCircle(centerX, centerY, radius, paint);}
}

从源码层面来看,View 类中的 onMeasure()onLayout()onDraw() 方法本身有默认实现,但这些实现可能不符合特定需求。例如,View 类的 onMeasure() 方法默认只是简单处理,没有考虑复杂的测量逻辑。自定义 View 时,重写这些方法就如同在模板方法模式中,子类根据自身需求实现父类定义的抽象步骤。CustomCircleView 类重写 onMeasure() 方法确定 View 的大小,重写 onDraw() 方法绘制圆形,父类控制算法结构,子类实现具体步骤,体现了模板方法模式。

面试题 2:在自定义 View 中如何运用策略模式实现不同的绘制效果

考点分析

该问题考查对策略模式的掌握,以及如何在自定义 View 中灵活切换不同的绘制策略。

回答内容

策略模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换。在自定义 View 中,可根据不同情况使用不同的绘制策略。

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.view.View;// 绘制策略接口,定义了绘制的抽象方法
interface DrawingStrategy {// 在画布上进行绘制的方法,接收画布、画笔、宽度和高度作为参数void draw(Canvas canvas, Paint paint, int width, int height);
}// 矩形绘制策略类,实现了 DrawingStrategy 接口
class RectangleDrawingStrategy implements DrawingStrategy {// 实现绘制矩形的逻辑@Overridepublic void draw(Canvas canvas, Paint paint, int width, int height) {// 在画布上绘制矩形canvas.drawRect(0, 0, width, height, paint);}
}// 圆形绘制策略类,实现了 DrawingStrategy 接口
class CircleDrawingStrategy implements DrawingStrategy {// 实现绘制圆形的逻辑@Overridepublic void draw(Canvas canvas, Paint paint, int width, int height) {// 计算圆心的 x 坐标int centerX = width / 2;// 计算圆心的 y 坐标int centerY = height / 2;// 取圆心 x 和 y 坐标的最小值作为半径int radius = Math.min(centerX, centerY);// 在画布上绘制圆形canvas.drawCircle(centerX, centerY, radius, paint);}
}// 自定义形状 View 类,继承自 View
public class CustomShapeView extends View {// 当前使用的绘制策略private DrawingStrategy drawingStrategy;// 用于绘制的画笔对象private Paint paint;// 构造函数,接收上下文参数public CustomShapeView(Context context) {super(context);// 创建一个新的画笔对象paint = new Paint();// 设置画笔颜色为红色paint.setColor(Color.RED);// 设置画笔样式为填充paint.setStyle(Paint.Style.FILL);// 默认使用矩形绘制策略drawingStrategy = new RectangleDrawingStrategy();}// 设置绘制策略的方法public void setDrawingStrategy(DrawingStrategy drawingStrategy) {// 更新当前使用的绘制策略this.drawingStrategy = drawingStrategy;// 通知 View 重绘invalidate();}// 重写 onDraw 方法,用于绘制 View 的内容@Overrideprotected void onDraw(Canvas canvas) {// 获取 View 的宽度int width = getWidth();// 获取 View 的高度int height = getHeight();// 如果绘制策略不为空if (drawingStrategy != null) {// 调用当前绘制策略的 draw 方法进行绘制drawingStrategy.draw(canvas, paint, width, height);}}
}

从源码层面看,策略模式将不同的绘制算法封装在不同的策略类中,如 RectangleDrawingStrategy 和 CircleDrawingStrategyCustomShapeView 类通过持有 DrawingStrategy 接口的引用,实现了绘制策略的切换。当调用 setDrawingStrategy() 方法时,只需传入不同的策略对象,就可以改变绘制行为,而不需要修改 CustomShapeView 类的核心逻辑。这种设计使得代码的可维护性和扩展性得到了提高。

面试题 3:简述观察者模式在自定义 View 中的应用场景及实现方式

考点分析

此问题考查对观察者模式的理解,以及如何在自定义 View 中实现状态监听和通知机制。

回答内容

观察者模式定义了对象之间的一对多依赖关系,当一个对象状态改变时,所有依赖它的对象都会得到通知并更新。在自定义 View 中,可用于监听 View 的状态变化。

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.view.View;import java.util.ArrayList;
import java.util.List;// 进度改变监听器接口,定义了进度改变时的回调方法
interface ProgressChangeListener {// 当进度改变时调用的方法,接收新的进度值作为参数void onProgressChanged(int progress);
}// 自定义进度条 View 类,继承自 View
public class CustomProgressBar extends View {// 当前的进度值private int progress;// 存储进度改变监听器的列表private List<ProgressChangeListener> listeners;// 用于绘制的画笔对象private Paint paint;// 构造函数,接收上下文参数public CustomProgressBar(Context context) {super(context);// 初始化进度为 0progress = 0;// 创建一个新的监听器列表listeners = new ArrayList<>();// 创建一个新的画笔对象paint = new Paint();// 设置画笔颜色为绿色paint.setColor(Color.GREEN);// 设置画笔样式为填充paint.setStyle(Paint.Style.FILL);}// 添加进度改变监听器的方法public void addProgressChangeListener(ProgressChangeListener listener) {// 将监听器添加到列表中listeners.add(listener);}// 移除进度改变监听器的方法public void removeProgressChangeListener(ProgressChangeListener listener) {// 从列表中移除指定的监听器listeners.remove(listener);}// 设置进度的方法public void setProgress(int progress) {// 更新当前的进度值this.progress = progress;// 通知所有监听器进度已改变notifyListeners();// 通知 View 重绘invalidate();}// 通知所有监听器进度已改变的方法private void notifyListeners() {// 遍历监听器列表for (ProgressChangeListener listener : listeners) {// 调用每个监听器的 onProgressChanged 方法listener.onProgressChanged(progress);}}// 重写 onDraw 方法,用于绘制进度条@Overrideprotected void onDraw(Canvas canvas) {// 获取 View 的宽度int width = getWidth();// 获取 View 的高度int height = getHeight();// 根据当前进度计算进度条的宽度int progressWidth = (int) (width * ((float) progress / 100));// 在画布上绘制进度条canvas.drawRect(0, 0, progressWidth, height, paint);}
}

从源码层面来看,CustomProgressBar 类维护了一个 ProgressChangeListener 列表,当进度发生变化时,调用 notifyListeners() 方法遍历列表,通知所有监听器进度已改变。这类似于 Android 系统中 LiveData 的实现机制,LiveData 也是通过维护一个观察者列表,当数据发生变化时通知所有观察者。在自定义 View 中使用观察者模式,可以实现 View 状态变化的监听和响应,提高代码的可维护性和扩展性。

面试题 4:请说明组合模式在自定义 ViewGroup 中的体现

考点分析

该问题考查对组合模式的认识,以及如何在自定义 ViewGroup 中构建 “部分 - 整体” 的层次结构。

回答内容

组合模式将对象组合成树形结构以表示 “部分 - 整体” 的层次结构,用户对单个对象和组合对象的使用具有一致性。在 Android 中,ViewGroup 是组合模式的典型应用。

import android.content.Context;
import android.view.View;
import android.view.ViewGroup;// 自定义线性布局 ViewGroup 类,继承自 ViewGroup
public class CustomLinearLayout extends ViewGroup {// 构造函数,接收上下文参数public CustomLinearLayout(Context context) {super(context);}// 重写 onLayout 方法,用于布局子 View@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {// 获取子 View 的数量int childCount = getChildCount();// 当前子 View 的顶部位置int currentTop = 0;// 遍历所有子 Viewfor (int i = 0; i < childCount; i++) {// 获取当前子 ViewView child = getChildAt(i);// 获取子 View 的测量宽度int childWidth = child.getMeasuredWidth();// 获取子 View 的测量高度int childHeight = child.getMeasuredHeight();// 布局子 View 的位置child.layout(0, currentTop, childWidth, currentTop + childHeight);// 更新当前顶部位置currentTop += childHeight;}}// 重写 onMeasure 方法,用于测量子 View 和自身的大小@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {// 获取子 View 的数量int childCount = getChildCount();// 子 View 的总高度int totalHeight = 0;// 子 View 的最大宽度int maxWidth = 0;// 遍历所有子 Viewfor (int i = 0; i < childCount; i++) {// 获取当前子 ViewView child = getChildAt(i);// 测量子 View 的大小measureChild(child, widthMeasureSpec, heightMeasureSpec);// 累加子 View 的高度totalHeight += child.getMeasuredHeight();// 更新最大宽度maxWidth = Math.max(maxWidth, child.getMeasuredWidth());}// 设置自身的测量宽度和高度setMeasuredDimension(maxWidth, totalHeight);}
}

从源码层面看,ViewGroup 类本身就体现了组合模式的思想。ViewGroup 可以包含多个子 View 或 ViewGroup,形成一个树形结构。CustomLinearLayout 继承自 ViewGroup,重写 onMeasure() 方法测量子 View 的大小并确定自身大小,重写 onLayout() 方法布局子 View 的位置。用户可以像操作单个 View 一样操作 CustomLinearLayout,而不需要关心其内部子 View 的具体实现。这种设计使得代码的结构更加清晰,易于维护和扩展。

相关文章:

  • 尼日利亚slot游戏出海赛道借助本土网盟cpi流量广告投放优势
  • 企业数据合规实战:用API+AI构建备案核验系统
  • Python爬虫(11)Python数据存储实战:深入解析NoSQL数据库的核心应用与实战
  • WPF性能优化举例
  • python+echart绘制一个听力图
  • 第六章 QT基础:9、Qt中数据库的操作
  • 【Dify系列教程重置精品版】第四章:实现Dify的 hello world
  • Learning vtkjs之ImageCropFilter
  • C++负载均衡远程调用学习之自定义内存池管理
  • 突破SQL注入字符转义的实战指南:绕过技巧与防御策略
  • RSYSLOG收集深信服log
  • 20250430在ubuntu14.04.6系统上查看系统实时网速
  • 耘想WinNAS:企业级NAS解决方案的终极选择
  • JavaScript 代码搜索框
  • 自动驾驶-一位从业两年的独特视角
  • LeetCode —— 572. 另一棵树的子树
  • VS Code 插件Git History Diff 使用
  • 数学建模论文手的学习日常01
  • 数据接收全流程图(物理网卡 → 应用层)
  • 90.如何将Maui应用安装到手机(最简) C#例子 Maui例子
  • “网约摩托”在部分县城上线:起步价五六元,专家建议纳入监管
  • 国务院任免国家工作人员:颜清辉任人社部副部长
  • “80后”蒋美华任辽宁阜新市副市长
  • 中信银行一季度净利195.09亿增1.66%,不良率持平
  • 首映|“凤凰传奇”曾毅:拍电影,我是认真的
  • 一季度我国服务进出口总额19741.8亿元,同比增长8.7%