
在自定义 View 中直接调用 Drawable.draw(canvas) 无法显示应用图标,根本原因是系统返回的 Drawable 未设置有效 bounds;只需手动调用 setBounds() 指定绘制区域,即可正常渲染。
在自定义 view 中直接调用 `drawable.draw(canvas)` 无法显示应用图标,根本原因是系统返回的 drawable 未设置有效 bounds;只需手动调用 `setbounds()` 指定绘制区域,即可正常渲染。
在开发 Launcher 类应用时,常通过 PackageManager 获取目标 App 的图标 Drawable:
Drawable icon = resolveInfo.activityInfo.loadIcon(packageManager);
该 Drawable 确实包含有效的图像资源(如 BitmapDrawable 或 AdaptiveIconDrawable),但其内部 getBounds() 返回的是 (0, 0, 0, 0) —— 即空边界矩形。而 Drawable.draw(Canvas) 方法仅在 bounds 非空且面积大于 0 时才会执行实际绘制逻辑,否则静默跳过,导致“什么也没画出来”。
相比之下,View.setBackground(Drawable) 能成功显示,正是因为 View 在设置背景时自动调用了 drawable.setBounds(),将其适配到视图的当前尺寸(例如 setBounds(0, 0, width, height))。因此,你无需依赖 super.draw() 或背景机制,只需显式设定 bounds 即可完全掌控绘制流程。
✅ 正确做法(在自定义 View 的 onDraw() 中):
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (icon != null) {
// 关键:必须设置有效 bounds,否则 draw() 不生效
int iconSize = Math.min(getWidth(), getHeight());
icon.setBounds(0, 0, iconSize, iconSize); // 或按需适配宽高比
icon.draw(canvas);
}
}
⚠️ 注意事项:
-
不要复用未初始化 bounds 的 Drawable:每次
onDraw()前都应确保setBounds()已被调用(尤其当 View 尺寸变化或图标更换时); - 若需缩放/居中,建议使用
new Rect()构造精确目标区域,而非简单设为getWidth()/getHeight(),避免拉伸失真; - 对于
AdaptiveIconDrawable(Android 8.0+),setBounds()同样必要,且其内部子图层(foreground/background)会据此自动布局; - 可结合
Drawable.getMinimumWidth()/getMinimumHeight()获取推荐尺寸,提升兼容性。
总结:Drawable 是无状态绘图对象,其绘制行为高度依赖 bounds —— 这是 Android 图形系统的设计契约,而非 Bug。掌握这一机制,你就能在完全绕过 View 默认绘制逻辑的前提下,实现精准、可控的图标渲染,为焦点态高亮、动画遮罩、混合图层等高级 UI 效果打下坚实基础。











