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 的具体实现。这种设计使得代码的结构更加清晰,易于维护和扩展。

相关推荐
海尔辛2 小时前
学习黑客了解密码学
学习·密码学
看到我,请让我去学习2 小时前
LInux—shell编程
android·linux·chrome
ShallowLin3 小时前
HarmonyOS学习——UIAbility组件(下)
学习
.小墨迹3 小时前
Python学习——执行python时,键盘按下ctrl+c,退出程序
linux·开发语言·python·学习·自动驾驶
蓝婷儿3 小时前
6个月Python学习计划 Day 1
开发语言·python·学习
@codercjw5 小时前
齿轮,链轮,同步轮,丝杆传动sw画法
学习
widder_5 小时前
软考中级软件设计师——设计模式篇
单例模式·设计模式
沐雨风栉6 小时前
Ubuntu+Docker+内网穿透:保姆级教程实现安卓开发环境远程部署
android·ubuntu·docker
珹洺6 小时前
计算机操作系统(十一)调度器/调度程序,闲逛调度与调度算法的评价指标
android·java·算法
ARM2NCWU7 小时前
安卓原生兼容服务器
android·运维·服务器