ConstraintLayout2.0進(jìn)階之路-歡迎新同學(xué)
ConstraintLayout2.0針對布局來說,主要新增了兩類東西,一個是新增了VirtualLayouts,用于將一組View進(jìn)行關(guān)聯(lián)管理,并賦予定義的能力,另一個是放開了ConstraintHelper的限制。
國際慣例,官網(wǎng)鎮(zhèn)樓。
https://github.com/androidx/constraintlayout/wiki/What's-New-in-2.1
https://developer.android.com/training/constraint-layout
VirtualLayouts
ConstraintLayout1.1首次引入了Barriers和Guidelines這樣的ConstraintHelper,它們是創(chuàng)建了一個不可見的View,讓他管理了多個View的id引用,從而實現(xiàn)多View的整體布局,例如對齊等邏輯。而在ConstraintLayout2.0中,ConstraintLayout增加了類似Flow這樣的VirtualLayouts,它允許將某些行為同時作用到所有被引用的widget上,這實際上是對這類使用場景的一種約束范式,同時統(tǒng)一了實現(xiàn)方式。
在Constraint Layout中,VirtualLayouts作為virtual view group的角色參與約束和布局中,但是它們并不會作為視圖添加到視圖層級結(jié)構(gòu)中,而是僅僅引用其它視圖來輔助它們在布局系統(tǒng)中完成各自的布局功能。
Flow
Flow可以說是Chains的強(qiáng)化版,它是一種新的VirtualLayouts,可以用來構(gòu)建流式排版效果,當(dāng)出現(xiàn)空間不足的情況時,它能夠自動換行,或者是自動延展到屏幕的另一區(qū)域。當(dāng)你需要對多個View進(jìn)行流式布局,或者不確定其布局空間的實際尺寸時,就可以使用Flow來實現(xiàn)。借助官網(wǎng)上的一張圖,可以很方便的了解Flow的使用。
圖片Flow包含很多可以設(shè)置的屬性,下面通過一些例子來進(jìn)行講解。
wrapMode
這個屬性用于控制元素的排列方式,通過下面這個動圖,可以了解其布局的基本規(guī)則。
圖片Flow可以通過constraint_referenced_ids來獲取要引用的所有視圖,然后根據(jù)這些視圖創(chuàng)建一個虛擬的virtual view group,再對這些視圖進(jìn)行流式布局,示例代碼如下所示。
????android:layout_width="0dp"
????android:layout_height="wrap_content"
????app:constraint_referenced_ids="bt1,bt2,bt3"
????app:flow_wrapMode="aligned"
????app:layout_constraintEnd_toEndOf="parent"
????app:layout_constraintStart_toStartOf="parent"
????app:layout_constraintTop_toTopOf="parent"?/>
- NONE : 默認(rèn)模式。Views不會被包裹到另一行或列。如果它們在屏幕之外,則不能被看到。
- CHAIN : CHAIN與Chains非常相似,可以認(rèn)為是Chains的強(qiáng)化版本。CHAIN是根據(jù)orientation逐行或逐列進(jìn)行排列的,默認(rèn)的展示風(fēng)格是SPREAD。
- ALIGNED : ALIGNED模式與CHAIN類似,但鏈?zhǔn)绞窃趯⒉煌泻土械囊晥D對齊后考慮的,默認(rèn)的樣式是SPREAD。
Gap
Gap展示了Flow中每個元素直接的間隔,這個間隔包含horizontalGap和verticalGap兩種,你可以在原有Chain Style的基礎(chǔ)上進(jìn)行額外設(shè)置,示例代碼如下所示。
????android:layout_width="0dp"
????android:layout_height="wrap_content"
????app:constraint_referenced_ids="bt1,bt2,bt3"
????app:flow_horizontalGap="50dp"
????app:flow_verticalGap="50dp"
????app:flow_wrapMode="aligned"
????app:layout_constraintEnd_toEndOf="parent"
????app:layout_constraintStart_toStartOf="parent"
????app:layout_constraintTop_toTopOf="parent"?/>
Style
當(dāng)wrapMode設(shè)置為ALIGNED或CHAIN時,根據(jù)Flow的orientation,可以設(shè)置相應(yīng)的flow_verticalStyle和flow_horizontalStyle,此時,這個Style與Chains中的Style枚舉一樣,可以設(shè)置spread、spread_inside和packed,效果也和Chains的效果一樣。
- flow_firstHorizontalStyle:約束第一條水平鏈,當(dāng)有多條鏈(多行)時,只約束第一條鏈(第一行),其他鏈(其他行)不約束
- flow_lastHorizontalStyle:約束最后一條水平鏈,當(dāng)有多條鏈(多行)時,只約束最后一條鏈(最后一行),其他鏈(其他行)不約束
- flow_horizontalStyle:約束所有水平鏈
- flow_firstVerticalStyle:同水平約束
- flow_lastVerticalStyle:同水平約束
- flow_verticalStyle:約束所有垂直鏈
Bias
當(dāng)Style設(shè)置為Packed時,可以通過Bias來設(shè)置位移的權(quán)重,這個效果與Chains的效果也是一樣的。
Alignment
這里的Alignment指的是Flow方向的法向方向的對齊方式,如果指定Flow的方向,是不會生效的,例如Flow的orientation是vertical,那么設(shè)置flow_horizontalAlign才會生效。
maxElementsWrap
表示當(dāng)前方向上最大的元素數(shù)量,這個屬性在固定行或固定列數(shù)量的時候,是非常有用的。
在Component Tree界面,可以直接多選要創(chuàng)建Flow的Views,拖進(jìn)去自動生成constraint_referenced_ids。
演示
Flow是一個非常強(qiáng)大的布局工具,但是屬性也很多,所以初學(xué)者很難掌握,所以我寫了一個Demo,用于演示Flow的使用。
image代碼很簡單,詳細(xì)的可以查看 https://github.com/xuyisheng/ConstraintLayout2Demo
ConstraintHelper
ConstraintHelper是ConstraintLayout中的一個非常重要的部分,你可以認(rèn)為ConstraintHelper是一個用于記錄標(biāo)記Views的Helper,它并沒有創(chuàng)建新的布局,而是引用了指定的Views,并針對這個整體實現(xiàn)一些效果。
Layer
Layer作為一種新的輔助工具,可以認(rèn)為是Group的強(qiáng)化版,它可以讓你在多個視圖上創(chuàng)建一個虛擬的圖層。但是,與Flow不同的是,它并不會對視圖進(jìn)行布局操作,它的使用場景是對多個視圖同時進(jìn)行變換。
例如,你需要對多個視圖整體進(jìn)行旋轉(zhuǎn)、平移或縮放操作,再或者說是設(shè)置一組View的背景,那么就可以使用Layer。
圖片Layer在布局期間會調(diào)整大小,其大小會根據(jù)其引用的所有視圖進(jìn)行調(diào)整,你可以將Layer理解為一組View的邊界矩形范圍,通過Layer,可以很方便的拿到referenced_ids指定的View的邊界范圍,示例代碼如下所示。
????android:layout_width="0dp"
????android:layout_height="wrap_content"
????app:constraint_referenced_ids="bt1,bt2,bt3"
????app:flow_wrapMode="aligned"
????app:layout_constraintEnd_toEndOf="parent"
????app:layout_constraintStart_toStartOf="parent"
????app:layout_constraintTop_toTopOf="parent"?/>
借助Layer,可以很方便的給一組View設(shè)置背景色的功能,這個場景在ConstraintLayout1中,只能通過新增一個View并建立與要設(shè)置背景色的一組View的約束的方式來實現(xiàn),而現(xiàn)在,使用Layer就很方便了。
????android:layout_width="match_parent"
????android:layout_height="wrap_content"
????android:background="#80BEBEBE"
????app:constraint_referenced_ids="chipOne,chipTwo,chipThree,chipFour,chipFive,chipSix,chipSeven,chipEight"
????app:layout_constraintTop_toTopOf="parent"?/>
在Component Tree界面,可以直接多選要創(chuàng)建Layer的Views,拖進(jìn)去自動生成constraint_referenced_ids。
自定義ConstraintHelper
ConstraintHelper的三個使用場景。
- 輔助布局:創(chuàng)建一個新的布局方式,避免創(chuàng)建新的ViewGroup從而加深層級
- 修改布局:在布局完成后,修改布局效果
- 重新渲染:在View繪制完成后,對View進(jìn)行修改、重新渲染效果
常用回調(diào):
- init:初始化調(diào)用
- updatePreLayout:布局前更新
- updatePostLayout:布局后更新
- updatePostMeasure:測量后更新
- updatePostConstraints:更新約束
- onDraw:進(jìn)行繪制
自定義ConstraintHelper最基本的方式就是通過繼承ConstraintHelper來實現(xiàn),并通過重寫上面的一些回調(diào),來實現(xiàn)布局的修改,下面通過一個簡單的例子來演示下如何創(chuàng)建自定義ConstraintHelper,代碼如下所示。
class?CircularRevealHelper?@JvmOverloads?constructor(
????context:?Context,?attrs:?AttributeSet??=?null,?defStyleAttr:?Int?=?0
)?:?ConstraintHelper(context,?attrs,?defStyleAttr)?{
????override?fun?updatePostLayout(container:?ConstraintLayout?)?{
????????super.updatePostLayout(container)
????????getViews(container).forEach?{?view?->
????????????ViewAnimationUtils.createCircularReveal(
????????????????view,?view.width?/?2,
????????????????view.height?/?2,?0f,
????????????????hypot((view.height?/?2.0),?(view.width?/?2.0)).toFloat()
????????????).apply?{
????????????????duration?=?1000
????????????????start()
????????????}
????????}
????}
}
updatePostLayout會在onLayout之后調(diào)用,在這里做動畫就可以。這里除了使用createCircularReveal來創(chuàng)建動畫,一般的屬性動畫也是一樣的。
在xml布局中,只需要指定ConstraintHelper和referenced_ids即可。
????android:layout_width="match_parent"
????android:layout_height="wrap_content"
????app:constraint_referenced_ids="chipOne,chipTwo,chipThree,chipFour,chipFive,chipSix,chipSeven,chipEight"
????app:layout_constraintTop_toTopOf="parent"?/>
ConstraintHelper還有一個好處就是可以對渲染效果進(jìn)行封裝,例如上面的CircularReveal效果,創(chuàng)建好ConstraintHelper之后,以后如果還有View需要使用CircularReveal的效果,那直接創(chuàng)建一個ConstraintHelper就可以了,實現(xiàn)了很好的復(fù)用功能。
除了繼承ConstraintHelper來實現(xiàn)自定義的ConstraintHelper,也可以通過繼承現(xiàn)有的一些系統(tǒng)ConstraintHelper,例如Layer,這樣的好處是,可以拿到一些額外的參數(shù),方便計算,例如Layer中提供的布局邊界,這樣實現(xiàn)一個發(fā)散靠攏的效果,就很簡單了,示例代碼如下所示。
class?FlyingHelper?@JvmOverloads?constructor(
????context:?Context,?attrs:?AttributeSet??=?null,?defStyleAttr:?Int?=?0
)?:?Layer(context,?attrs,?defStyleAttr)?{
????override?fun?updatePostLayout(container:?ConstraintLayout)?{
????????super.updatePostLayout(container)
????????val?centerPoint?=?PointF(((left?+?right)?/?2).toFloat(),?((top?+?bottom)?/?2).toFloat())
????????ValueAnimator.ofFloat(0f,?1f).setDuration(1000).apply?{
????????????addUpdateListener?{?animation?->
????????????????val?animatedFraction?=?animation.animatedFraction
????????????????updateTranslation(centerPoint,?animatedFraction,?container)
????????????}
????????????start()
????????}
????}
????private?fun?updateTranslation(centerPoint:?PointF,?animatedFraction:?Float,?container:?ConstraintLayout)?{
????????val?views?=?getViews(container)
????????for?(view?in?views)?{
????????????val?viewCenterX?=?(view.left?+?view.right)?/?2
????????????val?viewCenterY?=?(view.top?+?view.bottom)?/?2
????????????val?startTranslationX?=?if?(viewCenterX?else?2000f
????????????val?startTranslationY?=?if?(viewCenterY?else?2000f
????????????view.translationX?=?(1?-?animatedFraction)?*?startTranslationX
????????????view.translationY?=?(1?-?animatedFraction)?*?startTranslationY
????????}
????}
}
ImageFilterButton、ImageFilterView
ImageFilterButton繼承自AppCompatImageButton,而ImageFilterView繼承自AppCompatImageView,他們都可以實現(xiàn)對圖片的一些簡單處理,例如圓角、亮度、色相、飽和度等。
圓角
例如通過roundPercent和round來實現(xiàn)圓角。
????android:id="@+id/chipOne"
????android:layout_width="80dp"
????android:layout_height="60dp"
????android:background="#2962FF"
????app:roundPercent="1"?/>
圖像疊加效果
使用crossfade屬性來實現(xiàn)altSrc和src的疊加效果。altSrc提供的資源,會和src提供的資源通過crossfade屬性形成交叉淡化效果。默認(rèn)情況下,crossfade = 0,altSrc所引用的資源為不可見,它的取值范圍在0-1,借助這個屬性可以實現(xiàn)兩個Image過渡的效果動畫。
圖片處理
除了上面提到的這些,ImageFilterButton、ImageFilterView還支持很多簡單的圖像處理特性,下面的表中詳細(xì)介紹了。
image總的來說,ImageFilterButton、ImageFilterView可以幫助開發(fā)者實現(xiàn)一些簡單的圖像處理效果,不過使用場景還是比較有限。
ConstraintLayoutStates
ConstraintLayoutStates是ConstraintLayout2.0新增的用于切換狀態(tài)布局的一個功能,它可以根據(jù)狀態(tài)切換不同的布局文件。
首先,需要在layout下創(chuàng)建不同狀態(tài)的layout xml文件,布局文件的root id相同即可。
然后在xml文件夾下創(chuàng)建管理文件,代碼如下所示。
"1.0"?encoding="utf-8"?>
"http://schemas.android.com/apk/res/android"
????xmlns:app="http://schemas.android.com/apk/res-auto">
???? ????????android:id="@+id/state1"
????????app:constraints="@layout/states_1"?/>
???? ????????android:id="@+id/state2"
????????app:constraints="@layout/states_2"?/>
在使用的時候,使用loadLayoutDescription來加載管理文件即可。
override?fun?onCreate(savedInstanceState:?Bundle?)?{
????super.onCreate(savedInstanceState)
????setContentView(R.layout.states_1)
????val?constraintLayout?=?findViewById(R.id.constraint_state)
????constraintLayout.loadLayoutDescription(R.xml.constraint_layout_states)
????constraintLayout.setOnClickListener?{
????????constraintLayout.setState(R.id.state2,?0,?0)
????}
}
這樣就可以通過setState來實現(xiàn)狀態(tài)布局的切換了。
ConstraintProperties
ConstraintProperties是一個用于在代碼中創(chuàng)建約束的工具類。在2.0版本之前,我們需要這樣修改屬性,代碼如下所示。
ConstraintSet().apply?{
????clone(constraintLayout)
????setTranslationX(R.id.xxx,?32f)
????setMargin(R.id.xxx,?ConstraintSet.START,?42)
????applyTo(constraintLayout)
}
而2.0提供了ConstraintProperties可以使用流式API修改屬性,代碼如下所示。
ConstraintProperties(findViewById(R.id.xxx))
????.translationZ(8f)
????.margin(ConstraintSet.START,?8)
????.apply()
在代碼中使用ConstraintLayout更加方便了。
向大家推薦下我的網(wǎng)站?https://xuyisheng.top/??點(diǎn)擊原文一鍵直達(dá)
專注 Android-Kotlin-Flutter 歡迎大家訪問
