還需要多屏幕適配?—— ConstraintLayout(一)

業(yè)務(wù)場景


每當(dāng)你興高采烈的提測后,測試同事總是可以找到一個“奇怪”的手機(jī)讓你的布局文件不堪入目。多屏幕適配問題一直困擾著我們Android開發(fā)。

究其根本是因?yàn)闃?biāo)準(zhǔn)的UI設(shè)計(jì)圖遇到各種不標(biāo)準(zhǔn)的屏幕。
UI出圖一般是按照360dp * 640dp,但并不是所有手機(jī)的寬度都是360dp。
今日頭條的適配方案就是假裝讓所有手機(jī)的寬度都是360dp。
將 px 轉(zhuǎn)換成 dp 需要一個系數(shù),這個系數(shù)叫屏幕密度,公式如下:

dp = px / density
中文版公式:屏幕寬度dp值 = 屏幕寬度px值 / density
其中 density = DPI / 160

屏幕寬度 px 值是一個無法改變的常量,今日頭條通過代碼改變density以實(shí)現(xiàn)讓不同的手機(jī)屏幕寬度dp值和設(shè)計(jì)圖一致。

dp救不了你


dp的確比直接使用px有更好的適配效果,但無奈手機(jī)屏幕太多且尺寸跨度太大。下面兩張圖是同一份布局文件在不同分辨率手機(jī)上的顯示效果:


屏幕快照1.png

屏幕快照2.png

布局文件如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/cootek_383838"
    android:padding="16dp">

    <ImageView
        android:id="@+id/iv_close"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_marginTop="30dp" //通過上邊距來定位
        android:src="@drawable/activity_login_close" />

    <ImageView
        android:id="@+id/iv_icon"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_below="@id/iv_close"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="50dp"  //通過上邊距來定位
        android:src="@drawable/signin_ill_01" />

    <TextView
        android:id="@+id/tv_login"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/iv_icon"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="30dp" //通過上邊距來定位
        android:text="LOG IN TO CONTINUE"
        android:textColor="#ffffffff"
        android:textSize="18sp" />

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@id/tv_login"
        android:layout_marginTop="35dp"  //通過上邊距來定位
        android:paddingLeft="70dp"
        android:paddingRight="70dp">

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:orientation="vertical">

            <ImageView
                android:id="@+id/img_facebook"
                android:layout_width="100dp"
                android:layout_height="100dp"
                android:src="@drawable/login_facebook" />

            <TextView
                android:id="@+id/tv_facebook"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_horizontal"
                android:layout_marginTop="15dp"
                android:text="facebook"
                android:textColor="#ffffffff"
                android:textSize="14sp" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:orientation="vertical">

            <ImageView
                android:id="@+id/img_google"
                android:layout_width="100dp"
                android:layout_height="100dp"
                android:src="@drawable/login_google" />

            <TextView
                android:id="@+id/tv_google"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_horizontal"
                android:layout_marginTop="15dp"
                android:text="Google"
                android:textColor="#ffffffff"
                android:textSize="14sp" />
        </LinearLayout>
    </RelativeLayout>
</RelativeLayout>

這是一個垂直型布局,下方的控件通過設(shè)置上邊距來定位(UI圖給定上邊距值)。寫死的dp值只要遇到長度和UI設(shè)計(jì)圖不同的手機(jī)時(shí),就會出現(xiàn)適配問題。

百分比布局


是不是可以放棄用dp進(jìn)行布局?
如果用百分比來布局,適配問題是不是就迎刃而解了?

ConstraintLayout是2016年Google I/O推出的一個新組件,借助于它就能實(shí)現(xiàn)百分比布局。
ConstraintLayout實(shí)現(xiàn)垂直百分比布局的思路是 定義控件頂部在整個屏幕的百分比位置,比如控件頂部距離屏幕頂部64dp,而整個屏幕是640dp,則控件頂部應(yīng)該在屏幕10%的位置。

  1. 基準(zhǔn)線:
    為了實(shí)現(xiàn)這個效果,需要用到Guideline,它是一條隱形的線,它的位置可以通過百分比定位,代碼如下:
    <!--只有作為ConstraintLayout孩子時(shí)才有效-->
    <android.support.constraint.Guideline
        android:id="@+id/gl_top"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent=".1" />

