Android 屏幕适配
一、適配方式之dp
名詞解釋
分辨率:480*800,1280*720。表示物理屏幕區域內像素點的總和。(切記:跟屏幕適配沒有任何關系)
因為我們既可以把1280*720 的分辨率做到4.0 的手機上面。我也可以把1280*720 的分辨率做到5.0 英寸的手機上面,如果分辨率相同,手機屏幕越小清晰。
px:pixels 的意思,像素,就是屏幕中最小的一個顯示單元,是屏幕的物理像素點,與密度相關,密度大了,單位面積上的px 會比較多。
sp:與刻度無關的單位,同dip/dp 相似,會根據用戶的字體大小偏好來縮放,主要用于設置字體的大小
dpi:dpi :dots per inch,像素密度,即每英寸屏幕所擁有的像素數,像素密度越大,顯示畫面細節就越豐富。常見取值120,160,240,320,480
計算公式:像素密度=(長度像素數2+寬度像素數2)?????????????????????√/屏幕尺寸
注意:屏幕尺寸單位為英寸,例:分辨率為1280*720 屏幕寬度為6 英寸計算所得像素密度約等于245,屏幕尺寸指屏幕對角線的長度
在Android 手機中dpi 分類:
| ldpi | Resources for low-density (ldpi) screens (~120dpi) |
| mdpi | Resources for medium-density (mdpi) screens (~160dpi). (This is the baseline density.) |
| hdpi | Resources for high-density (hdpi) screens (~240dpi). |
| xhdpi | Resources for extra high-density (xhdpi) screens (~320dpi). |
在我們的Android 工程目錄中有如下drawable-*dpi 目錄,這些目錄是用來適配不同分辨率手機的。
Android 應用在查找圖片資源時會根據其分辨率自動從不同的文件目錄下查找(這本身就是Android 系統的適配策略),如果在低分辨的文件目錄中比如drawable-mdpi 中沒有圖片資源,其他目錄中都有,當我們將該應用部署到mdpi 分辨率的手機上時,那么該應用會查找分辨率較高目錄下的資源文件,如果較高分辨率目錄下也沒有資源則只好找較低目錄中的資源了。
常見手機屏幕像素及對應分別率級別:
- ldpi:320*240
- mdpi:480*320
- hdpi:800*480
- xhdpi:1280*720
- xxhdpi:1920*1080
dp 和px 之間的簡單換算關系:
- ldpi 的手機:1dp=0.75px
- mdpi 的手機:1dp=1.0px
- hdpi 的手機:1dp=1.5px
- xhdpi 的手機:1dp=2.0px
- xxhdpi 的手機:1dp=3.0px
Tips:根據上面的描述我們得出如下結論,對于mdpi 的手機,我們的布局通過dp 單位可以達到適配效果
dp 和px 之間的關系
Android 官方定義dip 等價于160dpi 屏幕下的一個物理像素點,即1dip=1px。轉換公式如下:
dip =(dpi/(160 像素/英寸))px = density px舉例來說,在240 dpi 的屏幕上,1dip 等于1.5px
density :直接翻譯的話也叫密度,但是他在Android 中特指dp 和px 的比例關系。常見取值1.5 ,1.0
dp:是dip(Density independent pixels) 的簡寫,指密度無關的像素。指一個抽象意義上的像素,程序用它來定義界面元素。一個與密度無關的,在邏輯尺寸上,與一個位于像素密度為160dpi 的屏幕上的像素是一致的。要把密度無關像素轉換為屏幕像素,可以用這樣一個簡單的公式:
pixels=dips*(density/160)舉個例子,在DPI 為240 的屏幕上,1 個DIP 等于1.5 個物理像素。
布局時最好使用dp 來定義我們程序的界面,因為這樣可以保證我們的UI 在各種分辨率的屏幕上都可以正常顯示。
/*** 根據手機的分辨率從px(像素) 的單位轉成為dp*/public static int px2dip(Context context, float pxValue) {final float scale = context.getResources().getDisplayMetrics().density;return (int) (pxValue / scale + 0.5f);}/*** 根據手機的分辨率從dip 的單位轉成為px(像素)*/public static int dip2px(Context context, float dpValue) {final float scale = context.getResources().getDisplayMetrics().density;return (int) (dpValue * scale + 0.5f);}二、適配方式之dimens
跟drawable 目錄類似的,在Android 工程的res 目錄下有values 目錄,這個是默認的目錄,同時為了適配不同尺寸手機我們可以創建一個values-1280x720 的文件夾,同時將dimens.xml 文件拷貝到該目錄下
在dimens.xml 中定義一個尺寸,如下所示
<resources><dimen name="width">160dp</dimen> </resources>在values-1280x720 目錄中的dimens.xml 中定義同樣的尺寸名稱,但是使用不同的尺寸,如下所示
<resources><dimen name="width">180dp</dimen> </resources>當我們在布局文件中使用長或者寬度單位時,比如下圖所示,應該使用@dimen/width 來靈活的定義寬度
<?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"android:orientation="vertical"><TextView android:layout_width="@dimen/width"android:layout_height="wrap_content"/></LinearLayout>Tips:在values-1280x720 中, 中間的是大寫字母X 的小寫形式x , 而不是加減乘除的乘號。如果我們在values-1280x720 中放置了dimens 常量,一定記得也將該常量的對應值在values 目錄下的dimens.xml 中放一份,因為該文件是默認配置,當用戶的手機不是1280*720 的情況下系統應用使用的是默認values 目錄中的dimens.xml
三、適配方式之layout
跟values 一樣,在Android 工程目錄中layout 目錄也支持類似values 目錄一樣的適配,在layout 中我們可以針對不同手機的分辨率制定不同的布局,如下圖所示
四、適配方式之java 代碼適配
為了演示用java 代碼控制適配的效果,因此假設有這樣的需求,讓一個TextView 控件的寬和高分別為屏幕的寬和高的一半。
我們新創建一個Android 工程,修改main_activity.xml,布局文件清單如下:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><!-- 當前控件寬高為屏幕寬度的各50% --><TextView android:id="@+id/tv"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="#000000"android:text="@string/hello_world"/> </RelativeLayout>在MainActivity.java 類中完成用java 代碼控制TextView 的布局效果,其代碼清單如下:
public class MainActivity extends Activity {private static final String tag = null;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//去掉titlerequestWindowFeature(Window.FEATURE_NO_TITLE);setContentView(R.layout.activity_main);//獲取TextView 控件TextView tv = (TextView) findViewById(R.id.tv);//找到當前控件的夫控件(父控件上給當前的子控件去設定一個規則)DisplayMetrics metrics = new DisplayMetrics();//給當前metrics 去設置當前屏幕信息(寬(像素)高(像素))getWindowManager().getDefaultDisplay().getMetrics(metrics);//獲取屏幕的高度和寬度Constant.srceenHeight = metrics.heightPixels;Constant.srceenWidth = metrics.widthPixels;//日志輸出屏幕的高度和寬度Log.i(tag, "Constant.srceenHeight = "+Constant.srceenHeight);Log.i(tag, "Constant.srceenWidth = "+Constant.srceenWidth);//寬高各50%RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(//數學角度上四舍五入(int)(Constant.srceenWidth*0.5+0.5),(int)(Constant.srceenHeight*0.5+0.5));//給tv 控件設置布局參數tv.setLayoutParams(layoutParams);}}其中Constant 類是一個常量類,很簡單,只有兩個常量用來記錄屏幕的寬和高,其代碼清單如下:
public class Constant {public static int srceenHeight;public static int srceenWidth; }五、適配方式之weight 權重適配
在控件中使用屬性android:layout_weight=”1”可以起到適配效果,但是該屬性的使用有如下規則:
- 只能用在線性控件中,比如LinearLayout。
- 豎直方向上使用權重的控件高度必須為0dp(Google 官方的推薦用法)
- 水平方向上使用權重的控件寬度必須為0dp(Google 官方的推薦用法)
六、屏幕適配的處理技巧
手機自適應主要分為兩種情況:橫屏和豎屏的切換,以及分辨率大小的不同。
橫屏和豎屏的切換
Android 應用程序支持橫豎屏幕的切換,Android 中每次屏幕的切換動會重啟Activity,所以應該在Activity銷毀(執行onPause()方法和onDestroy()方法)前保存當前活動的狀態;在Activity 再次創建的時候載入配置,那樣,進行中的游戲就不會自動重啟了!有的程序適合從豎屏切換到橫屏,或者反過來,這個時候怎么辦呢?可以在配置Activity 的地方進行如下的配置android:screenOrientation=”portrait”(landscape 是橫向,portrait 是縱向)。這樣就可以保證是豎屏總是豎屏了。
而有的程序是適合橫豎屏切換的。如何處理呢?首先要在配置Activity 的時候進行如下的配置:
android:configChanges="keyboardHidden|orientation"另外需要重寫Activity 的onConfigurationChanged ()方法。實現方式如下:
@Overridepublic void onConfigurationChanged(Configuration newConfig){super.onConfigurationChanged(newConfig);if(this.getResources().getConfiguration().orientation==Configuration.ORIENTATION_LANDSCAPE){//TODO}elseif(this.getResources().getConfiguration().orientation==Configuration.ORIENTATION_PORTRAIT){//TODO}}分辨率大小不同
對于分辨率問題,官方給的解決辦法是創建不同的layout 文件夾,這就需要對每種分辨率的手機都要寫一個布局文件,雖然看似解決了分辨率的問題,但是如果其中一處或多處有修改了,就要每個布局文件都要做出修改,這樣就造成很大的麻煩。那么可以通過以下幾種方式解決:
1、使用layout_weight
目前最為推薦的Android 多屏幕自適應解決方案。
該屬性的作用是決定控件在其父布局中的顯示權重,一般用于線性布局中。其值越小,則對應的layout_width或layout_height 的優先級就越高(一般到100 作用就不太明顯了);一般橫向布局中,決定的是layout_width 的優先級;縱向布局中,決定的是layout_height 的優先級。
傳統的layout_weight 使用方法是將當前控件的layout_width 和layout_height 都設置成fill_parent,這樣就可以把控件的顯示比例完全交給layout_weight;這樣使用的話,就出現了layout_weight 越小,顯示比例越大的情況(即權重越大,顯示所占的效果越小)。不過對于2 個控件還好,如果控件過多,且顯示比例也不相同的時候,控制起來就比較麻煩了,畢竟反比不是那么好確定的。于是就有了現在最為流行的0px 設值法。看似讓人難以理解的layout_height=0px 的寫法,結合layout_weight,卻可以使控件成正比例顯示,輕松解決了當前Android 開發最為頭疼的碎片化問題之一。
2、清單文件配置
不建議使用這種方式,需要對不同的界面寫不同的布局
需要在AndroidManifest.xml 文件的元素如下添加子元素
<supports-screens android:largeScreens="true"android:normalScreens="true"android:anyDensity="true"android:smallScreens="true"android:xlargeScreens="true"></supports-screens>以上是為我們的屏幕設置多分辨率支持(更準確的說是適配大、中、小三種密度)。
Android:anyDensity="true"這一句對整個的屏幕都起著十分重要的作用,值為true,我們的應用程序當安裝在不同密度的手機上時,程序會分別加載hdpi,mdpi,ldpi 文件夾中的資源。相反,如果值設置為false,即使我們在hdpi,mdpi,ldpi,xdpi 文件夾下擁有同一種資源,那么應用也不會自動地去相應文件夾下尋找資源。而是會在大密度和小密度手機上加載中密度mdpi 文件中的資源。
有時候會根據需要在代碼中動態地設置某個值,可以在代碼中為這幾種密度分別設置偏移量,但是這種方法最好不要使用,最好的方式是在xml 文件中不同密度的手機進行分別設置。這里地圖的偏移量可以在values-xpdi,values-hpdi,values-mdpi,values-ldpi 四種文件夾中的dimens.xml 文件進行設置。
其他
說明:
- 在不同分辨率的手機模擬器下,控件顯示的位置會稍有不同
- 通過在layout 中定義的布局設置的參數,使用dp(dip),會根據不同的屏幕分辨率進行適配
- 但是在代碼中的各個參數值,都是使用的像素(px)為單位的
技巧:
1、盡量使用線性布局,相對布局,如果屏幕放不下了,可以使用ScrollView(可以上下拖動)
ScrowView 使用的注意:
在不同的屏幕上顯示內容不同的情況,其實這個問題我們往往是用滾動視圖來解決的,也就是ScrowView;需要注意的是ScrowView 中使用layout_weight 是無效的,既然使用ScrowView 了,就把它里面的控件的大小都設成固定的吧。
2、指定寬高的時候,采用dip 的單位,dp 單位動態匹配
3、由于android 代碼中寫的單位都是像素,所有需要通過工具類進行轉化
4、盡量使用9-patch 圖,可以自動的依據圖片上面顯示的內容被拉伸和收縮。其中在編輯的時候,灰色區域是被拉伸的,上下兩個點控制水平方向的拉伸,左右兩點控制垂直方向的拉伸
百分比布局
百分比布局github 地址
支持百分比設置的布局有百分比相對布局PercentRelativeLayout、百分比線性布局PercentLinearLayout、
百分比幀布局PercentFrameLayout,它們的使用與之前的常用布局使用類似。
百分比相對布局PercentRelativeLayout 中包含了3 個view 控件,前2 個view 是顯示在第三個view 上面,第一個view 設置了父控件高度的百分之二十,父控件寬度的百分之七十,第二個view 顯示在第一個view 的右邊并設置了父控件高度的百分之二十,父控件寬度的百分之三十。這樣前2個view 的剛好顯示整個屏幕的寬度,屏幕高度的百分之二十。第三個view 設置了父控件高度的百分之八十,寬度為整個屏幕。這樣百分比就設置好了,運行效果如圖所示。
因為百分比支持庫是隨著Android Support Library 23 一起的,如果要使用請確保你已經在SDKManager 中的Android Support Library 更新了最新的版本。然后在build.gradle 文件中添加下面這樣的依賴:
compile 'com.android.support:percent:23.0.0'布局文件
<android.support.percent.PercentRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:layout_width="match_parent"android:layout_height="match_parent"><View android:id="@+id/top_left"android:layout_width="0dp"android:layout_height="0dp"android:layout_alignParentTop="true"android:background="#ff44aacc"app:layout_heightPercent="20%"app:layout_widthPercent="70%" /><View android:id="@+id/top_right"android:layout_width="0dp"android:layout_height="0dp"android:layout_alignParentTop="true"android:layout_toRightOf="@+id/top_left"android:background="#ffe40000"app:layout_heightPercent="20%"app:layout_widthPercent="30%" /><View android:id="@+id/bottom"android:layout_width="match_parent"android:layout_height="0dp"android:layout_below="@+id/top_left"android:background="#ff00ff22"app:layout_heightPercent="80%" /> </android.support.percent.PercentRelativeLayout>動態加載布局
使用限定符
屏幕大小,layout-large
| small | 小屏幕 |
| normal | 中等屏幕 |
| large | 大屏幕 |
| xlarge | 超大屏幕 |
分辨率
| ldpi | 低分辨率,120dpi以下 |
| mdpi | 中等分辨率,120~160dpi |
| hdpi | 高分辨率,160~240dpi |
| xhdpi | 超高分辨率,240~320dpi |
| xxhdpi | 超超高分辨率,320~480dpi |
方向
| land | 橫屏 |
| port | 豎屏 |
最小寬度限定符,layout-sw600dp
總結
以上是生活随笔為你收集整理的Android 屏幕适配的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java高并发编程:原子类
- 下一篇: Notification详解