Android第六次面试总结(自定义 View与事件分发)

在 Android 中实现自定义 View 处理 1 万条数据的流畅滑动,需结合视图复用、按需绘制、硬件加速等核心技术。以下是具体实现方案:

一、核心优化策略

1. 视图复用机制(类似 RecyclerView)
  • ViewHolder 模式:将每个数据项的视图封装为 ViewHolder,通过对象池复用视图实例。
class ItemViewHolder {View itemView;TextView textView;// 其他子控件
}

对象池管理:使用LinkedList<ItemViewHolder>缓存闲置视图,避免频繁创建销毁

private final LinkedList<ItemViewHolder> viewPool = new LinkedList<>();private ItemViewHolder obtainViewHolder() {if (viewPool.isEmpty()) {View itemView = LayoutInflater.from(context).inflate(R.layout.item_layout, this, false);return new ItemViewHolder(itemView);}return viewPool.poll();
}private void recycleViewHolder(ItemViewHolder holder) {viewPool.offer(holder);
}
2. 按需绘制(仅渲染可见区域)
  • 计算可见范围:根据滚动偏移量(scrollY)和视图高度,确定需绘制的起始和结束索引。
@Override
protected void onDraw(Canvas canvas) {super.onDraw(canvas);int visibleStart = (int) Math.floor(scrollY / itemHeight);int visibleEnd = (int) Math.ceil((scrollY + getHeight()) / itemHeight);for (int i = visibleStart; i <= visibleEnd; i++) {if (i >= dataList.size()) break;drawItem(canvas, i);}
}

3. 硬件加速与缓存优化
  • 启用硬件加速:在构造函数中设置setLayerType(LAYER_TYPE_HARDWARE, null)
  • 缓存绘制结果:使用Bitmap缓存已绘制的视图区域,减少重复计算。
private Bitmap cacheBitmap;
private Canvas cacheCanvas;@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);cacheBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);cacheCanvas = new Canvas(cacheBitmap);
}@Override
protected void onDraw(Canvas canvas) {// 先绘制到缓存画布,再整体绘制到屏幕cacheCanvas.drawColor(Color.WHITE);// 绘制可见项canvas.drawBitmap(cacheBitmap, 0, 0, null);
}

二、实现步骤

1. 数据适配器设计
  • 抽象数据适配器:分离数据逻辑与视图渲染。
public abstract class DataAdapter<T> {public abstract int getItemCount();public abstract T getItem(int position);public abstract int getItemHeight(int position);public abstract void bindViewHolder(ItemViewHolder holder, T item);
}
2. 触摸事件处理
  • 惯性滚动实现:使用ScrollerVelocityTracker
private Scroller scroller;
private VelocityTracker velocityTracker;@Override
public boolean onTouchEvent(MotionEvent event) {if (velocityTracker == null) {velocityTracker = VelocityTracker.obtain();}velocityTracker.addMovement(event);switch (event.getAction()) {case MotionEvent.ACTION_DOWN:if (!scroller.isFinished()) {scroller.abortAnimation();}startY = (int) event.getY();break;case MotionEvent.ACTION_MOVE:int dy = (int) (event.getY() - startY);scrollBy(0, -dy);startY = (int) event.getY();break;case MotionEvent.ACTION_UP:velocityTracker.computeCurrentVelocity(1000);int velocityY = (int) velocityTracker.getYVelocity();scroller.fling(0, getScrollY(), 0, -velocityY, 0, 0, 0, maxScrollY);velocityTracker.recycle();velocityTracker = null;invalidate();break;}return true;
}@Override
public void computeScroll() {if (scroller.computeScrollOffset()) {scrollTo(scroller.getCurrX(), scroller.getCurrY());invalidate();}
}

三、性能监控与调优

1. 帧率监控
  • 使用 Android Profiler:监控 GPU 渲染帧率,确保稳定在 60fps。
  • 自定义帧率统计
private long frameTime = System.currentTimeMillis();
private int frameCount = 0;@Override
protected void onDraw(Canvas canvas) {super.onDraw(canvas);frameCount++;if (System.currentTimeMillis() - frameTime >= 1000) {Log.d(TAG, "FPS: " + frameCount);frameCount = 0;frameTime = System.currentTimeMillis();}
}
2. 内存分析
  • 使用 MAT 工具:分析内存快照,识别潜在泄漏点。
  • 资源释放:在onDetachedFromWindow中清理缓存。
