android屏幕適配總結

內容簡介.png

Android系統碎片化、Android機型屏幕尺寸碎片化、Android屏幕分辨率碎片化造成同一元素在不同手機上顯示的效果不同,也就是我們要適配的根本原因。這里主要是對不同屏幕尺寸和不同屏幕密度的適配。

基礎

屏幕尺寸:手機對角線的長度,單位是英寸,1英寸=2.54cm

屏幕分辨率:手機橫向和縱向上的像素點數,單位是px,1px就是一個像素,一般表示形式:縱向像素*橫向像素,如1920 * 1080

屏幕像素密度:每英寸的像素點數,單位是dpi,"dot per inch"的縮寫

屏幕像素密度計算公式.png

我的手機是魅藍max,屏幕分辨率:1920 * 1080,屏幕尺寸:6.0,計算屏幕像素密度:

屏幕像素密度= √(1920^2 + 1080^2)/ 6.0 = 367.15 dpi,手機官方:368dpi。

dip、dp、sp

dip與dp是一個意思,稱為密度無關像素,android手機專屬的單位,與px有什么關系呢?在android中,規定以160dpi為基準,也就是在手機屏幕像素密度為160的時候,1dp = 1px,然后其他的屏幕像素密度之間需要進行比例換算。比如屏幕像素密度為320dpi時,就這么計算,1dp / 160 dpi * 320 dpi = 2 px.

sp是獨立比例像素,可以根據文字大小首選項自動進行縮放,最好用復數,

mdpi hdpi xhdpi xxdpi xxxdpi用來區分不同的像素密度

名稱 像素密度范圍
mdpi 120dpi~160dpi
hdpi 160dpi~240dpi
xhdpi 240dpi~320dpi
xxhdpi 320dpi~480dpi
xxxhdpi 480dpi~640dpi

圖片像素換算比例:
mdpi:hdpi:xhdpi:xxhdpi:xxxhdpi = 2:3:4:6:8

根據要適配的機型的屏幕像素密度切圖,將圖標放入相應像素密度的文件夾中。

屏幕像素匹配

做前端的的朋友都不會遇到像android開發猿們遇到的這種適配問題,因為有一種百分比的東西。那我們也可以使用百分比來做適配,假如我們以480 * 320作為基準,那么在480 * 320像素的手機上顯示一張圖片,這時候1px就是一像素,用x1表示,x表示為寬,1表示1像素,也就是x1=1px;如果應用安裝在960 * 640的手機上的話,我們想讓應用呈現出與480 * 320一樣的效果,那么這張圖片的寬高都必須乘以2才行??梢蚤喿x洋哥原文


/**
 * email:naildingmouren@gmail.com
 * 針對各種屏幕密度的匹配
 * 在480*320的設備上,x2就代表2.0px,y2就代表3.0px。這里以320*480為基準
 * 在800×480的設備上,x2就代表3.0px,y2就代表3.33px。
 */

public class MakeXml {
    //項目中res文件夾的路徑
    private final static String rootPath = "D:\\AndroidSampleAll\\app\\src\\main\\res\\values-{0}x{1}\\";
    //手機屏幕像素的基準
    private final static float dw = 320f;
    private final static float dh = 480f;

    private final static String WTemplate = "<dimen name=\"x{0}\">{1}px</dimen>\n";
    private final static String HTemplate = "<dimen name=\"y{0}\">{1}px</dimen>\n";

    public static void main(String[] args) {
        makeString(320, 480);
        makeString(480,800);
        makeString(480, 854);
        makeString(540, 960);
        makeString(600, 1024);
        makeString(720, 1184);
        makeString(720, 1196);
        makeString(720, 1280);
        makeString(768, 1024);
        makeString(800, 1280);
        makeString(1080, 1812);
        makeString(1080, 1920);
        makeString(1440, 2560);
    }

