Android中的drawable像素密度适配详解

2023-10-30

本文大纲:

  • 1.为什么要分drawable-xxx目录?
  • 2.Bitmap的介绍
  • 3.不同drawable-xxx中的图片内存测试

1.为什么要分drawable-xxx目录

 

android项目资源中对于drawable文件夹可以分为以下类型的文件夹:

  • drawable-ldpi(低密度) 
  • drawable-mdpi(中等密度)     
  • drawable-hdpi(高密度)
  • drawable-xhdpi(超高密度)    
  • drawable-xxhdpi(超超高密度)
  • drawable-xxxhdpi(超超超高密度)
  • drawable-nohdpi(无缩放)   
  • drawable(默认密度)

 

我们大概知道这些是存放不同分辨率的图片的,高分辨率的图片要放在高密度的文件夹里,低分辨率的图片要放在低密度的文件夹里。但是考虑下面几个问题:

  1. 各个不同的文件夹对应的具体密度是多少?
  2. 各个文件夹该放置多大分辨率的图片?
  3. android加载图片的时候是怎么从这么多drawable文件夹里找到图片的?

问题1:各个文件夹对应的具体密度

表1:

注意密度是根据设备来决定的,设备密度的获取方式:

float xdpi = context.getResources().getDisplayMetrics().xdpi; 

float ydpi = context.getResources().getDisplayMetrics().ydpi;

假设此时的设备密度是480dpi, 那么xxhdpi会被认为是程序的匹配密度目录,默认优先使用xxhdpi文件下的图片。

 

问题2:各个文件夹该放置多大分辨率的图片:

每种密度下的图片设计成什么尺寸Android也是给出了建议:

表2:

上面是andriod给出的建议尺寸,实际中可能会不同。但是一定要记住一下几点:

 

  • 如果图片所在目录的dpi低于匹配目录,那么该图片会被人为是为低密度设备需要的,现在要显示在高密度设备上,图片会进行放大。
  • 如果图片所在目录的dpi高于匹配目录,那么该图片会被认为是为高密度设备需要的,现在要显示在低密度设备上,图片会进行缩放。
  • 如果图片所在目录为drawable-nodpi,则无论设备dpi为多少,保留原图片大小,不进行缩放

 

延伸问题:如果不匹配,那么缩放或者放大多少呢?

 

公式: scale=设备dpi / 图片所在drawable目录对应的最大dpi

 

举个例子,假设当前的设备密度是 480dpi, 一张72x72的图片分别放在drawable-xxhdpi和drawable-xhdp,两种情况下图片各占多少内存?(假设现在是RGB565格式)

 

分析:

1.图片放在drawable-xxhdpi:

由表1可知xxhdpi对应的最大dpi为480dpi

scale = 设备密度/xxhdpi对应的最大dpi = 480dpi/480dpi = 1

也就是说这种情况下不会进行缩放,因为图片恰好放在了设备匹配的文件夹中。此时图片占用的内存= 72 * 72 * 2 = 10368

 

2.图片放在drawable-xhdpi:

由表1可知xhdpi对应的最大dpi为320dpi

scale = 设备密度/xhdpi对应的最大dpi = 480dpi/320dpi =1.5

这种情况下要进行放大,因为低于设备匹配目录,此时图片占用的内存= 72 * 72 * 2 * 1.5 = 15552

 

所以,我们知道,如果在开发时不恰当的将高分辨的图片放在低密度的文件夹里,很可能在高密度的设备上显示的时候会oom,因为它们会进行放大!!

问题3:Android加载drawable文件夹中的图片的过程

图1:

查找步骤:

  • 1.计算出设备的密度,并找到与屏幕密度匹配的drawable目录,根据表1可得
  • 2.首先去匹配的drawable文件夹里找图片,如果没有,先去更高密度的drawable中查找,如果依然找不到,再去更高密度drawable中找。
  • 3.如果没有密度更高的drawable文件夹,就去nodpi文件夹查找。
  • 4.如果nodpi文件夹没有,就去匹配密度的低级别的drawable文件夹依次查找。
  • 5.如果所有drawable都找不到,就报错。