@Override
protected void onDetachedFromWindow() {super.onDetachedFromWindow();if (cacheBitmap != null && !cacheBitmap.isRecycled()) {cacheBitmap.recycle();cacheBitmap = null;}
}

四、完整代码示例

public class CustomListView extends ViewGroup {private DataAdapter<?> adapter;private int itemHeight = 100; // 每项高度private int maxScrollY;// 初始化代码public CustomListView(Context context) {super(context);init();}private void init() {scroller = new Scroller(context);setWillNotDraw(false);setLayerType(LAYER_TYPE_HARDWARE, null);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int width = MeasureSpec.getSize(widthMeasureSpec);int height = MeasureSpec.getSize(heightMeasureSpec);setMeasuredDimension(width, height);maxScrollY = adapter.getItemCount() * itemHeight - height;}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {// 布局逻辑(此处简化)}private void drawItem(Canvas canvas, int position) {ItemViewHolder holder = obtainViewHolder();adapter.bindViewHolder(holder, adapter.getItem(position));holder.itemView.layout(0, position * itemHeight - scrollY, getWidth(), (position + 1) * itemHeight - scrollY);holder.itemView.draw(canvas);recycleViewHolder(holder);}// 设置适配器public void setAdapter(DataAdapter<?> adapter) {this.adapter = adapter;requestLayout();invalidate();}
}

五、扩展建议

  1. 数据分批加载:结合分页加载,每次仅加载当前可见区域附近的数据。
  2. 预取优化:在滚动时预加载下一页数据。
  3. 动态高度支持:在DataAdapter中实现getItemHeight(int position)动态返回项高度。
  4. 缓存策略:使用 LruCache 缓存常用视图,提升复用效率。

在 Android 开发中,事件分发机制是实现用户交互的核心逻辑之一。理解事件如何被 View 接收和处理,对于解决滑动冲突、触摸响应异常等问题至关重要。以下是事件分发机制的详细解析:

一、事件分发的核心流程

事件分发遵循Activity → ViewGroup → View的传递路径,通过三个关键方法实现:

  1. dispatchTouchEvent(MotionEvent ev)

    • 作用:决定事件是否继续分发。
    • 返回值:
      • true:事件由当前 View 处理,停止向下传递。
      • false:事件回传给父 View 的onTouchEvent
      • super.dispatchTouchEvent(ev):继续调用子 View 的dispatchTouchEvent
  2. onInterceptTouchEvent(MotionEvent ev)(仅 ViewGroup 可用)

    • 作用:判断是否拦截事件。
    • 返回值:
      • true:拦截事件,事件由当前 ViewGroup 处理。
      • false:不拦截,事件继续传递给子 View。
  3. onTouchEvent(MotionEvent ev)

    • 作用:处理具体的触摸事件。
    • 返回值:
      • true:事件被消费,停止向上传递。
      • false:事件未被消费,回传给父 View 的onTouchEvent

二、事件传递的典型场景

场景 1:事件被 ViewGroup 拦截
public class CustomViewGroup extends LinearLayout {@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {// 拦截DOWN事件,后续事件(MOVE/UP)也会被拦截return ev.getAction() == MotionEvent.ACTION_DOWN;}@Overridepublic boolean onTouchEvent(MotionEvent event) {// 处理点击事件return true;}
}
场景 2:子 View 处理事件
public class CustomButton extends AppCompatButton {@Overridepublic boolean onTouchEvent(MotionEvent event) {// 处理点击事件return true;}
}

三、常见问题与解决方案

问题 1:父 View 拦截导致子 View 无法响应
  • 原因:父 ViewGroup 在onInterceptTouchEvent中错误拦截事件。
  • 解决方案
    onInterceptTouchEvent中根据业务逻辑选择性拦截(如仅拦截滑动事件)。
问题 2:事件未被消费导致冒泡
  • 原因:View 的onTouchEvent返回false,事件向上传递。
  • 解决方案
    onTouchEvent中处理完事件后返回true,或设置clickable="true"(默认消费事件)。
问题 3:滑动冲突(如嵌套滑动)
  • 解决方案
    1. 使用NestedScrollViewRecyclerView等支持嵌套滑动的控件。
    2. 重写父 ViewGroup 的onInterceptTouchEvent,根据滑动方向动态决定是否拦截。

四、优化事件分发的建议

