在 Android 界面開(kāi)發(fā)過(guò)程中我們經(jīng)常需要查看頁(yè)面效果。在遠(yuǎn)古時(shí)期使用 eclipse 開(kāi)發(fā)的時(shí)候預(yù)覽是一件讓人頭疼的事,代碼和預(yù)覽界面不能同時(shí)出現(xiàn),需要切換 tab,好在 Android Studio 提供的編輯界面可以讓我們同時(shí)看到代碼和預(yù)覽圖,省去我們開(kāi)會(huì)切換的時(shí)間。(這是當(dāng)年驅(qū)使我把 eclipse 換成 Android Studio 的主要原因之一)
但是預(yù)覽界面提供的預(yù)覽比較弱,只能看一些簡(jiǎn)單的效果,稍微復(fù)雜的效果就需要運(yùn)行起來(lái)才能看到,當(dāng)工程變得復(fù)雜之后,構(gòu)建一次 app 的時(shí)間會(huì)比較長(zhǎng),如果編寫(xiě)復(fù)雜界面,需要調(diào)整好幾次的時(shí)候,花在構(gòu)建上的時(shí)間可能都要很久,這會(huì)在一定程度上影響開(kāi)發(fā)效率。相對(duì)于原生,react native 和 flutter 在這方面優(yōu)勢(shì)就很突出,只需要 reload 一下馬上就能看到更新,不用每次去重新構(gòu)建。雖然原生也有 instant run 的功能,但是開(kāi)啟之后可能會(huì)造成很多莫名其妙問(wèn)題,所以大部分時(shí)候都會(huì)關(guān)掉 instant run 保平安。
android tools 屬性
tools: 替換 android:
tools 屬性能幫助我們?cè)鰪?qiáng)預(yù)覽效果。比如 tools:text 能夠幫助我們預(yù)覽文本,它的效果跟 android:text 一樣,但是在實(shí)際運(yùn)行中并不起作用。eg. 我們?cè)诰帉?xiě) RecyclerView item 的時(shí)候需要預(yù)覽效果,但是因?yàn)槊總€(gè) item 的數(shù)據(jù)都不同,我們不能寫(xiě)死 android:text。如果我們想要預(yù)覽文本效果,我們可以在 xml 使用 android:text , 在提交代碼的時(shí)候刪掉,但是這樣會(huì)比較麻煩,也可能會(huì)忘記,這時(shí)候我們就可以使用 tools:text 來(lái)代替 android:text,預(yù)覽效果完全相同,并且我們即使不刪除代碼也不會(huì)對(duì)運(yùn)行效果造成影響,可以放心大膽的 push 代碼。

實(shí)際上大部分 android:xxx 的屬性都能替換成 tools:xxx,這樣我們就能在預(yù)覽的時(shí)候看到顯示效果,并且不用擔(dān)心打包的時(shí)候不小心把測(cè)試數(shù)據(jù)帶上去。
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/imageView"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription"
tools:src="@mipmap/ic_launcher" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
app:layout_constraintStart_toEndOf="@+id/imageView"
app:layout_constraintTop_toTopOf="@+id/imageView"
tools:text="Jon Snow"
tools:textColor="#000"
tools:textSize="20sp" />
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
app:layout_constraintStart_toStartOf="@+id/textView2"
app:layout_constraintTop_toBottomOf="@+id/textView2"
tools:text="I know nothing"
tools:textColor="#666"
tools:textSize="14sp" />
<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="24dp"
app:layout_constraintBottom_toBottomOf="@+id/imageView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/imageView"
tools:text="3/8/2020"
tools:textColor="#666"
tools:textSize="14sp" />
</androidx.constraintlayout.widget.ConstraintLayout>

tools:listitem
listitem 可能幫助我們預(yù)覽 RecyclerView 的樣式。通常情況下我們?cè)?xml 中加入 RecyclerView 控件,as 會(huì)生成一個(gè)默認(rèn)的預(yù)覽樣式.默認(rèn)的樣式很簡(jiǎn)單,我們不能看到實(shí)際的效果,需要運(yùn)行代碼才能看到。使用tools:listitem 可以幫助我們實(shí)現(xiàn)在編輯的時(shí)候預(yù)覽 RecyclerView 的效果。
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.main.MainFragment">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="@layout/test_item" />
</androidx.constraintlayout.widget.ConstraintLayout>

除了 tools:listitem,Android 還提供了另外的屬性幫助我們更好的預(yù)覽 RecyclerView。 tools:itemCount 可以設(shè)置預(yù)覽個(gè)數(shù),搭配 layoutManager 和 spanCount 我們還能預(yù)覽 GridLayoutManager 下的樣式。
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.main.MainFragment">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:itemCount="10"
tools:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
tools:listitem="@layout/test_avatar_item"
tools:spanCount="3" />
</androidx.constraintlayout.widget.ConstraintLayout>

