業(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ī)上的顯示效果:


布局文件如下:
<?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%的位置。
- 基準(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%的位置
- 相對基準(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是如何化繁為簡的,請聽下回分析~~