ConstraintLayout2.0一篇寫不完之嵌套滾動怎么滾

點(diǎn)擊上方藍(lán)字關(guān)注我,知識會給你力量

在ConstraintLayout1.x階段,它主要提供的能力是對靜態(tài)布局的支撐,那么到2.x之后,MotionLayout的拓展,讓它對動態(tài)布局的支持有了進(jìn)一步的優(yōu)化,在1.x階段不能實(shí)現(xiàn)的嵌套滾動布局布局方式,現(xiàn)在也就非常簡單了。
在沒有ConstraintLayout的時候,要實(shí)現(xiàn)嵌套滾動布局,通常都是使用CoordinatorLayout來實(shí)現(xiàn),但是這個東西的使用局限性比較大,能非常簡單的實(shí)現(xiàn)的嵌套布局,就那么幾種,如果要實(shí)現(xiàn)一些特別的滾動效果,就需要自定義behavior來實(shí)現(xiàn),這樣一來,嵌套滾動布局就成了一個比較復(fù)雜的布局方式了,而MotionLayout的出現(xiàn),就可以完美的解決這樣一個布局難題。
在ConstraintLayout2.x中,有兩種方式來實(shí)現(xiàn)嵌套滾動布局。
CoordinatorLayout配合MotionLayout
這種方式實(shí)際上還是借助CoordinatorLayout,是一種比較早期的實(shí)現(xiàn)方案,如果是對CoordinatorLayout比較熟悉的開發(fā)者,可以很快改造現(xiàn)有代碼來適配MotionLayout的嵌套滾動。
這種方案的布局結(jié)構(gòu)如下:
CoordinatorLayout
--------AppBarLayout
----------------MotionLayout
--------NestedScrollView
可以發(fā)現(xiàn),這種方式,實(shí)際上就是利用MotionLayout來替代之前在AppBarLayout里面的CollapsingToolbarLayout,借助MotionLayout來實(shí)現(xiàn)之前CollapsingToolbarLayout的一些折疊效果。
這種方式的一般套路結(jié)構(gòu)如下。