说明:

  1. 以上几步,每一步如果找到,就会立即返回,不会再往下查找。
  2. 只有当图片在设备密度匹配的drawable文件夹或者nodpi文件夹中时,图片不会进行缩放。

Bitmap的介绍:

简单介绍bitmap, 它是android的图片资源或文件在内存中的展现方式。

 

颜色通道格式:

/**
*每个像素存储为一个半透明(alpha)通道。
*这对于有效地存储掩码非常有用。
*不存储颜色信息。
*使用这种配置,每个像素需要1字节的内存。
* /
Bitmap.Config.ALPHA_8

/ * *
*每个像素存储在2个字节,只有RGB通道
*编码:红色是存储5位精度(32可能
*值),绿色是存储与6位精度(64可能
*值)和蓝色是存储与5位精度。
*
*根据不同的情况,此配置可能会产生轻微的可视工件
*关于源的配置。例如,没有
*犹豫不决,结果可能会显示出一抹绿色。得到更好的
*结果应用抖动。
*
*此配置在使用不透明位图时可能很有用
*不需要高的色彩保真度。
*
* <p>使用此公式压缩成16位:</p>
* < pre类= " prettyprint " >
*短颜色= (R & 0 x1f) < < 11 | (G & 0 x3f) < < 5 | (B & 0 x1f);
* < / pre >
Bitmap.Config.RGB_565

/ * *
*每个像素存储在2个字节。三个RGB彩色通道
*和alpha通道(半透明)存储为4位
精度(16个可能的值)
*
*如果应用程序需要,此配置非常有用
*存储半透明信息,但也需要保存
*内存。
*
*建议使用{@link #ARGB_8888}代替这个
*配置。
*
*注意:在{@link android.os.Build.VERSION_CODES#KITKAT}时,
*使用此配置创建的任何位图都将被创建
*使用{@link #ARGB_8888}代替。
*
* @deprecated,因为这个配置的质量很差,
*建议使用{@link #ARGB_8888}。
* /
Bitmap.Config.ARGB_4444

/ * *
*每个像素存储4个字节。每个频道(RGB和alpha)
*用于半透明)以8位精度存储(256位)
*可能值。)
*
这种配置非常灵活,提供了最好的
*质量。只要可能就应该使用它。
*
* <p>使用这个公式打包成32位:</p>
* < pre类= " prettyprint " >
* int颜色= (& 0 xff) < < 24 | (B & 0 xff) < < 16 | (G & 0 xff) < < 8 | (R & 0 xff);
* < / pre >
* /
Bitmap.Config.ARGB_8888

/ * *
*每个像素存储在8个字节。每个频道(RGB和alpha)
*用于半透明)存储为a
* {@link android.util。半精度浮点值}。
*
*此配置特别适合宽色域和
* HDR的内容。
*
* <p>使用这个公式压缩成64位:</p>
* < pre类= " prettyprint " >
* long color = (A & 0xffff) << 48 | (B & 0xffff) << 32 | (G & 0xffff) << 16 | (R & 0xffff);
* < / pre >
* /
Bitmap.Config.RGBA_F16

/ * *
*特殊配置,当位图只存储在图形内存。
该配置中的位图总是不可变的。
*
*这是最佳的情况下,当与位图的唯一操作是绘制它在a
*屏幕上。
* /
Bitmap.Config.HARDWARE

在加载图片的时候默认使用ARGB_8888,可以指定颜色格式,通过BitpamOption设置

BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = ARGB_4444;
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), img, options);

Bitmap的信息:

// 一行占用多少像素
int width = bitmap.getWidth();
// 一列占用多少像素
int height = bitmap.getHeight();
// 一行占用多少字节
int rowBytes = bitmap.getRowBytes();
// 可用于存储此位图像素的最小字节数 它等于 rowBytes * height
int byteCount = bitmap.getByteCount();
// 用于存储位图像素的已分配内存的大小。
int allocationByteCount = bitmap.getAllocationByteCount();

说明:

  • width和height是像素, 比如 72*66的图片,width和height是 72 和 66. 不过当图片没有在设备密度匹配drawable目录中的时候,会进行相应的缩放。
  • rowBytes=width* 每个像素所占字节, 每个像素所占的字节是由图像的颜色通道决定的。
  • byteCount 和 allocationByteCount 默认情况下是相同的。但是如果一个位图被重用或者手动重新配置之后,那么就会不同。

不同drawable-xxx中的图片内存测试

 

基于上面的基础知识,我们密度为480dpi的设备上,将一张 66x71的图片分别放在drawable不同密度的文件夹中,进行内存占用情况的测试。

推测:

    图片放在drawable-xxhdpi或者drawable-nodpi中时内存占用为原始大小,其他情况会缩放。

验证:

    首先来编写测试的代码:

// 获取设备的dpi
float xdpi = context.getResources().getDisplayMetrics().xdpi;
float ydpi = context.getResources().getDisplayMetrics().ydpi;
// 加载图片查看内存
LogUtil.E("=====测试,设备密度: xdpi:"+xdpi+",ydpi:"+ydpi+", 使用ARGB8888模式加载66x71的图片======");
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = ARGB_8888;
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), img, options);
int width = bitmap.getWidth();
int height = bitmap.getHeight();
int rowBytes = bitmap.getRowBytes();
int byteCount = bitmap.getByteCount();
int allocationByteCount = bitmap.getAllocationByteCount();
LogUtil.E("加载结果:");
LogUtil.E("width-->" + width);
LogUtil.E("height-->" + height);
LogUtil.E("rowBytes-->" + rowBytes);
LogUtil.E("byteCount-->" + byteCount);
LogUtil.E("allocationByteCount-->" + allocationByteCount);

 

图片放在 drawable下的输出情况:

=====测试,设备密度: xdpi:480.0,ydpi:480.0, 使用ARGB8888模式加载66x71的图片======
加载结果:
width-->198
height-->213
rowBytes-->792
byteCount-->168696
allocationByteCount-->168696

图片放在 drawable-mdpi下的输出情况:

=====测试,设备密度: xdpi:480.0,ydpi:480.0, 使用ARGB8888模式加载66x71的图片======
加载结果:
width-->198
height-->213
rowBytes-->792
byteCount-->168696
allocationByteCount-->168696

图片放在 drawable-xhdpi下的输出情况:

=====测试,设备密度: xdpi:480.0,ydpi:480.0, 使用ARGB8888模式加载66x71的图片======
加载结果:
width-->99
height-->107
rowBytes-->396
byteCount-->42372
allocationByteCount-->42372

图片放在 drawable-xxhdpi下的输出情况:

=====测试,设备密度: xdpi:480.0,ydpi:480.0, 使用ARGB8888模式加载66x71的图片======
加载结果:
width-->66
height-->71
rowBytes-->264
byteCount-->18744
allocationByteCount-->18744

图片放在 drawable-xxxhdpi下的输出情况:

=====测试,设备密度: xdpi:480.0,ydpi:480.0, 使用ARGB8888模式加载66x71的图片======
加载结果:
width-->50
height-->53
rowBytes-->200
byteCount-->10600
allocationByteCount-->10600

图片放在 drawable-nodpi下的输出情况:

=====测试,设备密度: xdpi:480.0,ydpi:480.0, 使用ARGB8888模式加载66x71的图片======
加载结果:
width-->66
height-->71
rowBytes-->264
byteCount-->18744
allocationByteCount-->18744

 

 

 

 

 

 

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android中的drawable像素密度适配详解 的相关文章

  • 如何快速自动发送FCM或APNS消息?

    我正在开发一项后端服务 通过 FCM 或 APNS 向移动应用程序发送推送通知 我想创建一个可以在一分钟内运行的自动化测试 并验证服务器是否可以成功发送通知 请注意 我不一定需要检查通知是否已送达 只需检查 FCM 或 APNS 是否已成功
  • 使用workmanager时Firestore脱机持久性错误

    我正在使用一个WorkManger定期从我的中检索信息Firestore当应用程序处于后台和前台时的数据库 此信息用于根据状态更新 UI 因此不同的状态会添加或删除 UI 的不同部分 第一次运行时效果很好 但是 一旦应用程序处于后台并且Wo
  • android中向sqlite中插入大量数据

    目前 我必须一次向我的 Android 中插入超过 100 亿条数据 然而 内存不足的问题会使程序崩溃 sqlite 插入测试非常简单 只需使用 for 循环生成 sql 插入命令并通过 开始 和 提交 进行包装 private Array
  • Android - 从资产中解析巨大(超大)JSON 文件的最佳方法

    我正在尝试从资产文件夹中解析一些巨大的 JSON 文件 我如何加载并添加到 RecyclerView 我想知道解析这种大文件 大约 6MB 的最佳方法是什么 以及您是否知道可以帮助我处理此文件的良好 API 我建议您使用GSON lib h
  • Adobe 是否为其 PDF 阅读器提供 Android SDK 或 API? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我希望能够在我们的应用程序内的视图中显示本地 PDF 文件 在 Android 4 03 下的平板电脑上运行 目前 我们将 Adob eR
  • Android 模拟器插件无法初始化后端 EGL 显示

    我在 Cloudbees 上设置了 Jenkins 作业 并且可以在那里成功签出并编译我的 Android 项目 现在我想在 android 模拟器中运行一些 JUnit 测试并添加 Android 模拟器插件 我将 显示模拟器窗口 选项设
  • 在 java 类和 android 活动之间传输时音频不清晰

    我有一个android活动 它连接到一个java类并以套接字的形式向它发送数据包 该类接收声音数据包并将它们扔到 PC 扬声器 该代码运行良好 但在 PC 扬声器中播放声音时会出现持续的抖动 中断 安卓活动 public class Sen
  • 带有 EditText 和 Spinner 的对话框

    我有一个按钮 单击后会弹出一个对话框 我希望对话框有一个EditText and a Spinner对话框内 我不知道如何设置它的视图 我有一个代码AlertDialog它有效 只是EditText and Spinner我需要将其放入其中
  • 是否有 ADB 命令来检查媒体是否正在播放

    我想使用 ADB 命令检查根植于终端的外部设备中是否正在播放音频 视频 我无法找到任何 ADB 命令 如果有 我尝试过 adb shell dumpsys media player 我想要一个命令来指定视频是否正在运行 您可以使用以下命令查
  • 无法展开 RemoteViews - 错误通知

    最近 我收到越来越多的用户收到 RemoteServiceException 错误的报告 我每次给出的堆栈跟踪如下 android app RemoteServiceException Bad notification posted fro
  • Android MediaExtractor seek() 对 MP3 音频文件的准确性

    我在使用 Android 时无法在eek 上获得合理的准确度MediaExtractor 对于某些文件 例如this one http www archive org download emma solo librivox emma 01
  • 获取当前 android.intent.category.LAUNCHER 活动的实例

    我创建了一个库项目 并在多个应用程序之间共享 我实现了一个简单的会话过期功能 该功能将在一段时间后将用户踢回到登录屏幕 登录屏幕活动是我的主要活动 因此在清单中它看起来像这样
  • 如何发布Android .aar源以使Android Studio自动找到它们?

    我正在将库发布到内部 Sonatype Nexus 存储库 Android Studio 有一个功能 可以自动查找通过 gradle 引用的库的正确源 我将 aar 的源代码作为单独的 jar 发布到 Nexus 但 Android Stu
  • 尝试在 ubuntu 中编译 android 内核时出错

    我正在尝试从源代码编译 Android 内核 并且我已经下载了所有正确的软件包来执行此操作 但由于某种原因我收到此错误 arm linux androideabi gcc error unrecognized command line op
  • 字符串数组文本格式化

    我有这个字符串 String text Address 1 Street nr 45 Address 2 Street nr 67 Address 3 Street nr 56 n Phone number 000000000 稍后将被使用
  • Android访问远程SQL数据库

    我可以直接从 Android 程序访问远程 SQL 数据库 在网络服务器上 吗 即简单地打开包含所有必需参数的连接 然后执行 SQL 查询 这是一个私人程序 不对公众开放 仅在指定的手机上可用 因此我不担心第三方获得数据库访问权限 如果是这
  • 一次显示两条Toast消息?

    我希望在一个位置显示一条 Toast 消息 并在另一位置同时显示另一条 Toast 消息 多个 Toast 消息似乎总是按顺序排队和显示 是否可以同时显示两条消息 是否有一种解决方法至少可以提供这种外观并且不涉及扰乱活动布局 Edit 看来
  • 将 Intent 包装在 LabeledIntent 中以用于显示目的

    要求 我的应用程序中有一个 共享 按钮 我需要通过 Facebook 分享 我需要选择是否安装原生 Facebook 应用程序 我们的决定是 如果未安装该应用程序 则将用户发送到 facebook com 进行分享 当前状态 我可以检测何时
  • 捕获的图像分辨率太大

    我在做什么 我允许用户捕获图像 将其存储到 SD 卡中并上传到服务器 但捕获图像的分辨率为宽度 4608 像素和高度 2592 像素 现在我想要什么 如何在不影响质量的情况下获得小分辨率图像 例如我可以获取或设置捕获的图像分辨率为原始图像分
  • 如何将 google+ 登录集成到我的 Android 应用程序中?

    大家好 实际上我需要通过我的应用程序从 google 登录人们 现在我阅读了 google 上的文档 其中指出 要允许用户登录 请将 Google Sign In 集成到您的应用中 初始化 GoogleApiClient 对象时 请求 PL

随机推荐

  • 浏览器刷新、关闭页面与统计在线人数

    项目中可能需要统计在线人数 也可能需要在用户在退出时进行用户注销登录 既为统计实时在线人数 也为及时清理暂时不再使用的session 节约资源提高性能 对于以上的情况 若用户使用页面的注销按钮退出登录 那一定万事大吉了 当实际中这种可能性很
  • Java面试题(1)-J2SE基础

    最近在为自己实习准备 看了网上各种面试经验贴 也和身边的小伙伴一起参加了不少牛逼IT企业的面试 这篇文章就将面试遇到的一些比较常见的问题整理一下 给大家一些参考 也为自己整理整理 J2SE基础 1 九种基本数据类型的大小 以及他们的封装类
  • 猿创征文

    猿创征文 国产数据库实战 使用docker部署PolarDB X云原生分布式开源数据库 一 PolarDB X介绍 1 PolarDB X简介 2 PolarDB X特点 二 检查docker版本 三 检查docker配置信息 四 下载Po
  • redis集群原理

    redis是单线程 但是一般的作为缓存使用的话 redis足够了 因为它的读写速度太快了 官方的一个简单测试 测试完成了50个并发执行100000个请求 设置和获取的值是一个256字节字符串 结果 读的速度是110000次 s 写的速度是8
  • MySQL高频面试题

    文章目录 1 什么是MySQL 2 关系型数据库和非关系型数据库 3 数据库三大范式是什么 4 一条 SQL 查询语句是如何执行的 5 引擎 MySQL存储引擎MyISAM与InnoDB区别 MyISAM索引与InnoDB索引的区别 Inn
  • 哈夫曼树带权路径长度

    一 长什么样 左边是普通树 右边是哈夫曼树 图a WPL 5 2 7 2 2 2 13 2 54 图b WPL 5 3 2 3 7 2 13 1 48 可见 图b的带权路径长度较小 我们可以证明图b就是哈夫曼树 也称为最优二叉树 二 怎么生
  • Vue实现swiper轮播组件

    目前市面上有很多轮播组件 但是有的不满足业务需求 因此也需要自己首先轮播组件 以下是一个用vue实现的轮播组件 带动画效果 可以自行设置轮播速度 选择是否需要分页器等 效果如下 思路 结构 一个轮播组件应该由三部分组成 一是轮播的元素 如图
  • 如何在内存中执行二进制代码之win平台

    大家可能会很好奇 我们的任意exe程序 不就是在内存中执行的二进制机器码吗 不 今天我要说的是 我们如何把实现指定功能的一段二进制机器码 放到我们的程序中 然后在需要的时候 直接调用它 当然 这段代码也有其他用途 故而有了shell cod
  • 公众号分享

    配置 gt 登录公众号 gt 设置与开发 gt 公众号设置 gt 功能设置 gt js安全域名域名 gt 网页授权域名 1 先拿集成微信 js 路径写自己的 import wxshare from common js wxShareModu
  • 【树莓派之旅】第01期:一根网线搞定树莓派可视化界面

    一次偶然的机会接触到了树莓派 于是就购了一个板子 入手快两个月了 由于一直忙于工作的事情 所以也就没折腾 今天拿出来的时候发现要想把树莓派运行起来还需要一些其他外接设备 一时也没去某宝或某东上采购 就利用手头现有的资源玩一下 于是就有了此文
  • Sentinel实现动态配置的集群流控的方法

    这篇文章主要介绍了Sentinel实现动态配置的集群流控 本文给大家介绍的非常详细 对大家的学习或工作具有一定的参考借鉴价值 需要的朋友可以参考下 介绍 为什么要使用集群流控呢 相对于单机流控而言 我们给每台机器设置单机限流阈值 在理想情况
  • 部署 - 前端部署https服务,并配置安全证书

    项目中要实现跨tab复制 剪切 粘贴 所以涉及到操作剪切板的操作 选用了navigator clipboard 但是该api有必须在https的服务下才能用 所以就需要把项目部署城https服务 vue cli中可以配置webpack达到启
  • Android OkHttp源码阅读详解一

    博主前些天发现了一个巨牛的人工智能学习网站 通俗易懂 风趣幽默 忍不住也分享一下给大家 点击跳转到教程 前言 源码阅读基于okhttp 3 10 0 Android中OkHttp源码阅读二 责任链模式 implementation com
  • 注解实现CRUD

    CRUD 我们可以在工具类创建的时候实现自动提交事务 这个是在MybatisUtils工具类里面的弄的 public static SqlSession getSqlSession return sqlSessionFactory open
  • react 加粗_React入门的家庭作业(1-2)

    作业前准备 React在他的官网上挂了一个入门教程 是做一个xxoo棋的小游戏 在教程的最后留下了6个作业题 现在就来完成一下 准备材料 作业是在已完成教程的基础上做的 所以这里预设已经做好了环境部署 写好了游戏代码 如果没有 可以把这个复
  • 深聊性能测试,从入门到放弃之:性能测试技术栈,看完这篇,保证刷新你对性能测试的认知~~

    性能测试技术栈 1 引言 2 性能测试基础 2 1 性能测试理论 2 2 测试开发技能 3 性能监听诊断 3 1 前端监听诊断 3 2 服务器监听诊断 3 2 1 Linux 3 2 2 Windows Server 3 3 中间件监听诊断
  • 实现http到https的重定向

    vim etc httpd conf httpd conf DocumentRoot var www html redirect temp https www a com RewriteEngine on RewriteRule https
  • tensorflow笔记【9】深度学习-几个经典网络的基本结构

    tensorflow笔记 9 深度学习 几个经典网络的基本结构 文章目录 前言 一 卷积神经网络 二 经典网络结构 1 自制CNN 2 LeNet 3 AlexNet 4 VGGNet 5 InceptionNet 6 ResNet 总结
  • Java - Java基础(一)

    一 计算机基础知识 1 计算机简介 第一台通用计算机 ENIAC 2 数据的存储和运算 计算机中对于数据的存储和运算 都是通过二进制数据来完成 3 人机交互方式 图形化界面 DOS命令行 D 切换盘符 dir 查看当前路径下的文件信息 cd
  • Android中的drawable像素密度适配详解

    本文大纲 1 为什么要分drawable xxx目录 2 Bitmap的介绍 3 不同drawable xxx中的图片内存测试 1 为什么要分drawable xxx目录 android项目资源中对于drawable文件夹可以分为以下类型的