Android的第一次面试(Java篇)

server/2025/3/17 0:28:38/

   在 Android 开发中,View 是用户界面的基础组件,理解 View 的绘制原理以及如何自定义 View 是实现独特界面效果的关键。本文将深入探讨 Android View 的绘制流程、自定义 View 的工作原理,并通过具体的代码示例来展示如何实现自定义 View。

Android View 的绘制流程

整体流程概述

Android View 的绘制主要分为三个步骤:测量(measure)、布局(layout)和绘制(draw),这三个步骤依次执行,完成 View 在屏幕上的显示。

 测量(measure)

测量过程决定了 View 的大小。系统会调用measure(int widthMeasureSpec, int heightMeasureSpec)方法,其中widthMeasureSpecheightMeasureSpec是测量规格,包含了测量模式和大小信息。测量模式有三种:

  • EXACTLY:精确值模式,父容器已经为子 View 指定了确切的大小。
  • AT_MOST:最大值模式,子 View 的大小不能超过父容器指定的大小。
  • UNSPECIFIED:未指定模式,父容器没有对 View 的大小做限制。
布局(layout)

布局过程决定了 View 在父容器中的位置。系统会调用layout(int left, int top, int right, int bottom)方法,其中lefttoprightbottom分别表示 View 相对于父容器的左、上、右、下边界的坐标。

 绘制(draw)

绘制过程将 View 绘制到屏幕上。系统会调用draw(Canvas canvas)方法,通过Canvas对象进行绘制操作,如绘制图形、文本等。

具体代码示例

以下是一个简单的自定义 View,展示了测量、布局和绘制的基本实现:

java">import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;public class CustomView extends View {private Paint mPaint;public CustomView(Context context) {super(context);init();}public CustomView(Context context, AttributeSet attrs) {super(context, attrs);init();}public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}private void init() {mPaint = new Paint();mPaint.setColor(Color.RED);mPaint.setStyle(Paint.Style.FILL);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int width = MeasureSpec.getSize(widthMeasureSpec);int height = MeasureSpec.getSize(heightMeasureSpec);setMeasuredDimension(width, height);}@Overrideprotected void onLayout(boolean changed, int left, int top, int right, int bottom) {super.onLayout(changed, left, top, right, bottom);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);int centerX = getWidth() / 2;int centerY = getHeight() / 2;int radius = Math.min(centerX, centerY);canvas.drawCircle(centerX, centerY, radius, mPaint);}
}

在上述代码中:

  • init()方法用于初始化画笔。
  • onMeasure()方法根据测量规格确定 View 的大小。
  • onLayout()方法调用父类的onLayout()方法,完成布局操作。
  • onDraw()方法在画布上绘制一个红色的圆形。

自定义 View 的工作原理

 继承 View 类

自定义 View 通常需要继承自View类或其子类,如TextViewImageView等。通过重写onMeasure()onLayout()onDraw()方法,可以实现自定义的测量、布局和绘制逻辑。

处理属性

可以通过自定义属性来为自定义 View 添加额外的配置选项。具体步骤如下:

  1. res/values目录下创建attrs.xml文件,定义自定义属性:
<resources><declare-styleable name="CustomView"><attr name="circleColor" format="color" /></declare-styleable>
</resources>

在自定义 View 的构造方法中获取属性值:

java">public CustomView(Context context, AttributeSet attrs) {super(context, attrs);TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomView);int circleColor = typedArray.getColor(R.styleable.CustomView_circleColor, Color.RED);typedArray.recycle();mPaint = new Paint();mPaint.setColor(circleColor);mPaint.setStyle(Paint.Style.FILL);
}

在布局文件中使用自定义属性:

<com.example.customview.CustomViewandroid:layout_width="200dp"android:layout_height="200dp"app:circleColor="@color/blue" />

事件处理

自定义 View 还可以处理用户的触摸事件,通过重写onTouchEvent(MotionEvent event)方法来实现

java">@Override
public boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:// 处理按下事件break;case MotionEvent.ACTION_MOVE:// 处理移动事件break;case MotionEvent.ACTION_UP:// 处理抬起事件break;}return true;
}

实战扩展:

  如果我要一个可以滑动的波浪形进度条怎么实现?

  实现一个可以滑动的波浪形进度条,你可以通过自定义 View 来完成。 

实现思路

  1. 绘制波浪:使用正弦函数来生成波浪的形状,并使用 Canvas 进行绘制。
  2. 实现滑动:通过监听触摸事件,根据触摸位置的变化来更新进度条的进度。
  3. 更新波浪:根据进度条的进度,动态更新波浪的位置和高度。

 示例代码:

java">import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;public class WaveProgressBar extends View {private Paint mWavePaint;private Path mWavePath;private float mProgress = 0; // 进度值,范围 0 - 1private float mWaveOffset = 0; // 波浪的偏移量private float mWaveHeight = 20; // 波浪的高度private float mWaveLength = 200; // 波浪的波长public WaveProgressBar(Context context) {super(context);init();}public WaveProgressBar(Context context, AttributeSet attrs) {super(context, attrs);init();}public WaveProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}private void init() {mWavePaint = new Paint();mWavePaint.setColor(Color.BLUE);mWavePaint.setStyle(Paint.Style.FILL);mWavePath = new Path();}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);mWavePath.reset();int width = getWidth();int height = getHeight();float progressHeight = height * mProgress;// 绘制波浪mWavePath.moveTo(0, height);for (float x = 0; x <= width; x++) {float y = (float) (progressHeight + mWaveHeight * Math.sin((x + mWaveOffset) * 2 * Math.PI / mWaveLength));mWavePath.lineTo(x, y);}mWavePath.lineTo(width, height);mWavePath.close();canvas.drawPath(mWavePath, mWavePaint);// 更新波浪偏移量,实现波浪滚动效果mWaveOffset += 5;if (mWaveOffset > mWaveLength) {mWaveOffset = 0;}invalidate(); // 触发重绘}@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:case MotionEvent.ACTION_MOVE:float x = event.getX();mProgress = Math.max(0, Math.min(1, x / getWidth()));invalidate();return true;case MotionEvent.ACTION_UP:return true;}return super.onTouchEvent(event);}public void setProgress(float progress) {this.mProgress = Math.max(0, Math.min(1, progress));invalidate();}public float getProgress() {return mProgress;}
}