    public static void makeString(int w, int h) {

        StringBuffer sb = new StringBuffer();
        sb.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
        sb.append("<resources>");
        float cellw = w / dw;
        for (int i = 1; i < 320; i++) {
            sb.append(WTemplate.replace("{0}", i + "").replace("{1}",
                    change(cellw * i) + ""));
        }
        sb.append(WTemplate.replace("{0}", "320").replace("{1}", w + ""));
        sb.append("</resources>");

        StringBuffer sb2 = new StringBuffer();
        sb2.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
        sb2.append("<resources>");
        float cellh = h / dh;
        for (int i = 1; i < 480; i++) {
            sb2.append(HTemplate.replace("{0}", i + "").replace("{1}",
                    change(cellh * i) + ""));
        }
        sb2.append(HTemplate.replace("{0}", "480").replace("{1}", h + ""));
        sb2.append("</resources>");

        String path = rootPath.replace("{0}", h + "").replace("{1}", w + "");
        File rootFile = new File(path);
        if (!rootFile.exists()) {
            rootFile.mkdirs();
        }
        File layxFile = new File(path + "lay_x.xml");
        File layyFile = new File(path + "lay_y.xml");
        try {
            PrintWriter pw = new PrintWriter(new FileOutputStream(layxFile));
            pw.print(sb.toString());
            pw.close();
            pw = new PrintWriter(new FileOutputStream(layyFile));
            pw.print(sb2.toString());
            pw.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

    }

    public static float change(float a) {
        int temp = (int) (a * 100);
        return temp / 100f;
    }
}

屏幕像素密度適配.png

屏幕尺寸匹配

布局匹配

  • 為了 使布局元素自適應屏幕尺寸,禁用絕對布局(AbsoluteLayout),建議使用相對布局(RelativeLayout),其他布局根據需求選擇。相對布局通過相對位置排列,屏幕大小變化也是沒有影響的
  • 為了可以根據屏幕的配置或設備來加載相應的布局(適配平板電腦、電視tv時),使用限定符來實現。
* 在平板電腦和電視的屏幕(>7英寸)上:實施“雙面板”模式以同時顯示更多內容
* 在手機較小的屏幕上:使用單面板分別顯示內容

限定符.png

尺寸限定符只適合android 3.2之前,large不能具體指定多大,所以android 3.2以后引入了最小寬度限定符,單位是dp。為了后期的維護,引入了“布局別名”的機制,同時維護sw600dp和large的兩套xml平板布局,解決重復的問題。

取以下例子:

小屏幕, 豎屏: 單面板
小屏幕, 橫屏: 單面板
7 英寸平板電腦,縱向:單面板,帶操作欄
7 英寸平板電腦,橫向:雙面板,寬,帶操作欄
10 英寸平板電腦,縱向:雙面板,窄,帶操作欄
10 英寸平板電腦,橫向:雙面板,寬,帶操作欄
電視,橫向:雙面板,寬,帶操作欄

res/layout/目錄下的xml資源:

res/layout/onepane.xml:(單面板)

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    android:orientation="vertical"  
    android:layout_width="match_parent"  
    android:layout_height="match_parent">  
  
    <fragment android:id="@+id/headlines"  
              android:layout_height="fill_parent"  
              android:name="com.example.android.newsreader.HeadlinesFragment"  
              android:layout_width="match_parent" />  
</LinearLayout>

res/layout/onepane_with_bar.xml:(單面板帶操作欄)

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">
    <!--操作欄-->
    <LinearLayout android:layout_width="match_parent"
                  android:id="@+id/linearLayout1"
                  android:gravity="center"
                  android:layout_height="50dp">
        <ImageView android:id="@+id/imageView1"
                   android:layout_height="wrap_content"
                   android:layout_width="wrap_content"
                   android:src="@mipmap/ic_launcher_round"
                   android:paddingRight="30dp"
                   android:layout_gravity="left"
                   android:layout_weight="0" />
        <View android:layout_height="wrap_content"
              android:id="@+id/view1"
              android:layout_width="wrap_content"
              android:layout_weight="1" />
        <Button android:id="@+id/categorybutton"
                android:background="@android:color/holo_red_light"
                android:layout_height="match_parent"
                android:layout_weight="0"
                android:layout_width="120dp"
                android:text="BUTTON"
                />
    </LinearLayout>

