为什么android drawable画圆-xxhdpi老是被锁住

Android drawable微技巧,你所不知道的drawable的那些细节
话说微技巧这个词也是我自己发明的,因为drawable这个东西相信大家天天都在使用,每个人都再熟悉不过了,之所以叫微技巧就是对于这个我们再熟悉不过的技术,可能还有一些你所不知道的细节,那今天我们就来一起探究一下这些微小的细节吧。
大家都知道,在项目当中,drawable文件夹都是用来放置图片资源的,不管是jpg、png、还是9.png,都可以放在这里。除此之外,还有像selector这样的xml文件也是可以放在drawable文件夹下面的。
但是如果你现在使用Android Studio来新建一个项目,你会发现有如下的目录结构:
嗯?怎么会有这么多mipmap开头的文件夹,而且它们的命名规则和drawable文件夹很相似,也是hdpi、mdpi、xhdpi等等,并且里面还真是放的图片,难道Android项目中放置图片的位置已经改了?
对于刚刚从Eclipse转向Android Studio的开发者们可能会对mipmap文件夹感到陌生,其实不用担心,我们平时的习惯并不需要发生任何改变,因为mipmap文件夹只是用来放置应用程序的icon的,仅此而已。那么在此之前,我们都是把应用程序的icon图标和普通的图片资源一起放到drawable文件夹下的,这样看上去就会比较杂乱,有的时候想从一堆的图片资源里面找icon半天也找不到,而文件一多也就容易出现漏放的情况,但恰恰Android是极度建议我们在每一种分辨率的文件夹下面都放一个相应尺寸的icon的,因此将它们独立出来专门放到mimap文件夹当中就很好地解决了这个问题。
另外,将icon放置在mipmap文件夹还可以让我们程序的launcher图标自动拥有跨设备密度展示的能力,比如说一台屏幕密度是xxhdpi的设备可以自动加载mipmap-xxxhdpi下的icon来作为应用程序的launcher图标,这样图标看上去就会更加细腻。
关于建议使用mipmap的原文可以参阅这篇文章:Getting Your Apps Ready for Nexus 6 and Nexus 9, 当然你还是要科学上网的。
除此之外,对于每种密度下的icon应该设计成什么尺寸其实Android也是给出了最佳建议,icon的尺寸最好不要随意设计,因为过低的分辨率会造成图标模糊,而过高的分辨率只会徒增APK大小。建议尺寸如下表所示:
然后我们引用mipmap的方式和之前引用drawable的方式是完全一致的,在资源中就使用@mipmap/res_id,在代码就使用R.mipmap.res_id。比如AndroidManifest.xml中就是这样引用ic_launcher图标的:
好的,关于mimap的内容就讲这么多,它并不是本篇文章的重点,接下来我们来真真正正看一些drawable的微技巧。
首先我准备了一张270*480像素的图片:
将图片命名为android_logo.png,然后把它放在drawable-xxhdpi文件夹下面。为什么要放在这个文件夹下呢?是因为我的手机屏幕的密度就是xxhdpi的。那么怎么才能知道自己手机屏幕的密度呢?你可以使用如下方法先获取到屏幕的dpi值:
float xdpi = getResources().getDisplayMetrics().
float ydpi = getResources().getDisplayMetrics().
其中xdpi代表屏幕宽度的dpi值,ydpi代表屏幕高度的dpi值,通常这两个值都是近乎相等或者极其接近的,在我的手机上这两个值都约等于403。那么403又代表着什么意思呢?我们直接参考下面这个表格就知道了:
从表中可以看出,403dpi是处于320dpi到480dpi之间的,因此属于xxhdpi的范围。
图片放好了之后,下面我在布局文件中引用这张图片,如下所示:
在ImageView控件中指定加载android_logo这张图,并把ImageView控件的宽高都设置成wrap_content,这样图片有多大,我们的控件就会有多大。
现在运行一下程序,效果如下所示:
由于我的手机分辨率是像素的,而这张图片的分辨率是270*480像素的,刚好是手机分辨率的四分之一,因此从上图中也可以看出,android_logo图片的宽和高大概都占据了屏幕宽高的四分之一左右,大小基本是比较精准的。
到目前为止一切都挺顺利的,不是吗?下面我们尝试做点改变,将android_logo.png这张图移动到drawable-xhdpi文件夹下,注意不是复制一份到drawable-xhdpi文件夹下,而是将图片移动到drawable-xhdpi文件夹下,然后重新运行一下程序,效果如下图所示:
嗯?怎么感觉图片好像变大了一点,是错觉吗?
那么我们再将这张图移动到drawable-mdpi文件夹下试试,重新运行程序,效果如下图所示:
这次肯定不是错觉了,这实在是太明显了,图片被放大了!
那么为什么好端端的一张图片会被自动放大呢?而且这放大的比例是不是有点太过份了。其实不然,Android所做的这些缩放操作都是有它严格的规定和算法的。可能有不少做了很多年Android的朋友都没去留意过这些缩放的规则,因为这些细节太微小了,那么本篇的微技巧探索里面,我们就来把这些细节理理清楚。
首先解释一下图片为什么会被放大,当我们使用资源id来去引用一张图片时,Android会使用一些规则来去帮我们匹配最适合的图片。什么叫最适合的图片?比如我的手机屏幕密度是xxhdpi,那么drawable-xxhdpi文件夹下的图片就是最适合的图片。因此,当我引用android_logo这张图时,如果drawable-xxhdpi文件夹下有这张图就会优先被使用,在这种情况下,图片是不会被缩放的。但是,如果drawable-xxhdpi文件夹下没有这张图时, 就会自动去其它文件夹下找这张图了,优先会去更高密度的文件夹下找这张图片,我们当前的场景就是drawable-xxxhdpi文件夹,然后发现这里也没有android_logo这张图,接下来会尝试再找更高密度的文件夹,发现没有更高密度的了,这个时候会去drawable-nodpi文件夹找这张图,发现也没有,那么就会去更低密度的文件夹下面找,依次是drawable-xhdpi -& drawable-hdpi -& drawable-mdpi -& drawable-ldpi。
总体匹配规则就是这样,那么比如说现在终于在drawable-mdpi文件夹下面找到android_logo这张图了,但是系统会认为你这张图是专门为低密度的设备所设计的,如果直接将这张图在当前的高密度设备上使用就有可能会出现像素过低的情况,于是系统自动帮我们做了这样一个放大操作。
那么同样的道理,如果系统是在drawable-xxxhdpi文件夹下面找到这张图的话,它会认为这张图是为更高密度的设备所设计的,如果直接将这张图在当前设备上使用就有可能会出现像素过高的情况,于是会自动帮我们做一个缩小的操作。所以,我们可以尝试将android_logo这张图移动到drawable-xxxhdpi文件夹下面将会得到这样的结果:
可以看到,现在图片的宽和高都达到不手机屏幕的四分之一,说明图片确实是被缩小了。
另外,刚才在介绍规则的时候提到了一个drawable-nodpi文件夹,这个文件夹是一个密度无关的文件夹,放在这里的图片系统就不会对它进行自动缩放,原图片是多大就会实际展示多大。但是要注意一个加载的顺序,drawable-nodpi文件夹是在匹配密度文件夹和更高密度文件夹都找不到的情况下才会去这里查找图片的,因此放在drawable-nodpi文件夹里的图片通常情况下不建议再放到别的文件夹里面。
图片被放大的原因现在我们已经搞清楚了,那么接下来还有一个问题,就是放大的倍数是怎么确定的呢?很遗憾,我没有找到相关的文档记载,但是我自己总结出了一个规律,这里跟大家分享一下。
还是看一下刚才的 dpi范围-密度 表格:
可以看到,每一种密度的dpi范围都有一个最大值,这个最大值之间的比例就是图片会被系统自动放大的比例。
口说无凭,下面我们来通过实例验证一下,修改布局文件中的代码,如下所示:
可以看到,我们添加了一个按钮,并给按钮注册了一个点击事件。然后在MainActivity中处理这个点击事件:
public class MainActivity extends AppCompatActivity {
ImageView imageV
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = (ImageView) findViewById(R.id.image);
public void buttonClick(View view) {
Toast.makeText(this, &图片宽度:& + imageView.getWidth(), Toast.LENGTH_SHORT).show();
Toast.makeText(this, &图片高度:& + imageView.getHeight(), Toast.LENGTH_SHORT).show();
这里在点击事件中分别获取图片的宽和高并使用Toast提示出来。代码修改这么多就可以了,然后将图片移动到drawable-mdpi文件夹下。
下面我们来开始分析,mdpi密度的最高dpi值是160,而xxhdpi密度的最高dpi值是480,因此是一个3倍的关系,那么我们就可以猜测,放到drawable-mdpi文件夹下的图片在xxhdpi密度的设备上显示会被放大3倍。对应到android_logo这张图,原始像素是270*480,放大3倍之后就应该是810*1440像素。下面运行程序,效果如下图所示:
验证通过。我们再来试验一次,将图片移动到drawable-xxxhdpi目录下。xxxhdpi密度的最高dpi值是640,480是它的0.75倍,那么我们就可以猜测,放到drawable-xxxdpi文件夹下的图片在xxhdpi密度的设备上显示会被缩小至0.75倍。270*480的0.75倍应该是202.5*360,由于像素不支持小数点,那么四舍五入就应该是203*360像素。重新运行程序,效果如下图所示:
再次验证通过。如果你有兴趣的话可以使用其它几种dpi的drawable文件夹来试一试,应该都是适配这套缩放规则的。这样我们就把图片为什么会被缩放,以及具体的缩放倍数都搞明白了,drawable相关的细节你已经探究的非常细微了。
不过本篇文章到这里还没结束,下面我准备讲一讲我们在实际开发当中会遇到的场景。根据Android的开发建议,我们在准备图片资源时尽量应该给每种密度的设备都准备一套,这样程序的适配性就可以达到最好。但实际情况是,公司的UI们通常就只会给一套图片资源,想让他们针对每种密度的设备都设计一套图片资源,并且还是按照我们上面讲的缩放比例规则来设计,就有点想得太开心了。没错,这个就是现实情况,那么在这种情况下,我们应该将仅有的这一套图片资源放在哪个密度的文件夹下呢?
可以这样来分析,根据我们刚才所学的内容,如果将一张图片放在低密度文件夹下,那么在高密度设备上显示图片时就会被自动放大,而如果将一张图片放在高密度文件夹下,那么在低密度设备上显示图片时就会被自动缩小。那我们可以通过成本的方式来评估一下,一张原图片被缩小了之后显示其实并没有什么副作用,但是一张原图片被放大了之后显示就意味着要占用更多的内存了。因为图片被放大了,像素点也就变多了,而每个像素点都是要占用内存的。
我们仍然可以通过例子来直观地体会一下,首先将android_logo.png图片移动到drawable-xxhdpi目录下,运行程序后我们通过Android Monitor来观察程序内存使用情况:
可以看到,程序所占用的内存大概稳定在19.45M左右。然后将android_logo.png图片移动到drawable-mdpi目录下,重新运行程序,结果如下图所示:
现在涨到23.40M了,占用内存明显增加了。如果你将图片移动到drawable-ldpi目录下,你会发现占用内存会更高。
通过这个例子同时也验证了一个问题,我相信有不少比较有经验的Android程序员可能都遇到过这个情况,就是当你的项目变得越来越大,有的时候加载一张drawable-hdpi下的图片,程序就直接OOM崩掉了,但如果将这张图放到drawable-xhdpi或drawable-xxhdpi下就不会崩掉,其实就是这个道理。
那么经过上面一系列的分析,答案自然也就出来了,图片资源应该尽量放在高密度文件夹下,这样可以节省图片的内存开支,而UI在设计图片的时候也应该尽量面向高密度屏幕的设备来进行设计。就目前来讲,最佳放置图片资源的文件夹就是drawable-xxhdpi。那么有的朋友可能会问了,不是还有更高密度的drawable-xxxhdpi吗?干吗不放在这里?这是因为,市面上480dpi到640dpi的设备实在是太少了,如果针对这种级别的屏幕密度来设计图片,图片在不缩放的情况下本身就已经很大了,基本也起不到节省内存开支的作用了。视野多大,舞台就有多大
Android适配——drawable和values的加载规则
Google搞了一大套 dip、sp、mdpi、hdpi、xhdpi之类的这些东西,简单说来,就是为了让我们轻松实现“与设备密度无关的视觉大小一致性”,这里需要明确的“视觉大小一致性”,就是说无论是在手机、低分辨率平板、高分辨率平板上,这个控件或者图片在物理尺寸上都是一样的。
这里我们借 @雨打萍 的一张图片来看看:
(出处: )
其中,黑色和蓝色矩形的视觉大小就是一致的。
另外一个需要明确的是屏幕密度、分辨率、物理尺寸之间的关系:
以一个分辨率为,物理尺寸为7寸的手持平板而言,根据勾股定理,我们可以得出其对角线上的像素数大约为2264,用2264除以7就是此屏幕的屏幕密度,结果为323.
也就是说,屏幕密度、分辨率、物理尺寸可以由二推一,hdpi-屏幕的物理尺寸绝对要比hdpi-大,这也是我们之后讨论适配的前提。
· 实际密度与系统密度
我们经常见到的Android设备屏幕密度大多为120、160、240、320、480等,而上述例子中的323dpi则是设备的实际密度,说明这块屏幕每寸有323个像素。得到实际密度以后,Android系统推荐选择一个最近的密度作为系统密度,通过DisplayMetrics类获取上述设备的系统密度就是320dpi。
但是,现在很多Android厂商不一定会选择这些值作为系统密度,而是选择实际的dpi作为系统密度,这就导致了很多手机的dpi也不是在这些值内。例如小米Note这样的xxhdpi的设备他的系统密度并不是480,而是它的实际密度440。
那么针对这些非规范的系统密度,Google官方指定按照系列标准进行区分:
有了这两个前提,再来看android的适配规则就很清楚了:
drawable目录
我们经常会给应用程序切几套图片,放在drawable-mdpi、drawable-hdpi、drawable-xhdpi等目录下面。当应用在设备对应dpi目录下没有找到某个资源时,遵循“先高再低”原则,然后按比例缩放图片:
比如,当前为xhdpi设备,并且只有以下几个目录,则drawable的寻找顺序为:
xhdpi-&xxhdpi-&xxxhdpi(如果没有更高的了)-&nodpi(如果有的话)-&hdpi-&mdpi,如果在xxhdpi中找到目标图片,则压缩2/3来使用,如果在mdpi中找到图片,则放大2倍来使用。
这很好理解,如果我们按规则放置两张图片,mdpi中为48x48,xxhdpi中为144x144,那么不管我们最后从哪个目录拿到图片,在xhdpi设备上显示的像素大小都是96x96,只是一个被拉伸而来,一个被压缩而来。由于xhdpi定义了96个像素点的物理尺寸,那么这张图的物理尺寸实际就被定下来了。
同样的,mdpi中48个像素点的物理尺寸与xhdpi中96个像素点的物理尺寸是相同的,这就保证了该图片在任何设备上显示出的视觉大小一致。
那么,一个结论就是,对于期望保持视觉大小一致的那部分图片而言,如果你也能接受android为你拉伸/压缩图片导致一定程度的模糊或者锐化,那么这些图片是不需要在每个drawable目录下都制作一份的。以现在主流设备来说一般可能在drawable-xxhdpi放置一份即可,这样可以尽量避免Android为我们放大图片所导致的OOM。
当然,在某些情况下,我们会主观希望打破android提供的“视觉大小一致”这种机制,此时我们就可以建立另外的drawable目录来放置需要变化的图片了。
然后附一份比例表吧:
values目录
values目录用来放置colors.xml,dimens.xml,strings.xml等,也可以根据屏幕密度设置特定的values目录让满足设定的设备进行加载,比如values-mdpi、values-hdpi、values-xhdpi、values-xxhdpi等等,然后每个目录放置一个demins.xml,使不同分辨率的设备应用不同的尺寸设置。当应用设备在当前dpi对应目录的demins.xml中没有找到目标条目时,采用“就近匹配”原则:
比如,当前为hdpi设备,并且只有以下几个目录,则values的寻找顺序为:
hdpi-&xhdpi-&mdpi-&values,即先向上级dpi目录查找,再向下级dpi目录查找,最后一路向下查找到values目录,如果values下都找不到,就只有找values-ldpi,当然,现在有这个目录的应用不多了。
那么,我们需要将mdpi目录下的值都乘以相应的倍数来放置在其他目录下面吗?答案当然是否定的,由于我们对期望屏幕密度无关的值都定义为了dp或者dip的单位,无论android从哪个目录最终找到该值,都会直接应用这个值与当前设备的密度来计算最终的尺寸。
也就是说,如果我们同样在values-xhdpi和values下写 length=10dp
那么在mdpi设备上得到的都是10px,在xhdpi设备上得到的都是20px。
但它们看起来“都是一样宽”,这样就已经是“保证视觉大小一致性了”。
48dp法则告诉我们,48dp的物理尺寸约等于9mm,是人的手指比较容易点击到的大小,并且是独立于设备的。
显而易见,如果我们在values目录下写length=10dp,values-xhdpi目录下写length=20dp,mdpi设备上得到的将是10px,而xhdpi设备上得到的将是40px,得到视觉结果就是,该控件在xhdpi设备上看起来比mdpi设备上大了一倍。
考虑这样一个情况:有一个BottomBar,左右两端各有一个按钮,按钮长宽用dp定义,这样在一个大屏手机中,两个按钮可能就相隔更远了,因为按钮的视觉尺寸是没有变化的。如果你想保持按钮在BottomBar中所占的比例,最好办法不是添加一个values-xxx,然后重写这两个dp值,而是精心设计你的布局。
那么,既然最后都要找到values,并且能够保证视觉大小一致性,那何必再添加其他values分辨率目录呢?答案是在某些情况下,我们主观希望某些尺寸不去保持视觉一致性。例如一个Button,在手机上那么大刚好,但如果在平板设备上,是的,它看起来和在手机上一样大,但是,它显得有点小了。
也就是说,我们应该把希望在任何设备上视觉大小都一样的尺寸都放置在values目录下并且只放置这一份,其他需要有变化的尺寸则放置在对应目录下即可
一般而言,使用在物理尺寸相差不大的几套设备上,一个values可能就够了,因为它本身就保证了“视觉大小一致性”,但是如果你的应用需要兼容平板,甚至电视,那么这种一致性可能是一种灾难。这时可以考虑添加一个对应dpi的values目录,把需要变化的值拷贝进去重写,但我更推荐采用values-xhdpi-,我们很容易通过这里的屏幕分辨率+dpi计算得到该设备的物理尺寸,显而易见这是一个平板设备,如此我们的改动便不至于影响同DPI的低物理尺寸设备(手机),而物理尺寸差不多的设备是可以共用一套dimens.xml的。
那么,如果当前设备为xhdpi-,当前目录有values-xhdpi-,values-xhdpi-,values-xhdpi-,android的寻找顺序则是:
values-xhdpi--&values-xhdpi--&values-xhdpi
只向低于自己分辨率的目录下寻找,直到values-xhdpi,如果依然没有找到,按照之前的顺序继续进行。(hdpi- -& hdpi- -& hdpi -& …)
也就是说,对于同dpi的多台不同分辨率平板设备,如果布局足够通用,我们可以只针对最低分辨率设计dimens即可,上面的例子中,则是values-xhdpi-。
我们还可以将这个分辨率写得更低,低到我们有把握:如果再出现比这个分辨率更低的设备,那么它的物理尺寸一定满足即使采用values-xhdpi中针对手机物理尺寸设计的大小也没有问题。
没有更多推荐了,出处:&好像有挺久时间没更新博客了,最近我为了准备下一个系列的博客,也是花了很长的时间研读源码。很遗憾的是,下一个系列的博客我可能还要再过一段时间才能写出来,那么为了不至于让大家等太久,今天就给大家更新一篇单篇的文章,讲一讲&drawable方面的微技巧。话说微技巧这个词也是我自己发明的,因为drawable这个东西相信大家天天都在使用,每个人都再熟悉不过了,之所以叫微技巧就是对于这个我们再熟悉不过的技术,可能还有一些你所不知道的细节,那今天我们就来一起探究一下这些微小的细节吧。&大家都知道,在Android项目当中,drawable文件夹都是用来放置图片资源的,不管是jpg、png、还是9.png,都可以放在这里。除此之外,还有像selector这样的xml文件也是可以放在drawable文件夹下面的。&但是如果你现在使用Android Studio来新建一个项目,你会发现有如下的目录结构:&嗯?怎么会有这么多mipmap开头的文件夹,而且它们的命名规则和drawable文件夹很相似,也是hdpi、mdpi、xhdpi等等,并且里面还真是放的图片,难道Android项目中放置图片的位置已经改了?&对于刚刚从Eclipse转向Android Studio的开发者们可能会对mipmap文件夹感到陌生,其实不用担心,我们平时的编程习惯并不需要发生任何改变,因为mipmap文件夹只是用来放置应用程序的icon的,仅此而已。那么在此之前,我们都是把应用程序的icon图标和普通的图片资源一起放到drawable文件夹下的,这样看上去就会比较杂乱,有的时候想从一堆的图片资源里面找icon半天也找不到,而文件一多也就容易出现漏放的情况,但恰恰Android是极度建议我们在每一种分辨率的文件夹下面都放一个相应尺寸的icon的,因此将它们独立出来专门放到mimap文件夹当中就很好地解决了这个问题。&另外,将icon放置在mipmap文件夹还可以让我们程序的launcher图标自动拥有跨设备密度展示的能力,比如说一台屏幕密度是xxhdpi的设备可以自动加载mipmap-xxxhdpi下的icon来作为应用程序的launcher图标,这样图标看上去就会更加细腻。&关于建议使用mipmap的原文可以参阅这篇文章:, 当然你还是要科学上网的。&除此之外,对于每种密度下的icon应该设计成什么尺寸其实Android也是给出了最佳建议,icon的尺寸最好不要随意设计,因为过低的分辨率会造成图标模糊,而过高的分辨率只会徒增APK大小。建议尺寸如下表所示:密度建议尺寸mipmap-mdpi48 * 48mipmap-hdpi72 * 72mipmap-xhdpi96 * 96mipmap-xxhdpi144 * 144mipmap-xxxhdpi192 * 192然后我们引用mipmap的方式和之前引用drawable的方式是完全一致的,在资源中就使用@mipmap/res_id,在代码就使用R.mipmap.res_id。比如AndroidManifest.xml中就是这样引用ic_launcher图标的:&application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme"&
&activity android:name=".MainActivity"&
&intent-filter&
&action android:name="android.intent.action.MAIN"/&
&category android:name="android.intent.category.LAUNCHER"/&
&/intent-filter&
&/activity& &/application&1234567891011121312345678910111213好的,关于mimap的内容就讲这么多,它并不是本篇文章的重点,接下来我们来真真正正看一些drawable的微技巧。首先我准备了一张270*480像素的图片:&将图片命名为android_logo.png,然后把它放在drawable-xxhdpi文件夹下面。为什么要放在这个文件夹下呢?是因为我的手机屏幕的密度就是xxhdpi的。那么怎么才能知道自己手机屏幕的密度呢?你可以使用如下方法先获取到屏幕的dpi值:float xdpi = getResources().getDisplayMetrics(). float ydpi = getResources().getDisplayMetrics().1212其中xdpi代表屏幕宽度的dpi值,ydpi代表屏幕高度的api值,通常这两个值都是近乎相等或者极其接近的,在我的手机上这两个值都约等于403。那么403又代表着什么意思呢?我们直接参考下面这个表格就知道了:dpi范围密度0dpi ~ 120dpildpi120dpi ~ 160dpimdpi160dpi ~ 240dpihdpi240dpi ~ 320dpixhdpi320dpi ~ 480dpixxhdpi480dpi ~ 640dpixxxhdpi从表中可以看出,403dpi是处于320dpi到480dpi之间的,因此属于xxhdpi的范围。&图片放好了之后,下面我在布局文件中引用这张图片,如下所示:&?xml version="1.0" encoding="utf-8"?& &LinearLayout
& xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
& android:layout_height="match_parent"
&ImageView
android:id="@+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/android_logo"
/& &&/LinearLayout&123456789101112131415123456789101112131415在ImageView控件中指定加载android_logo这张图,并把ImageView控件的宽高都设置成wrap_content,这样图片有多大,我们的控件就会有多大。&现在运行一下程序,效果如下所示:&由于我的手机分辨率是像素的,而这张图片的分辨率是270*480像素的,刚好是手机分辨率的四分之一,因此从上图中也可以看出,android_logo图片的宽和高大概都占据了屏幕宽高的四分之一左右,大小基本是比较精准的。&到目前为止一切都挺顺利的,不是吗?下面我们尝试做点改变,将android_logo.png这张图移动到drawable-xhdpi文件夹下,注意不是复制一份到drawable-xhdpi文件夹下,而是将图片移动到drawable-xhdpi文件夹下,然后重新运行一下程序,效果如下图所示:&嗯?怎么感觉图片好像变大了一点,是错觉吗?&那么我们再将这张图移动到drawable-mdpi文件夹下试试,重新运行程序,效果如下图所示:&这次肯定不是错觉了,这实在是太明显了,图片被放大了!&那么为什么好端端的一张图片会被自动放大呢?而且这放大的比例是不是有点太过份了。其实不然,Android所做的这些缩放操作都是有它严格的规定和算法的。可能有不少做了很多年Android的朋友都没去留意过这些缩放的规则,因为这些细节太微小了,那么本篇的微技巧探索里面,我们就来把这些细节理理清楚。&首先解释一下图片为什么会被放大,当我们使用资源id来去引用一张图片时,Android会使用一些规则来去帮我们匹配最适合的图片。什么叫最适合的图片?比如我的手机屏幕密度是xxhdpi,那么drawable-xxhdpi文件夹下的图片就是最适合的图片。因此,当我引用android_logo这张图时,如果drawable-xxhdpi文件夹下有这张图就会优先被使用,在这种情况下,图片是不会被缩放的。但是,如果drawable-xxhdpi文件夹下没有这张图时, 系统就会自动去其它文件夹下找这张图了,优先会去更高密度的文件夹下找这张图片,我们当前的场景就是drawable-xxxhdpi文件夹,然后发现这里也没有android_logo这张图,接下来会尝试再找更高密度的文件夹,发现没有更高密度的了,这个时候会去drawable-nodpi文件夹找这张图,发现也没有,那么就会去更低密度的文件夹下面找,依次是drawable-xhdpi -& drawable-hdpi -& drawable-mdpi -& drawable-ldpi。&总体匹配规则就是这样,那么比如说现在终于在drawable-mdpi文件夹下面找到android_logo这张图了,但是系统会认为你这张图是专门为低密度的设备所设计的,如果直接将这张图在当前的高密度设备上使用就有可能会出现像素过低的情况,于是系统自动帮我们做了这样一个放大操作。&那么同样的道理,如果系统是在drawable-xxxhdpi文件夹下面找到这张图的话,它会认为这张图是为更高密度的设备所设计的,如果直接将这张图在当前设备上使用就有可能会出现像素过高的情况,于是会自动帮我们做一个缩小的操作。所以,我们可以尝试将android_logo这张图移动到drawable-xxxhdpi文件夹下面将会得到这样的结果:&可以看到,现在图片的宽和高都达到不手机屏幕的四分之一,说明图片确实是被缩小了。&另外,刚才在介绍规则的时候提到了一个drawable-nodpi文件夹,这个文件夹是一个密度无关的文件夹,放在这里的图片系统就不会对它进行自动缩放,原图片是多大就会实际展示多大。但是要注意一个加载的顺序,drawable-nodpi文件夹是在匹配密度文件夹和更高密度文件夹都找不到的情况下才会去这里查找图片的,因此放在drawable-nodpi文件夹里的图片通常情况下不建议再放到别的文件夹里面。&图片被放大的原因现在我们已经搞清楚了,那么接下来还有一个问题,就是放大的倍数是怎么确定的呢?很遗憾,我没有找到相关的文档记载,但是我自己总结出了一个规律,这里跟大家分享一下。&还是看一下刚才的 dpi范围-密度 表格:dpi范围密度0dpi ~ 120dpildpi120dpi ~ 160dpimdpi160dpi ~ 240dpihdpi240dpi ~ 320dpixhdpi320dpi ~ 480dpixxhdpi480dpi ~ 640dpixxxhdpi可以看到,每一种密度的dpi范围都有一个最大值,这个最大值之间的比例就是图片会被系统自动放大的比例。&口说无凭,下面我们来通过实例验证一下,修改布局文件中的代码,如下所示:&LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
&ImageView
android:id="@+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/android_logo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="获取图片宽高"
android:onClick="buttonClick"
&/LinearLayout&123456789101112131415161718192021123456789101112131415161718192021可以看到,我们添加了一个按钮,并给按钮注册了一个点击事件。然后在MainActivity中处理这个点击事件:&&public class MainActivity extends AppCompatActivity {
ImageView imageV
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = (ImageView) findViewById(R.id.image);
public void buttonClick(View view) {
Toast.makeText(this, "图片宽度:" + imageView.getWidth(), Toast.LENGTH_SHORT).show();
Toast.makeText(this, "图片高度:" + imageView.getHeight(), Toast.LENGTH_SHORT).show();
}这里在点击事件中分别获取图片的宽和高并使用Toast提示出来。代码修改这么多就可以了,然后将图片移动到drawable-mdpi文件夹下。&下面我们来开始分析,mdpi密度的最高dpi值是160,而xxhdpi密度的最高dpi值是480,因此是一个3倍的关系,那么我们就可以猜测,放到drawable-mdpi文件夹下的图片在xxhdpi密度的设备上显示会被放大3倍。对应到android_logo这张图,原始像素是270*480,放大3倍之后就应该是810*1440像素。下面运行程序,效果如下图所示:&验证通过。我们再来试验一次,将图片移动到drawable-xxxhdpi目录下。xxxhdpi密度的最高dpi值是640,480是它的0.75倍,那么我们就可以猜测,放到drawable-xxxdpi文件夹下的图片在xxhdpi密度的设备上显示会被缩小至0.75倍。270*480的0.75倍应该是202.5*360,由于像素不支持小数点,那么四舍五入就应该是203*360像素。重新运行程序,效果如下图所示:&再次验证通过。如果你有兴趣的话可以使用其它几种dpi的drawable文件夹来试一试,应该都是适配这套缩放规则的。这样我们就把图片为什么会被缩放,以及具体的缩放倍数都搞明白了,drawable相关的细节你已经探究的非常细微了。&不过本篇文章到这里还没结束,下面我准备讲一讲我们在实际开发当中会遇到的场景。根据Android的开发建议,我们在准备图片资源时尽量应该给每种密度的设备都准备一套,这样程序的适配性就可以达到最好。但实际情况是,公司的UI们通常就只会给一套图片资源,想让他们针对每种密度的设备都设计一套图片资源,并且还是按照我们上面讲的缩放比例规则来设计,就有点想得太开心了。没错,这个就是现实情况,那么在这种情况下,我们应该将仅有的这一套图片资源放在哪个密度的文件夹下呢?&可以这样来分析,根据我们刚才所学的内容,如果将一张图片放在低密度文件夹下,那么在高密度设备上显示图片时就会被自动放大,而如果将一张图片放在高密度文件夹下,那么在低密度设备上显示图片时就会被自动缩小。那我们可以通过成本的方式来评估一下,一张原图片被缩小了之后显示其实并没有什么副作用,但是一张原图片被放大了之后显示就意味着要占用更多的内存了。因为图片被放大了,像素点也就变多了,而每个像素点都是要占用内存的。&我们仍然可以通过例子来直观地体会一下,首先将android_logo.png图片移动到drawable-xxhdpi目录下,运行程序后我们通过Android Monitor来观察程序内存使用情况:&可以看到,程序所占用的内存大概稳定在19.45M左右。然后将android_logo.png图片移动到drawable-mdpi目录下,重新运行程序,结果如下图所示:&现在涨到23.40M了,占用内存明显增加了。如果你将图片移动到drawable-ldpi目录下,你会发现占用内存会更高。&通过这个例子同时也验证了一个问题,我相信有不少比较有经验的Android程序员可能都遇到过这个情况,就是当你的项目变得越来越大,有的时候加载一张drawable-hdpi下的图片,程序就直接OOM崩掉了,但如果将这张图放到drawable-xhdpi或drawable-xxhdpi下就不会崩掉,其实就是这个道理。&那么经过上面一系列的分析,答案自然也就出来了,图片资源应该尽量放在高密度文件夹下,这样可以节省图片的内存开支,而UI在设计图片的时候也应该尽量面向高密度屏幕的设备来进行设计。就目前来讲,最佳放置图片资源的文件夹就是drawable-xxhdpi。那么有的朋友可能会问了,不是还有更高密度的drawable-xxxhdpi吗?干吗不放在这里?这是因为,市面上480dpi到640dpi的设备实在是太少了,如果针对这种级别的屏幕密度来设计图片,图片在不缩放的情况下本身就已经很大了,基本也起不到节省内存开支的作用了。好的,关于drawable微技巧方面的探索我们就讲到这里,本篇文章中也是集合了不少我平时的工作经验总结,以及通过做试验所得出的一些结论,相信还是可以给大家带来不少帮助的。后面我会抓紧时间继续准备新系列的内容,敬请期待。
阅读(...) 评论()

我要回帖

更多关于 android getdrawable 的文章

 

随机推荐