Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android知識梳理之屏幕適配全攻略

Android知識梳理之屏幕適配全攻略

編輯:關於Android編程

引言: 我相信Android碎片化問題是讓所有的Android開發者都比較頭疼的問題.尤其是屏幕適配這一塊兒.想要自己的app在不同的設備上面都有一個比較好的顯示效果.就必須做好相應的屏幕適配.本文是結合網上的相關知識總結、官方文檔結合自己的一些理解來進行闡述的.如有不恰當的地方,歡迎斧正.共同學習.

我們先來了解兩個概念:屏幕尺寸屏幕的分辨率:

 

屏幕尺寸: 就是屏幕的對角線的長度,度量單位是英寸,1英寸等於2.54厘米. 例如小米5的屏幕尺寸就為5.15英寸.nexus 5的屏幕為4.95英寸. \

 

 

屏幕分辨率: 實際上就是屏幕橫縱坐標上面的像素點.如比較常見的1280×720,1920×1080,480*800等等. 那麼問題來了:各家廠商生產的手機的分辨率和屏幕的尺寸都不盡相同.那麼我們做做出來的app需要在成千上百的不同尺寸不同分辨率的手機上面運行肯定也是會出現顯示差異問題.這裡將問題歸為兩類:app在不同的分辨率顯示出相同的效果.app在不同的屏幕尺寸上顯示相同的效果.   一、解決分辨率碎片化的問題。 我們知道。如果在一塊兒大小相同(尺寸)的屏幕上面顯示一個寬度為100px的控件,如果在1080*1920分辨率的屏幕上面顯示的效果和在分辨率為720*1280分辨率肯定是不會相同的. 那麼在相同尺寸但是分辨率不同的屏幕上面如何讓控件的大小看起來是一樣的呢?這個就是我們先要解決的問題. 首先來了解一些相關的概念: 屏幕像素密度:就是每英寸的像素數,又叫ppi,計算方式如下圖: \

 

 

Px像素:一塊顯示屏是由很多的光點組成的,每一個光點就是一個像素。例如1280x720屏幕的像素就是921600

 

dpi: dot per inch,就是每英寸的點數。在電子顯示范疇內它和PPI是一個意思計算方式也是和ppi的計算方式是一樣的。 只有在打印時這個縮寫才有意義,在打印領域不存在 PPI的叫法,只說DPI,它表示打印機每英寸打印幾個像素點。寬高同樣像素下,dpi越大,打印出來的圖案越小。

dip:是Density Independent Pixels的縮寫,或者叫dp,這是Android開發中特有的一種度量,稱作屏幕無關像素, 它不表示任何具體的長度或者像素點。顯示的時候根據具體平台屏幕密度的不同最終轉換為相應的像素長度,具體轉換規則是: 1dp =(目標屏幕密度/標准密度)*px,標准密度為160dpi,例如,1dp長度在密度為160dpi的平台表示一個像素的長度,而在240dpi的平台則表示1.5個像素的長度dpi是屏幕像素密度,假如一英寸裡面有160個像素,這個屏幕的像素密度就是160dpi,那麼在這種情況下,dp和px如何換算呢?在Android中,規定以160dpi為基准,1dip=1px,如果密度是320dpi,則1dip=2px,以此類推。與像素的換算方式為px=dp*(dpi/160)。 \

官方文檔自備梯子查看:http://developer.android.com/guide/practices/screens_support.html

 

統一化密度 我們先來看一個例子:nexus one,分辨率是480x800屏幕的大小是3.7英寸,我們可以根據上面的公式計算出它的dpi是:約為252,如果按照像素計算公式px=dp*(dpi/160)可得:1.575dp=1px 而屏幕的寬是480px,由此我們可以計算出寬度是約為304個dp.現在我們做一個測試:新建一個View設置背景顏色為紅色:寬度為304dp,按照道理來說是應該填滿了整個屏幕的寬度,但是實際情況是怎麼樣的呢?我們來看看效果: \

 

 

你可能會說wtf.那麼這是怎麼一回事呢?這裡我們就要引入一個統一化密度。 統一化密度:歸一化的密度是有固定值的, 這個固定值是120dpi(ldpi) , 160dpi(mdpi), 240dpi(hdpi), 320dpi(xhdpi), 480dpi(xxhdpi)。Android中計算像素經常使用的密度是這五個值之一; 就上面的例子來說我們計算出nexus one的dpi是252,Android系統裡面取的是240dpi,我們再根據公式px=dp*(240/160)可得:1.5dp=1px,再將View的寬度設置為320dp,可以看到如下效果: \

 

 