使用方法

在布局文件中使用自定义的 WaveProgressBar

<com.example.yourpackage.WaveProgressBarandroid:layout_width="match_parent"android:layout_height="200dp" />

代码解释

  1. 初始化:在 init() 方法中初始化画笔和路径。
  2. 绘制波浪:在 onDraw() 方法中,使用正弦函数生成波浪的形状,并使用 Path 记录波浪的路径,最后使用 Canvas 绘制路径。
  3. 更新波浪偏移量:在 onDraw() 方法中,每次绘制时更新波浪的偏移量,并调用 invalidate() 方法触发重绘,实现波浪滚动的效果。
  4. 处理触摸事件:在 onTouchEvent() 方法中,监听触摸事件,根据触摸位置的变化更新进度条的进度,并调用 invalidate() 方法触发重绘。
  5. 设置和获取进度:提供 setProgress() 和 getProgress() 方法,用于设置和获取进度条的进度。

  通过以上步骤,你就可以实现一个可以滑动的波浪形进度条。

   总结:

   自定义 View 的工作原理主要基于继承 View 类,重写关键方法如 onMeasureonLayout 和 onDraw 来完成测量、布局和绘制操作,还可通过自定义属性丰富配置,重写事件处理方法响应交互。基于此原理,我们利用正弦曲线生成波浪形状,在 onDraw 方法中动态绘制波浪,结合重写 onTouchEvent 方法响应触摸操作,从而实现可滑动且带有滚动效果的波浪形进度条。


http://www.ppmy.cn/server/175562.html

相关文章

Matlab 灰度质心+抛物线拟合提取条纹中心

文章目录 一、简介二、实现代码三、实现效果一、简介 这里的思路也是很简单,主要分为三个步骤: 1. 使用灰度质心法提取初始的条纹中心。但是这种方法有它的缺点,他是沿着水平方向进行灰度加权来计算得到的,这其实并不是很合理,因此就有第二个过程。 2. 计算初始条纹中心点…

Java 8新特性:Lambda表达式与Stream API实战

一、Lambda表达式革命性变革 1. 从匿名类到Lambda的演进 // Java 7 匿名内部类 Runnable oldRunnable new Runnable() { Override public void run() { System.out.println("Old way"); } }; // Java 8 Lambda表达式 Runnable newRunnable () -> S…

【yolo标签格式(txt)转coco格式(json)】

1.YOLO格式文件组织 这是要转化的YOLO格式文件组织&#xff0c;分别有images和labels文件&#xff0c;里面划分了train和val。 2.代码实现 将其转化为coco格式。 import os import cv2 import json import argparse from tqdm import tqdmCOCO_DICT [images, annotations…

总结 HTTPS 的加密流程

目录 1 HTTPS是什么 2 "加密"是什么 3 HTTPS的⼯作过程 3.1 引⼊对称加密 3.2 引⼊⾮对称加密 3.3 中间⼈攻击 3.4 引⼊证书 1 HTTPS是什么 HTTPS也是⼀个应⽤层协议.是在HTTP协议的基础上引⼊了⼀个加密层. HTTP协议内容都是按照⽂本的⽅式明⽂传输的.这就…

【ElasticSearch】学习笔记

一、lucene的组成 segment是一个具备完整搜索功能的最小单元。 多个segment组成了一个单机文本检索库lucene。 inverted index:倒排索引&#xff0c;用于快速根据关键词找到对应的文章term index: 构建出关键词的目录树&#xff0c;解决了term dictionary数据量过大&#xff…

如何绕过 reCAPTCHA V2/V3:Python、Selenium 与其他工具的实战指南

前言 验证码&#xff08;CAPTCHA&#xff09;技术已经存在多年&#xff0c;尽管它的有效性一直备受争议&#xff0c;但许多网站仍然依赖它来保护资源。特别是 Google 推出的 reCAPTCHA 系列&#xff0c;一直是验证码领域的领跑者。本文将探讨如何绕过 reCAPTCHA V2 和 V3&…

【C】嵌入式的中断,理解

&#x1f680; 嵌入式中断&#xff08;Interrupt&#xff09;概念解析 在嵌入式系统中&#xff0c;中断&#xff08;Interrupt&#xff09;是一种硬件或软件事件&#xff0c;它可以打断 CPU 的正常运行流程&#xff0c;立即执行特定的中断服务程序&#xff08;ISR&#xff0c;…

TTP/HTTPS、TCP/IP 协议、RPC、Socket 通信机制

1. TTP/HTTPS TTP (HTTP) 和 HTTPS&#xff08;Hypertext Transfer Protocol Secure&#xff09;是基于 TCP/IP 协议的应用层协议&#xff0c;主要用于客户端和服务器之间的数据传输。 HTTP&#xff08;超文本传输协议&#xff09;&#xff1a;这是用于 web 页面和服务器之间…