在AppBarLayout中,我們通過MotionLayout控制動畫效果。
那么在這里,一般又有兩個套路,一是直接使用MotionLayout,然后在代碼里面通過AppBarLayout.OnOffsetChangedListener的回調(diào),設(shè)置MotionLayout的progress,另一種是直接自定義MotionLayout,實(shí)現(xiàn)AppBarLayout.OnOffsetChangedListener,這樣通用性比較強(qiáng),示例如下。
class CollapsibleToolbar @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : MotionLayout(context, attrs, defStyleAttr), AppBarLayout.OnOffsetChangedListener {
override fun onOffsetChanged(appBarLayout: AppBarLayout?, verticalOffset: Int) {
progress = -verticalOffset / appBarLayout?.totalScrollRange?.toFloat()!!
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
(parent as? AppBarLayout)?.addOnOffsetChangedListener(this)
}
}
?這兩種方式?jīng)]有本質(zhì)上的不同,但是對于MotionEditor來說,如果使用自定義的MotionLayout,在非根布局下創(chuàng)建約束的時候會有一些問題(修改屬性也會存在一些問題),所以,如果使用自定義MotionLayout的話,建議通過include的方式,引用新的根布局為自定義MotionLayout的方式來使用,而直接使用MotionLayout的方式,則沒有這個限制,希望MotionEditor能早日改善這個問題。PS:好消息,Android Studio Arctic Fox已經(jīng)修復(fù)了這個問題。
?
單純MotionLayout實(shí)現(xiàn)
MotionLayout的出現(xiàn),就是為了能替代動態(tài)的布局模型,所以,如果還使用CoordinatorLayout,那么就違背了開發(fā)者的初心了,所以,我們的目的就是去除CoordinatorLayout,而僅使用MotionLayout來實(shí)現(xiàn)嵌套滾動效果,實(shí)現(xiàn)滾動布局的大一統(tǒng)。
這種套路的一般結(jié)構(gòu)如下所示。
MotionLayout
--------MotionLayout
--------NestedScrollView
我們可以發(fā)現(xiàn),這里有兩層MotionLayout,外層的MotionLayout,用于控制頭部的伸縮布局,而內(nèi)部的MotionLayout,則用于控制頭部的滾動時效果。
這樣一來,整個嵌套滾動的格局一下子就打開了,再也沒了之前使用CoordinatorLayout的高度限制,效果限制,所有的內(nèi)容,都可以通過約束來進(jìn)行設(shè)置,再通過MotionLayout來進(jìn)行動態(tài)約束,從而實(shí)現(xiàn)嵌套滾動布局。
對于外層的MotionLayout,它的Scene提供兩個能力,一個是控制頭部從200dp,變?yōu)?6dp,即提供一個伸縮的功能,另一個重要的而且很容易被忽視的作用,就是給內(nèi)層MotionLayout提供progress數(shù)據(jù),有了這個progress,內(nèi)部MotionLayout才能聯(lián)動,這個和使用CoordinatorLayout配合MotionLayout使用要設(shè)置progress是一個道理。
我們來看下最外層的Scene,代碼如下所示。
<?xml version="1.0" encoding="utf-8"?>
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:motion="http://schemas.android.com/apk/res-auto">
<Transition
motion:constraintSetEnd="@+id/end"
motion:constraintSetStart="@+id/start">
<OnSwipe
motion:dragDirection="dragUp"
motion:touchAnchorId="@+id/motionLayout"
motion:touchAnchorSide="bottom" />
</Transition>
<ConstraintSet android:id="@+id/start">
<ConstraintOverride
android:id="@id/motionLayout"
android:layout_height="200dp"
motion:motionProgress="0" />
</ConstraintSet>
<ConstraintSet android:id="@+id/end">
<ConstraintOverride
android:id="@id/motionLayout"
android:layout_height="56dp"
motion:motionProgress="1" />
</ConstraintSet>
</MotionScene>
對于非layout_constraintXXX_toXXXOf的約束,可以使用ConstraintOverride來直接覆寫,這樣可以少寫很多重復(fù)的約束,這里的約束改變實(shí)際上只有兩個,即layout_height從200變?yōu)?6,而另一個重要的點(diǎn),就是motionProgress的指定,motionProgress的作用就是設(shè)置motionProgress,如果不設(shè)置這個,那么progress數(shù)據(jù)是沒辦法傳遞到內(nèi)部MotionLayout的,從而會導(dǎo)致內(nèi)部無法聯(lián)動。
解決完外部的MotionLayout之后,內(nèi)部的MotionLayout就迎刃而解了,因?yàn)樗娴木褪且粋€平平常常的MotionLayout,你想要對它內(nèi)部的元素做任何的改動,都和之前直接使用MotionLayout沒有任何區(qū)別。
我們來看這個簡單的例子,如圖所示。

頭部伸縮配上文字的移動,一個簡簡單單的類CoordinatorLayout布局,外部的Scene我們已經(jīng)解決了,再來看看內(nèi)部的Scene,算了不看了,沒什么必要,就是簡單的體力勞動。
整個套路的布局結(jié)構(gòu)如下所示。

總體看來,MotionLayout是不是實(shí)現(xiàn)了大一統(tǒng),它將滾動的布局效果,轉(zhuǎn)化為了多層MotionLayout的Scene分解,利用progress串聯(lián)起來,設(shè)計(jì)思路不可謂不精,一旦你熟練掌握了MotionLayout的各種基礎(chǔ)布局,那么即使再復(fù)雜的布局,也能分而治之。
向大家推薦下我的網(wǎng)站 https://xuyisheng.top/ 點(diǎn)擊原文一鍵直達(dá)
專注 Android-Kotlin-Flutter 歡迎大家訪問
往期推薦
更文不易,點(diǎn)個“三連”支持一下??