結論:dp可以自適應屏幕的密度。不管屏幕密度怎樣變化,只要屏幕的物理尺寸不變,實際顯示的尺寸就不會變化。在低密度的設備上它的值很小,而在高密度的屏幕上它的值會變得大些,這樣一來,開發者就可以指定一個長度從而適應不同密度的屏幕,以解決控件在低密度屏幕變大,高密度屏幕變小的問題.如果屏幕物理不變的情況下設置同一個dp值在480p或者是720p或者是1080p上面的顯示長度幾乎是一樣的.DPI越高的屏幕,屏幕繪制1dp需要越多的像素,反之亦然。也就是說使用dp可以讓我們的app在大多數的屏幕上保持相近的呈現效果,從而也基本上解決了分辨率碎片化的問題。 (注意)實際上使用dp的話在不同的設備上面的,顯示的大小是相差無幾的:為什麼是相差無幾的呢?根據相面所說的統一化密度結合下面的例子就可以很好的理解。 例子: 手機三款:nexus s :4.0英寸 480*800 nexus one : 3.7英寸 480*800 nexus 5 : 4.95英寸1080*1920 根據上面的計算公式:我們可以很快的算出來:nexus s的dpi是 233.2380757937px/inch 我們可以很快的算出來:nexus s的dpi是 252.1492711283px/inch 我們可以很快的算出來:nexus 5的dpi是 445 px/inch nexus s和nuxus one他們都會歸一化到240dpi裡面對應的換算單位是1.5dp=1px,而nexus 5會歸一化到xxhdpi裡面,對應的換算單位是3dp=1px如果是顯示50dp的長度則: 100dp在nexus s上面顯示的實際像素是 100*1.5=150px 100dp在nexus one上面顯示的實際像素是 100*1.5=150px 100dp在nexus 5上面顯示的實際像素是 100*3=300px 100dp在nexus s顯示的物理像素的長度 150px /233 px/inch= 0.644inch 1.68656厘米 100dp在nexus one顯示的物理像素的長度 150px/252 px/inch= 0.595inch 1.5113 厘米 100dp在nexus 5上面顯示的物理像素的長度 300px/445px/inch = 0.6741inch 1.712214厘米 通過上面的例子我們可以看出來:在Android設備屏幕顯示時,雖然通過歸一化密度讓兩款實際密度不相同的手機都處在hdpi裡面,相同dp在兩款設備上面的實際像素是一樣的,但是因為兩台設備的實際物理dpi不一樣,所以相同的像素在最終的顯示尺寸上是有微弱差別的。當然這種微弱的差別我們基本可以忽略不計。

再來看一個例子。

通過上面的講解我們可以看到通過設置dp,可以做到在不同像素密度的手機屏幕上面顯示的控件大小幾乎是一樣的大小。但是如果屏幕的物理尺寸相差很大的話又會出現怎麼樣的情況呢?我們看看接下裡的例子: 有一台nexus 7的平板電腦.分辨率是1200*1920 屏幕的大小是7英寸,根據上面的公式我們可以算出它的dpi是:323;屬於xhdpi 另外的一台設備是nexus 5的dpi是 445 ;屬於xxhdpi. 我們定義一個寬度為120dp的View並設置背景顏色為紅色.分別取顯示在兩種設備上面. \

 

 

通過上面的圖我們可以看到盡管使用了dp使得120dp的View在兩種設備上的大小幾乎是一樣的.但是在nexus 5上面這個View占據了整個屏幕大小的三分之一,而在nexus 7二代上面卻只占到了屏幕寬度的五分之一。通過計算我們可以得知nexus 5的寬度是360dp而nexus 7二代的寬度是600dp.所以這也就很好的解釋了為什麼在nexus 5上面120dp占了屏幕寬度的三分之一而在nexus 7二代上面卻只占了五分之一. 通過這個例子我們也可以知道如果屏幕的物理尺寸相差很大的話使用dp就無能為力了.也就是所通過dp我們可以解決Android分辨率碎片化的問題,但是卻不能夠解決Android物理尺寸碎片化的問題. dp解決了同一數值在不同分辨率中展示相同尺寸大小的問題(即屏幕像素密度匹配問題),但卻沒有解決設備尺寸大小匹配的問題。(即屏幕尺寸匹配問題) 二、解決在不同尺寸設備上面的適配. 方案一:使用權重android:layout_weight,match_parent(填充父窗體)來達到初步的適配. 權重可簡單的看做是按百分比對控件進行分配.android允許layout_weight的值是小數或整數. 需要注意的一點就是權重只能在linerlayout中使用,如果想給兩個控件設置為寬度相同,Android推薦的做法是將兩個控件的屬性這麼設置:layout_width=0dp, layout_weight=1,不能設置控件包裹內容否則無法完成正確的適配。 例如:
  