其中
android:orientation="horizontal"表示它是一條水平線
app:layout_constraintGuide_percent=".1"表示這條水平線位于整個屏幕自頂向下10%的位置

  1. 相對基準(zhǔn)線布局
    有了基準(zhǔn)線后,只需要將控件相對于基準(zhǔn)線布局即可。ConstraintLayout擁有非常豐富的相對布局修飾詞,現(xiàn)在我們用到的是app:layout_constraintTop_toBottomOf="@id/gl_top",其語義是將本控件的頂部置于指定控件的底部,代碼如下:
<android.support.constraint.ConstraintLayout 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"
    android:background="@color/cootek_383838">

    <!--只有作為ConstraintLayout孩子時(shí)才有效-->
    <android.support.constraint.Guideline
        android:id="@+id/gl_top"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent=".1" />
    <ImageView
        android:id="@+id/iv_close"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/activity_login_close"
        app:layout_constraintTop_toBottomOf="@id/gl_top" />
</android.support.constraint.ConstraintLayout>

上述代碼實(shí)現(xiàn)了將ImageView置于屏幕縱向10%的位置

完整代碼


以下代碼是用百分比布局重對本文截圖的重新實(shí)現(xiàn):

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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"
    android:background="@color/cootek_383838">

    <!--只有作為ConstraintLayout孩子時(shí)才有效-->
    <android.support.constraint.Guideline
        android:id="@+id/gl_top"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent=".05" />

    <android.support.constraint.Guideline
        android:id="@+id/gl_logo"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent=".175" />

    <android.support.constraint.Guideline
        android:id="@+id/gl_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent=".514" />

    <android.support.constraint.Guideline
        android:id="@+id/gl_login_icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent=".596" />

    <android.support.constraint.Guideline
        android:id="@+id/gl_login_platform"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent=".731" />

    <android.support.constraint.Guideline
        android:id="@+id/gl_terms"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent=".896" />

    <android.support.constraint.Guideline
        android:id="@+id/gl_left"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_percent=".091" />

    <android.support.constraint.Guideline
        android:id="@+id/gl_right"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_percent=".909" />

    <ImageView
        android:id="@+id/iv_close"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/activity_login_close"
        app:layout_constraintEnd_toStartOf="@id/gl_right"
        app:layout_constraintTop_toBottomOf="@id/gl_top" />

    <ImageView
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:src="@drawable/signin_ill_01"
        app:layout_constraintDimensionRatio="1:1"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/gl_logo"
        app:layout_constraintWidth_percent=".497" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="LOG IN TO CONTINUE"
        android:textColor="#ffffffff"
        android:textSize="18sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/gl_title" />

    <ImageView
        android:id="@+id/img_facebook"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:src="@drawable/login_facebook"
        app:layout_constraintDimensionRatio="1:1"
        app:layout_constraintEnd_toStartOf="@id/img_google"
        app:layout_constraintHorizontal_chainStyle="spread"
        app:layout_constraintStart_toEndOf="@id/gl_left"
        app:layout_constraintTop_toBottomOf="@id/gl_login_icon"
        app:layout_constraintWidth_percent=".2" />

    <ImageView
        android:id="@+id/img_google"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:src="@drawable/login_google"
        app:layout_constraintDimensionRatio="1:1"
        app:layout_constraintEnd_toEndOf="@id/gl_right"
        app:layout_constraintHorizontal_chainStyle="spread"
        app:layout_constraintStart_toEndOf="@id/img_facebook"
        app:layout_constraintTop_toBottomOf="@id/gl_login_icon"
        app:layout_constraintWidth_percent=".2" />

    <TextView
        android:id="@+id/tv_facebook"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="facebook"
        android:textColor="#ffffffff"
        android:textSize="14sp"
        app:layout_constraintEnd_toEndOf="@id/img_facebook"
        app:layout_constraintStart_toStartOf="@id/img_facebook"
        app:layout_constraintTop_toBottomOf="@id/gl_login_platform" />

    <TextView
        android:id="@+id/tv_google"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Google"
        android:textColor="#ffffffff"
        android:textSize="14sp"
        app:layout_constraintEnd_toEndOf="@id/img_google"
        app:layout_constraintStart_toStartOf="@id/img_google"
        app:layout_constraintTop_toBottomOf="@id/gl_login_platform" />
</android.support.constraint.ConstraintLayout>

不止于此


ConstraintLayout的功能遠(yuǎn)不止于此,它巧妙的設(shè)計(jì)思想可以將復(fù)雜界面化繁為簡。細(xì)心的你一定發(fā)現(xiàn),上述布局文件沒有嵌套,所有控件都位于同一層次。
關(guān)于ConstraintLayout是如何化繁為簡的,請聽下回分析~~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容