关于android UI布局自适应

时间:2020-12-10 05:28:35

                    原以为上篇就是农历年到来前写的最后一篇了,但是看来现在还是有必要继续把看到的有用的记录一下,算是比较基础的了,以前没怎么关注。

                    言归正传,这篇主要说下android的自适应的一点东西,为什么会忽然想起来这个,主要还是因为之前看launcher的代码,其中看到的这段代码始终不明白,所以查了下资料。看下代码,

                    

				BitmapDrawable bitmapDrawable = (BitmapDrawable) icon;
				Bitmap bitmap = bitmapDrawable.getBitmap();
				if (bitmap.getDensity() == Bitmap.DENSITY_NONE) {
					bitmapDrawable.setTargetDensity(context.getResources()
							.getDisplayMetrics());

                    上面代码是出自Launcher2源码中Utilities.java中的static Bitmap createIconBitmap(Drawable icon, Context context),那么首先来看Bitmap.DENSITY_NONE这个参数的意义,查看了农民伯伯的译文说明,可以知道这个参数的说明是这样的:标志着该位图是以未知的像素密度创建的。所以很容易就猜测出下面的代码意思了,无非就是如果是按未知像素密度创建的位图后面会将bitmapDrawable的像素密度设置为当前屏幕的像素密度,也就是显示时创建位图的像素密度会是当前的屏幕像素密度。

                    但是对于DisplayMetrics我却是知之甚少了,基本可以说仅仅知道是获取屏幕分辨率的而已,于是火速查资料。

                    下面是API的原文说明了,我就直接贴了

                    

android.util
  类 DisplayMetrics
     java.lang.Object
  继承者 android.util.DisplayMetrics
          public class DisplayMetrics
                extends Object
                A structure describing general information about a display, such as its size, density, and font scaling.

字段摘要
          static int	DEFAULT_DENSITY 
                The reference density used throughout the system.
          float	density 
                The logical density of the display.
          int	heightPixels 
                The absolute height of the display in pixels.
          float	scaledDensity 
                A scaling factor for fonts displayed on the display.
          int	widthPixels 
                The absolute width of the display in pixels.
          float	xdpi 
                The exact physical pixels per inch of the screen in the X dimension.
          float	ydpi 
                The exact physical pixels per inch of the screen in the Y dimension.
 
构造方法摘要
          DisplayMetrics() 
           
 
方法摘要
          void	setTo(DisplayMetrics o) 
           
          void	setToDefaults() 
           
 
从类 java.lang.Object 继承的方法
              equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 
字段详细信息
     DEFAULT_DENSITY
               public static final int DEFAULT_DENSITY
               The reference density used throughout the system.
另请参见:
     常量字段值
          widthPixels
          public int widthPixels
                The absolute width of the display in pixels.
          heightPixels
          public int heightPixels
                The absolute height of the display in pixels.
          density
          public float density
                The logical density of the display. This is a scaling factor for the Density Independent Pixel unit, where one DIP is one pixel on an approximately 160 dpi screen (for example a 240x320, 1.5"x2" screen), providing the baseline of the system's display. Thus on a 160dpi screen this density value will be 1; on a 106 dpi screen it would be .75; etc.
                This value does not exactly follow the real screen size (as given by xdpi and ydpi, but rather is used to scale the size of the overall UI in steps based on gross changes in the display dpi. For example, a 240x320 screen will have a density of 1 even if its width is 1.8", 1.3", etc. However, if the screen resolution is increased to 320x480 but the screen size remained 1.5"x2" then the density would be increased (probably to 1.5).

另请参见:
      DEFAULT_DENSITY
           scaledDensity
           public float scaledDensity
                A scaling factor for fonts displayed on the display. This is the same as density, except that it may be adjusted in smaller increments at runtime based on a user preference for the font size.
           xdpi
           public float xdpi
                The exact physical pixels per inch of the screen in the X dimension.
           ydpi
           public float ydpi
                The exact physical pixels per inch of the screen in the Y dimension.
构造方法详细信息
           DisplayMetrics
                public DisplayMetrics()
方法详细信息
           setTo
                public void setTo(DisplayMetrics o)
           setToDefaults
                public void setToDefaults()
             格式不怎么好,凑合看了,因为只是作为一个了解的内容所以只是看个大概就够了。我的理解是通过DisplayMetrics类我们能够获取的应该是关于屏幕显示的大部分信息,具体获取方式应该会比较灵活,像最开始贴的那段代码

            直接用context.getResource().getDisplayMetrics(),也可以先初始化一个DisplayMetrics对象,再获取,如这样

               DisplayMetrics metrics = new DisplayMetrics();

               getWindowManager().getDefaultDisplay().getMetrics(metrics );

            一般推荐是用后者,因为考虑的用法是在应用去调用,这样获取的宽高度是屏幕的宽高,但是如果按第一种调用方法同时又是在Activity的onCreate函数里调用,并且是在setContentView()前就调用,那么很可能资源文件都还没加载,那么调用getResource很可能会出问题。因此推荐第二种,因为既然需要已经开始初始化Activity那么窗口对象肯定已经创建好了,现在调用getWindowManager是不会出问题的,自然获取到的数据都是正确的了。第一种可以推荐在Acitivity创建成功后调用。

            简单代码如下

            

	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		DisplayMetrics metrics = new DisplayMetrics();
		getWindowManager().getDefaultDisplay().getMetrics(metrics);
		metrics.toString();
		Log.e("Mylog----------", metrics.toString());
......
}
             下面的是模拟器演示的log

               关于android UI布局自适应

            可以看到log中打印的信息,density = 1.0 width = 1024,height = 552 scaledDensity = 1.0 xdpi = 160 ydpi = 160  因为我所设置的模拟器分辨率为1024*600,具体会差48高度值,我个人认为是下面的navigation_bar的高度,但是也有点奇怪为什么会不计算在内。

             下面的log是我在真机上取的log,就是正常的了

              E/Mylog----------(29597): DisplayMetrics{density=1.0, width=1024, height=600, scaledDensity=1.0, xdpi=160.0, ydpi=160.0}

              简单的看下API的说明,还不是很懂,只是大概看的懂density这个值是一个逻辑显示密度,并且是一个独立的像素密度的比例数单位,它的标准值是以每平方英寸160个像素点,也就是所说的160dpi。并且这个值不会完全按照屏幕的真实尺寸变化,就像一个分辨率为240*320的屏幕,其density为1,但是它的尺寸宽度可以是1.8寸,也可以是1.3寸。通过这些可以看出,对于像素密度比例这个参数应该仅仅是取决于实际屏幕的像素密度,一旦这个值发生变化不再是1的时候,那么实际我们的布局UI如果说是按实际像素值单位来布局就会在不同的分辨率下显示不同的布局效果,无法做到适应多种分辨率。那么android也在后面直接推出了于分辨率无关的度量单位来支持这些自适应的布局

              px:像素的直接描述,直接对应图片的实际分辨率。即1px屏幕上一个实际的物理像素点。官方不推荐,因为不同分辨率下显示效果会有差异,无法根据实际屏幕像素密度自动调整

              dp:这个是官方推荐的可以根据屏幕像素密度进行调整的单位。网上有解释,我个人认为下面的解释是最合理的吧。它与“像素密度”有关,所以首先我们解释一下什么是像素密度。假设有一部手机,屏幕的物理尺寸为1.5英寸x2英寸,屏幕分辨率为240x320,则我们可以计算出在这部手机的屏幕上,每英寸包含的像素点的数量为240/1.5=160dpi(横向)或320/2=160dpi(纵向),160dpi就是这部手机的像素密度,像素密度的单位dpi是Dots Per Inch的缩写,即每英寸像素数量。横向和纵向的这个值都是相同的,原因是大部分手机屏幕使用正方形的像素点。  不同的手机/平板可能具有不同的像素密度,例如同为4寸手机,有480x320分辨率的也有800x480分辨率的,前者的像素密度就比较低。Android系统定义了四种像素密度:低(120dpi)、中(160dpi)、高(240dpi)和超高(320dpi),它们对应的dp到px的系数分别为0.75、1、1.5和2,这个系数乘以dp长度就是像素数。例如界面上有一个长度为“80dp”的图片,那么它在240dpi的手机上实际显示为80x1.5=120px,在320dpi的手机上实际显示为80x2=160px。如果你拿这两部手机放在一起对比,会发现这个图片的物理尺寸“差不多”,这就是使用dp作为单位的效果。

             dip:与dp完全相同,只是名字不同而已。在早期的Android版本里多使用dip,后来为了与sp统一就建议使用dp这个名字了。

             sp:与缩放无关的抽象像素(Scale-independent?Pixel)。sp和dp很类似但唯一的区别是,Android系统允许用户自定义文字尺寸大小(小、正常、大、超大等等),当文字尺寸是“正常”时1sp=1dp=0.00625英寸,而当文字尺寸是“大”或“超大”时,1sp>1dp=0.00625英寸。类似我们在windows里调整字体尺寸以后的效果——窗口大小不变,只有文字大小改变。

             还有几个比较少用到的尺寸单位:

             mm:即毫米;  in:  即英寸,1英寸=2.54厘米(约);

             pt:1pt=1/72英寸=0.035厘米;  最佳实践,文字的尺寸一律用sp单位,非文字的尺寸一律使用dp单位。例如textSize="16sp"、layout_width="60dp";偶尔需要使用px单位,例如需要在屏幕上画一条细的分隔线时。

             通过以上的了解,也大概知道了android的UI布局的像素单位的一些设置原理,那么官方推荐的对于固定长度宽度需要设定的时候应该是尽量用与像素密度相关的dp单位来设定,而如果是希望以实际布局中的控件实际长宽来做自适应,那么对于长宽的设置应该尽可能的去采用wrap_content来设置,而不是强制的指定固定的长宽值即使是指定dp单位,也是很难完美展示自适应的这一优势的。但是我们也可以根据自身应用的特性,适当的用px和其他单位去使自己的应用更方便和布局更美观。

             总结了以上,实际对我来说很直观的引起我注意的还是我在做应用布局的时候,对比设计效果图我做出来的实际效果总感觉图片大小都是自适应或者直接指定于实际图片分辨率相同的dp值,效果却与设计图有出入,一直认为dp和像素是对等的,所以一直想不通,直到现在终于明白原来我们的dp值只有在density为1的时候才会是1dp=1px的效果,在这个值变化的时候就会造成实际设计效果和实际演示效果之间的出入,那么此时解决办法很可能就是直接指定固定像素值px来满足我们的单一设计了。

             以上理解是个人的理解,希望对此有研究的可以指正,理解算是似懂非懂的。