\

方案二.使用限定符:

例如標准的7英寸的平板的大小是600dp,這樣我們可以通過最小尺寸限定來完成不同尺寸屏幕的適配.在res文件夾下面定義一個layout-sw600dp的文件夾,在裡面寫上一個例如activity_main的布局文件,當我們的app運行在款或者高超過600dp的設備上面的時候就會去加載layout-sw600dp的布局文件,而在普通的小尺寸設備上面的時候還是會去加載默認的布局. 先編寫res/layout/activity_main: 再編寫res/layout-sw600dp/activity_main:   
我們將該應用程序運行到nexus 5和nexus 7(平板設備)上面看看效果.
\

 

 

通過上面的例子我們可以看到,由於nexus5的最小寬高達不到600dp因此加載的布局是默認的布局.而nexus 7的最小寬高達到了600dp因此展示的是在layout-sw600dp文件夾裡面定義的布局文件. 這裡需要注意的是:最小寬度限定符僅適用於 Android 3.2 及更高版本 . 此方法也可以限定values資源文件的使用.例如values-sw600dp.那麼在程序運行在最小寬度或者是高度為600dp的設備上面時.就會去加載values-sw600dp裡面的dimens,color,string等相關資源. 另外我們如果需要對設備的橫豎屏適配不同的布局只需要添加layout-sw600dp-land(橫屏)和layout-sw600dp-point(豎屏)這兩個文件夾裡面書寫相關的布局文件就可以了. 方案三:代碼動態適配: 既然在代碼裡面可以獲取到屏幕的相關信息,那麼我也就可以利用代碼獲取到的屏幕相關信息給控件動態設置相關的尺寸信息來達到,屏幕的適配.我們可以在第一個activity啟動的時候去獲取到屏幕的長寬等相關參數.然後在布局文件裡面給控件的大小都設置成0dp或者是包裹內容(設置的數據其實沒有任何的意義,因為我們需要在代碼裡面重新去定義控件的大小). 步驟: 1.創建一個類,裡面包含兩個靜態變量即是屏幕的寬高信息.
public class Constant {
    public static int displayWidth;  //屏幕寬度
    public static int displayHeight; //屏幕高度
}
2.然後在第一個activity啟動的時候調用如下代碼獲取到屏幕的相關參數:
     mView = findViewById(R.id.v);
        DisplayMetrics displayMetrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
        Constant.displayWidth = displayMetrics.widthPixels;
        Constant.displayHeight = displayMetrics.heightPixels;
        startActivity(new Intent(MainActivity.this, ActivityTwo.class));     
使用動態布局的優缺點如下: 優點:靈活性好。在應用運行過程中,可以隨時去調整布局,例如:增加未加載的新控件,調整控件的位置等。
缺點:可見性差,維護非常的繁瑣。 方案四:使用百分比庫(官方自帶的) 步驟: 1.添加gradle支持.
compile 'com.android.support:percent:23.3.0'
2.提供了兩種布局供大家使用: PercentRelativeLayout、PercentFrameLayout,通過名字就可以看出,這是繼承自FrameLayout和RelativeLayout兩個容器類; 支持的屬性有:

 

layout_widthPercent、layout_heightPercent、

layout_marginPercent、layout_marginLeftPercent、

layout_marginTopPercent、layout_marginRightPercent、

layout_marginBottomPercent、layout_marginStartPercent、layout_marginEndPercent。

3.在使用的時候需要自定義命名空間.寫法如下:
    
<!--?xml version="1.0" encoding="utf-8"?-->
<relativelayout android:layout_height="match_parent" android:layout_width="match_parent" tools:context="com.dapeng.searchdemo.MainActivity" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools">
    <android.support.percent.percentrelativelayout android:layout_height="match_parent" android:layout_width="match_parent" xmlns:android="http://schemas.android.com/apk/res/android">
        <view android:background="#ff44aacc" android:id="@+id/top_left" android:layout_alignparenttop="true" android:layout_height="0dp" android:layout_width="0dp" app:layout_heightpercent="20%" app:layout_widthpercent="70%">
        <view android:background="#ffe40000" android:id="@+id/top_right" android:layout_height="0dp" android:layout_torightof="@id/top_left" android:layout_width="0dp" app:layout_heightpercent="10%" app:layout_marginleftpercent="5%" app:layout_widthpercent="10%">
        <view android:background="#ff00ff22" android:id="@+id/bottom" android:layout_below="@+id/top_left" android:layout_height="0dp" android:layout_width="match_parent" app:layout_heightpercent="80%">
