關於android UI布局自適應,androidui自適應
原以為上篇就是農曆年到來前寫的最後一篇了,但是看來現在還是有必要繼續把看到的有用的記錄一下,算是比較基礎的了,以前沒怎麼關注。
言歸正傳,這篇主要說下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 intDEFAULT_DENSITY The reference density used throughout the system. floatdensity The logical density of the display. intheightPixels The absolute height of the display in pixels. floatscaledDensity A scaling factor for fonts displayed on the display. intwidthPixels The absolute width of the display in pixels. floatxdpi The exact physical pixels per inch of the screen in the X dimension. floatydpi The exact physical pixels per inch of the screen in the Y dimension. 構造方法摘要 DisplayMetrics() 方法摘要 voidsetTo(DisplayMetrics o) voidsetToDefaults() 從類 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
可以看到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來滿足我們的單一設計了。
以上理解是個人的理解,希望對此有研究的可以指正,理解算是似懂非懂的。