  1. 减少层级嵌套:避免多层 ViewGroup 嵌套导致事件传递效率低下。
  2. 复用事件对象:通过MotionEvent.obtain()减少对象创建开销。
  3. 延迟处理复杂逻辑:在ACTION_UP事件中处理耗时操作,避免阻塞主线程。
  4. 使用GestureDetector:简化手势处理逻辑,如双击、长按等。

五、总结

事件分发机制的核心原则是:事件由父容器向下传递,子 View 可通过返回true消费事件。掌握这一机制能有效解决触摸响应问题,尤其在处理自定义 View 和复杂布局时更为关键。结合具体场景合理设计拦截逻辑,可显著提升用户交互体验。

感谢观看!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/41915.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

GaussDB数据库表设计与性能优化实践

GaussDB分布式数据库表设计与性能优化实践 引言 在金融、电信、物联网等大数据场景下&#xff0c;GaussDB作为华为推出的高性能分布式数据库&#xff0c;凭借其创新的架构设计和智能优化能力&#xff0c;已成为企业核心业务系统的重要选择。本文深入探讨GaussDB分布式架构下的…

npm install 卡在创建项目:sill idealTree buildDeps

参考&#xff1a; https://blog.csdn.net/PengXing_Huang/article/details/136460133 或者再执行 npm install -g cnpm --registryhttps://registry.npm.taobao.org 或者换梯子

【MySQL】从零开始:掌握MySQL数据库的核心概念(五)

由于我的无知&#xff0c;我对生存方式只有一个非常普通的信条&#xff1a;不许后悔。 前言 这是我自己学习mysql数据库的第五篇博客总结。后期我会继续把mysql数据库学习笔记开源至博客上。 上一期笔记是关于mysql数据库的增删查改&#xff0c;没看的同学可以过去看看&#xf…

抖音矩阵系统源码开发与部署技巧!短视频矩阵源码搭建部署

在短视频蓬勃发展的时代&#xff0c;短视频矩阵已成为内容创作者和企业扩大影响力、提升传播效果的重要策略。而一个高效、易用的前端系统对于短视频矩阵的成功运营至关重要。本文将深入探讨短视频矩阵前端源码搭建的技术细节&#xff0c;为开发者提供全面的技术指导。 一、技…

ESP32S3 WIFI 实现TCP服务器和静态IP

一、 TCP服务器代码 代码由station_example_main的官方例程修改 /* WiFi station ExampleThis example code is in the Public Domain (or CC0 licensed, at your option.)Unless required by applicable law or agreed to in writing, thissoftware is distributed on an &q…

物质与空:边界中的确定性,虚无中的无限可能——跨学科视角下的存在本质探析

一、哲学框架&#xff1a;二元性与超越性 1. 物质的边界性&#xff1a;有限世界的确定性法则 在人类认知的起点&#xff0c;物质以"非0即1"的绝对姿态显现。一块石头、一滴水、乃至微观粒子&#xff0c;都以明确的边界定义自身存在。这种确定性映射着&#xff1a; 亚…

linux常用指令(10)

那么我们就继续来学习linux指令的使用,来了解搜索查找类的相关指令,话不多说,来看. 搜索查找类 1.find指令 find将从指定目录向下遍历其各个子目录,将满足条件的条件或目录显示在终端 基本语法 find[搜索范围][项项] 选项说明 -name<查询方式> 按照指定的文件名查找…

AWTK-WEB 快速入门(6) - JS WebSocket 应用程序

WebSocket 可以实现双向通信&#xff0c;适合实时通信场景。本文介绍一下使用 Javacript 语言开发 AWTK-WEB 应用程序&#xff0c;并用 WebSocket 与服务器通讯。 用 AWTK Designer 新建一个应用程序 先安装 AWTK Designer&#xff1a; https://awtk.zlg.cn/web/index.html …

机器学习——集成学习框架(GBDT、XGBoost、LightGBM、CatBoost)、调参方法

一、集成学习框架 对训练样本较少的结构化数据领域&#xff0c;Boosting算法仍然是常用项 XGBoost、CatBoost和LightGBM都是以决策树为基础的集成学习框架 三个学习框架的发展是&#xff1a;XGBoost是在GBDT的基础上优化而来&#xff0c;CatBoost和LightGBM是在XGBoost的基础上…

Leetcode 最长递增子序列的个数

java solution class Solution {public int findNumberOfLIS(int[] nums) {//边界情况处理int n nums.length;if(n < 1) return n;//然后我们创建2个数组, 分别是count数组和length数组,//length[i]的含义是以i结尾的子数组的最长递增子序列长度//count[i]的含义是以i结尾…

原型验证后客户推翻原有需求,如何止损

原型验证后客户推翻原有需求时止损的有效方法包括&#xff1a;迅速评估影响范围、立即开展沟通确认、调整项目计划和资源配置、更新变更管理流程、协商成本分担机制。其中&#xff0c;迅速评估影响范围是关键&#xff0c;项目团队必须立即明确此次变更的具体影响&#xff0c;包…

在rockylinux9.4安装mongodb报错:缺少:libcrypto.so.10文件库

问题点&#xff1a; rockylinux9.4系统环境报错&#xff1a; ./mongod: error while loading shared libraries: libcrypto.so.10: cannot open shared object file: No such file or directory 解决方法&#xff1a; Ps&#xff1a;解压之后&#xff0c;检查mongodb的依赖环境…

如何应对竞品分析不足导致的方案偏差

应对竞品分析不足导致方案偏差的有效措施包括&#xff1a;深入竞品调研、建立定期竞品分析机制、明确竞品分析维度、引入专业竞品分析工具、优化内部沟通反馈机制。其中&#xff0c;深入竞品调研尤为重要。通过全面深入地分析竞争对手的产品策略、市场定位及用户反馈&#xff0…

基于Python+LanceDB实战向量搜索

本篇实战演示向量搜索的实现和示例。 预期效果 给出一个查询的字符串&#xff0c;通过向量搜索&#xff0c;在下面三个语句中搜索出关联性最大的那句。 "熊猫是中国的国宝&#xff0c;主要栖息在四川山区。","长城是古代中国建造的军事防御工事&#xff0c;全…

在 Linux(Ubuntu / CentOS 7)上快速搭建我的世界 MineCraft 服务器,并实现远程联机,详细教程

Linux 部署 MineCraft 服务器 详细教程&#xff08;丐版&#xff0c;无需云服务器&#xff09; 一、虚拟机 Ubuntu 部署二、下载 Minecraft 服务端三、安装 JRE 21四、安装 MCS manager 面板五、搭建服务器六、本地测试连接七、下载樱花&#xff0c;实现内网穿透&#xff0c;邀…

【科研绘图系列】R语言绘制重点物种进化树图(taxa phylogenetic tree)

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍加载R包数据下载导入数据数据预处理画图输出图片系统信息介绍 【科研绘图系列】R语言绘制重点物种进化树图(taxa phylogenetic tree) 加载R包 library(tidyverse) library(ape…

浏览器渲染过程

浏览器的渲染过程是多个线程、进程和阶段的复杂编排&#xff0c;它将原始的 HTML、CSS 和 JavaScript 转换为屏幕上的交互像素。 你在浏览器中输入一个 URL 并按下回车键 网站在你的屏幕上呈现出来 注意&#xff1a;本文中&#xff0c;将使用 “客户端&#xff08;client&am…

华鲲振宇天工TG225 B1国产服务器试装openEuler22.03 -SP4系统

今天测试了一下在华鲲振宇公司的天工TG225 B1国产服务器上进行openEuler22.03 -SP4操作系统的试装&#xff0c;本文记录整个测试过程。 一、服务器信息 1、服务器型号 Huakun TG225 B1 (D) 2、登录IPMI帐户信息 初始用户名Tech.ON 密码TianGong8000 二、磁盘RAID配置 测试…

Qemu-STM32(十二):STM32F103 框架代码添加

简介 本系列博客主要描述了STMF103的qemu模拟器实现&#xff0c;进行该项目的原因有两点: 作者在高铁上&#xff0c;想在STM32F103上验证一个软件框架时&#xff0c;如果此时掏出开发板&#xff0c;然后接一堆的线&#xff0c;旁边的人估计会投来异样的目光&#xff0c;特别是…

英伟达与通用汽车深化合作,澳特证券am broker助力科技投资

在近期的GTC大会上&#xff0c;英伟达CEO黄仁勋宣布英伟达将与通用汽车深化合作&#xff0c;共同推进AI技术在自动驾驶和智能工厂的应用。此次合作标志着自动驾驶汽车时代的加速到来&#xff0c;同时也展示了英伟达在AI技术领域的最新进展。      合作内容包括&#xff1a;…