其他屬性
除了上面介紹的屬性之外,tools 還有很多別的作用,比如 tools:ignore,tools:context,tools:showIn等,具體可以參考官方文檔
sample data
使用 tools 屬性后我們能夠在一定程度上增強(qiáng)我們的預(yù)覽效果,但是效果比較一般,比如預(yù)覽 RecyclerView 的數(shù)據(jù)時(shí),我們雖然可以使用 tools:listitem 和 tools:itemCount 設(shè)置預(yù)覽數(shù)據(jù),但是每個(gè) item 都一樣,數(shù)據(jù)看上去很假,跟實(shí)際使用的時(shí)候有一些出入。sample data 的出現(xiàn)能夠很好的幫我解決這個(gè)問(wèn)題,真正的實(shí)現(xiàn) make preview great again。我們可以使用 as 內(nèi)置的 sample data,也可以自定義數(shù)據(jù),數(shù)據(jù)會(huì)體現(xiàn)在預(yù)覽界面上,讓我們的預(yù)覽更接近實(shí)際運(yùn)行效果。
as 內(nèi)置 sample data
Android Studio 內(nèi)置了一些 sample data,我們可以直接使用。
item.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/imageView"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription"
tools:src="@tools:sample/avatars" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
app:layout_constraintStart_toEndOf="@+id/imageView"
app:layout_constraintTop_toTopOf="@+id/imageView"
tools:text="@tools:sample/full_names"
tools:textColor="#000"
tools:textSize="20sp" />
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
app:layout_constraintStart_toStartOf="@+id/textView2"
app:layout_constraintTop_toBottomOf="@+id/textView2"
tools:text="@tools:sample/us_phones"
tools:textColor="#666"
tools:textSize="14sp" />
<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="24dp"
app:layout_constraintBottom_toBottomOf="@+id/imageView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/imageView"
tools:text="@tools:sample/date/mmddyy"
tools:textColor="#666"
tools:textSize="14sp" />
</androidx.constraintlayout.widget.ConstraintLayout>
layout.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.main.MainFragment">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:itemCount="10"
tools:listitem="@layout/test_sample_data_item" />
</androidx.constraintlayout.widget.ConstraintLayout>

我們看一下預(yù)覽效果是不是比之前的好一些呢,每個(gè) item 都有不同的數(shù)據(jù),而且我們也不需要引入額外的測(cè)試數(shù)據(jù)。
除了上面用到的 avatar, name 之類(lèi)的,sample data 還有很多類(lèi)型,基本上能滿(mǎn)足我們的日程需求,具體含義可以參考官方文檔。

自定義 sample data
如果 Android Studio 提供的 sample data 還不能滿(mǎn)足你的需求,比如內(nèi)置的姓名只有英文,你一定要預(yù)覽中文名字,這時(shí)候可以自定義數(shù)據(jù)。
我們可以通過(guò) new -> Sample Data directory 來(lái)創(chuàng)建數(shù)據(jù)目錄。在 sampledata 下創(chuàng)建文本文件
mynames:
亞托克斯
阿貍
阿卡麗
阿利斯塔
阿木木
艾尼維亞
安妮
艾希
奧瑞利安·索爾
阿茲爾
巴德
布里茨
布蘭德
mynicknames
暗裔劍魔
九尾妖狐
暗影之拳
牛頭酋長(zhǎng)
殤之木乃伊
冰晶鳳凰
黑暗之女
寒冰射手
鑄星龍王
沙漠皇帝
星界游神
蒸汽機(jī)器人
復(fù)仇焰魂
在 xml 中使用 tools:text="@sample/mynicknames" 即可。

除了上面這種簡(jiǎn)單文本的定義,我們還可以使用 json 來(lái)定義數(shù)據(jù)。
loldata.json:
{
"comment": "lol data - loldata.json",
"data": [
{
"name": "亞托克斯",
"nickname": "暗裔劍魔"
},
{
"name": "阿貍",
"nickname": "九尾妖狐"
},
{
"name": "阿卡麗",
"nickname": "暗影之拳"
},
{
"name": "阿利斯塔",
"nickname": "牛頭酋長(zhǎng)"
},
{
"name": "阿木木",
"nickname": "殤之木乃伊"
},
{
"name": "艾尼維亞",
"nickname": "冰晶鳳凰"
}
]
}
在 xml 中使用 tools:text="@sample/loldata.json/data/name" 即可
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/imageView"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription"
tools:src="@sample/myimg" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
app:layout_constraintStart_toEndOf="@+id/imageView"
app:layout_constraintTop_toTopOf="@+id/imageView"
tools:text="@sample/loldata.json/data/name"
tools:textColor="#000"
tools:textSize="20sp" />
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
app:layout_constraintStart_toStartOf="@+id/textView2"
app:layout_constraintTop_toBottomOf="@+id/textView2"
tools:text="@sample/loldata.json/data/nickname"
tools:textColor="#666"
tools:textSize="14sp" />
<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="24dp"
app:layout_constraintBottom_toBottomOf="@+id/imageView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/imageView"
tools:text="@tools:sample/date/mmddyy"
tools:textColor="#666"
tools:textSize="14sp" />
</androidx.constraintlayout.widget.ConstraintLayout>
[圖片上傳失敗...(image-9091b2-1583572490719)]
效果是不是還不錯(cuò)呢
再說(shuō)點(diǎn)什么
google 一直在改進(jìn)編輯界面的功能和體驗(yàn),特別是 ConstraintLayout 推出之后,編輯和預(yù)覽的功能更加強(qiáng)大,現(xiàn)在 Google 在各個(gè)大會(huì)上演示 ConstraintLayout 功能的時(shí)候基本都使用拖拽,特別是 ConstraintLayout 2.0 推出 MotionLayout 之后,編輯的功能變得更加強(qiáng)大,能夠預(yù)覽各種炫酷的動(dòng)畫(huà)。這些都能很好幫助開(kāi)發(fā)者節(jié)省開(kāi)發(fā)時(shí)間, make preview great again!
代碼
https://github.com/LyCharlie/SampleDataDemo