方案五:使用第三方框架(鴻洋大神版) github地址:https://github.com/hongyangAndroid/AndroidAutoLayout 博客地址:http://blog.csdn.net/lmj623565791/article/details/49990941 使用步驟: 1.在gradle裡面引入該庫. 2.在清單文件中配置基礎參數(也就是UI設計人員給定的尺寸例如我的例子裡面是720x1280).
<!--?xml version="1.0" encoding="utf-8"?-->
<manifest package="com.dapeng.autolayout" xmlns:android="http://schemas.android.com/apk/res/android">
    <application android:allowbackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:name=".MyApplication" 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">
            </category></action></intent-filter>
        </activity>
        <meta-data android:name="design_width" android:value="720">
        
        <meta-data android:name="design_height" android:value="1280">
        
    </meta-data></meta-data></application><meta-data android:name="design_width" android:value="720"><meta-data android:name="design_height" android:value="1280"><meta-data android:name="design_width" android:value="720"><meta-data android:name="design_height" android:value="1280">

3.在布局文件裡面寫上UI設計師給定的尺寸參數.單位是px.
<!--?xml version="1.0" encoding="utf-8"?-->
<relativelayout android:layout_height="match_parent" android:layout_width="match_parent" tools:context="com.dapeng.autolayout.MainActivity" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools">
    <view android:background="@android:color/holo_blue_bright" android:layout_height="640px" android:layout_width="360px">
</view></relativelayout></pre>
4.讓activity去繼承AutoLayoutActivity就可以了.<br />
<pre class="brush:java;">
public class MainActivity extends AutoLayoutActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        getSupportActionBar().hide();
    }
}

如果不想讓自己的activity去繼承AutoLayoutActivity,可以在編寫布局文件時,將
  • LinearLayout -> AutoLinearLayout
  • RelativeLayout -> AutoRelativeLayout
  • FrameLayout -> AutoFrameLayout
目前支持下列屬性:
  • layout_width
  • layout_height
  • layout_margin(left,top,right,bottom)
  • pading(left,top,right,bottom)
  • textSize
  • maxWidth, minWidth, maxHeight, minHeight
注意:

默認使用的高度是設備的可用高度,也就是不包括狀態欄和底部的操作欄的,如果你希望拿設備的物理高度進行百分比化:可以在Application的onCreate方法中進行設置:

public class MyApplication extends Application
{
    @Override
    public void onCreate()
    {
        super.onCreate();
        AutoLayoutConifg.getInstance().useDeviceSize();
    }
}
補充知識: 1.圖片的適配: 默認的我們的app運行在哪個手機上面就會去加載默認的drawable-xxdpi文件夾下面的圖片文件,例如,nexus 5如果使用到了圖片就會去加載xxhdpi文件夾裡面的圖片文件. 但是我們不可能為每一套密度的屏幕都去制作一套圖片,這樣不但是增加了美工的工作量也使得我們的app的體積會過於臃腫.drawable-mdpi、drawable-hdpi、drawable-xhdpi、drawable-xxhdpi。在這些文件下提供的圖片大小最好是3:4:6:8:12。 在講解下面內容的時候我們先來講講圖片加載規則.舉個例子:現在有一個ldpi的手機屏幕,有一個應用在其上運行(假如只有ldpi、mdpi、hdpi還有drawable四個存放圖片的文件夾),並需要調用一個圖片a.png(在下文中用a來代替a.png)。Android系統會經歷以下流程: \
結論:根據實際的測量結果也是這樣如果在設備對用的dpi的drawable文件夾下面沒有對應的圖片,那麼首先是要去dpi最高的文件夾裡面找看是否有對應的圖片.如果有則按照相應的縮放比例進行縮放顯示,如果沒有則到低一級的dpi文件夾裡面取查找是否有對應的圖片.以此類推.如果在所有的比自己dpi都高的文件夾裡面都找不到對應的文件,則去比自己低一級dpi的文件夾裡面找相關的資源.如果還是找不到資源就去默認的drawable文件夾裡面找,如果drawable文件夾裡面還是沒有相關的資源那麼程序就會報錯.   2.mimaps文件夾的使用. 補充知識:在Android4.2以上的版本中,提供了對mipmaps的支持,說簡單點就是他能對bitmap進行縮放的時候減少一些性能的耗損。如果你用Andorid Studio開發Android程序會發現Android Studio自動幫 你創建了幾個mipmaps文件夾,你可將應用的啟動圖標放到不同的mipmaps文件夾中.所以說我們一般是可以將圖標等放到mipmaps文件夾下面.    
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved