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

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

考点分析

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

回答内容

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

java 复制代码
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 的大小
    @Override
    protected 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 的内容
    @Override
    protected 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 中,可根据不同情况使用不同的绘制策略。

java 复制代码
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 {
    // 实现绘制矩形的逻辑
    @Override
    public void draw(Canvas canvas, Paint paint, int width, int height) {
        // 在画布上绘制矩形
        canvas.drawRect(0, 0, width, height, paint);
    }
}

// 圆形绘制策略类,实现了 DrawingStrategy 接口
class CircleDrawingStrategy implements DrawingStrategy {
    // 实现绘制圆形的逻辑
    @Override
    public 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 的内容
    @Override
    protected void onDraw(Canvas canvas) {
        // 获取 View 的宽度
        int width = getWidth();
        // 获取 View 的高度
        int height = getHeight();
        // 如果绘制策略不为空
        if (drawingStrategy != null) {
            // 调用当前绘制策略的 draw 方法进行绘制
            drawingStrategy.draw(canvas, paint, width, height);
        }
    }
}

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

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

考点分析

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

回答内容

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

java 复制代码
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);
        // 初始化进度为 0
        progress = 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 方法,用于绘制进度条
    @Override
    protected 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 是组合模式的典型应用。

java 复制代码
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
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        // 获取子 View 的数量
        int childCount = getChildCount();
        // 当前子 View 的顶部位置
        int currentTop = 0;
        // 遍历所有子 View
        for (int i = 0; i < childCount; i++) {
            // 获取当前子 View
            View 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 和自身的大小
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // 获取子 View 的数量
        int childCount = getChildCount();
        // 子 View 的总高度
        int totalHeight = 0;
        // 子 View 的最大宽度
        int maxWidth = 0;

        // 遍历所有子 View
        for (int i = 0; i < childCount; i++) {
            // 获取当前子 View
            View child = getChildAt(i);
            // 测量子 View 的大小
            measureChild(child, widthMeasureSpec, heightMeasureSpec);
            // 累加子 View 的高度
            totalHeight += child.getMeasuredHeight();
            // 更新最大宽度
            maxWidth = Math.max(maxWidth, child.getMeasuredWidth());
        }

        // 设置自身的测量宽度和高度
        setMeasuredDimension(maxWidth, totalHeight);
    }
}

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

相关推荐
电子艾号哲1 小时前
STM32单片机入门学习——第49节: [15-2] 读写内部FLASH&读取芯片ID
stm32·单片机·学习
hnlucky1 小时前
redis 数据类型新手练习系列——List类型
运维·数据库·redis·学习·bootstrap·list
橙子199110161 小时前
请简述一下什么是 Kotlin?它有哪些特性?
android·开发语言·kotlin
虾球xz2 小时前
游戏引擎学习第250天:# 清理DEBUG GUID
c++·学习·游戏引擎
kukuwawu2 小时前
基因组注释笔记——GeneMark-ES/ET的使用
经验分享·笔记·学习·bash·基因注释
贫道绝缘子2 小时前
【Android】四大组件之Service
android
ghost1432 小时前
C#学习第20天:垃圾回收
开发语言·学习·c#
weixin_472339462 小时前
Android Studio下载安装教程
android·ide·android studio
tangweiguo030519873 小时前
Android Kotlin 依赖注入全解:Koin appModule 配置与多 ViewModel 数据共享实战指南
android·kotlin
西楚曹长卿3 小时前
RN 获取视频封面,获取视频第一帧
android·react native·音视频·react