    <fragment android:id="@+id/headlines"
              android:layout_height="fill_parent"
              android:layout_width="match_parent" />
</LinearLayout>


res/layout/twopanes.xml:(雙面板,寬布局)

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal">
    <fragment android:id="@+id/headlines"
              android:layout_height="fill_parent"
              android:name="com.example.android.newsreader.HeadlinesFragment"
              android:layout_width="400dp"
              android:layout_marginRight="10dp"/>
    <fragment android:id="@+id/article"
              android:layout_height="fill_parent"
              android:name="com.example.android.newsreader.ArticleFragment"
              android:layout_width="fill_parent" />
</LinearLayout>
res/layout/twopanes_narrow.xml:(雙面板,窄布局)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal">
    <fragment android:id="@+id/headlines"
              android:layout_height="fill_parent"
              android:name="com.example.android.newsreader.HeadlinesFragment"
              android:layout_width="200dp"
              android:layout_marginRight="10dp"/>
    <fragment android:id="@+id/article"
              android:layout_height="fill_parent"
              android:name="com.example.android.newsreader.ArticleFragment"
              android:layout_width="fill_parent" />
</LinearLayout>

系統根據設備的情況自動加載布局

res/values/layouts.xml:(默認布局)

<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--默認布局
    bool 可以獲取boolean值動態判斷當前適配的布局
-->
    <item name="main_layout" type="layout">@layout/onepane</item>
    <bool name="has_two_panes">false</bool>

</resources>
res/values-sw600dp-land/layouts.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--大屏  橫向、雙面板、寬  android3.2后-->
    <item name="main_layout" type="layout">@layout/twopanes</item>
    <bool name="has_two_panes">true</bool>
</resources>
res/values-sw600dp-port/layouts.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--大屏、縱向、單面板帶操作欄  android3.2后-->
    <item name="main_layout" type="layout">@layout/onepane_with_bar</item>
    <bool name="has_two_panes">false</bool>
</resources>
res/values-large-land/layouts.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--大屏、橫向、雙面板、寬  android3.2前-->
    <item name="main_layout" type="layout">@layout/twopanes</item>
    <bool name="has_two_panes">true</bool>
</resources>
res/values-large-port/layouts.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--大屏 縱向 單面板帶操作欄  android3.2前-->
    <item name="main_layout" type="layout">@layout/onepane_with_bar</item>
    <bool name="has_two_panes">false</bool>
</resources>



實現自適應用戶界面流程(緊接限定符實現的布局適配)

以上的布局匹配設置好以后,我們要實現自適應的用戶界面流程,因為設備特點不同的話,就就會加載不同的布局,相應的用戶界面流程可能會有所不同。例如,如果應用處于雙面板模式下,點擊左側面板上的項即可直接在右側面板上顯示相關內容;而如果該應用處于單面板模式下,點擊相關的內容應該跳轉到另外一個Activity進行后續的處理。所以我們應該按照下面的流程,一步步的完成自適應界面的實現。

1.確定當前布局

由于每種布局的實施都會稍有不同,因此我們需要先確定當前向用戶顯示的布局。例如,我們可以先了解用戶所處的是“單面板”模式還是“雙面板”模式。要做到這一點,可以通過查詢指定視圖是否存在以及是否已顯示出來。

public class NewsReaderActivity extends FragmentActivity {
    boolean mIsDualPane;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_layout);

        View articleView = findViewById(R.id.article);
        mIsDualPane = articleView != null && articleView.getVisibility() == View.VISIBLE;
    }
}

2.根據當前布局作出響應

有些操作可能會由于當前布局而產生不同的操作結果。例如,在新聞閱讀器示例中,如果用戶界面處于雙面板模式下,那么點擊標題列表中的標題就會在右側面板中打開相應報道;但如果用戶界面處于單面板模式下,那么上述操作就會啟動一個獨立活動。

//自定義的點擊回調
@Override
public void onHeadlineSelected(int index) {
    mArtIndex = index;
    if (mIsDualPane) {
        /*在右邊面板直接顯示相應的內容,雙面板 */
        mArticleFragment.displayArticle(mCurrentCat.getArticle(index));
    } else {
        /* 開啟一個新的activity來顯示內容,單面板 */
        Intent intent = new Intent(this, ArticleActivity.class);
        intent.putExtra("catIndex", mCatIndex);
        intent.putExtra("artIndex", index);
        startActivity(intent);
    }
}

3.處理屏幕旋轉變化

如果我們使用獨立Activity實施界面的獨立部分,那么請注意,我們可能需要對特定配置變化(例如屏幕方向的變化)做出響應,以便保持界面的一致性。

布局組件匹配

為了讓布局組件自適應屏幕尺寸。
LinearLayout中權重屬性的使用可以通過百分比的形式對組件進行空間匹配。

圖片資源匹配

為了讓圖片資源在不同屏幕密度上顯示相同的像素效果,可以使用.9圖,可以自動拉伸圖。


參考:Android 屏幕適配:最全面的解決方案

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,663評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,125評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 175,506評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,614評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,402評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,934評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,021評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,168評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,690評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,596評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,784評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,288評論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,027評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,404評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,662評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,398評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,743評論 2 370

推薦閱讀更多精彩內容