XML中配置網易雲歌手詳情滑動效果

XML中配置網易雲歌手詳情滑動效果

HeaderLayout


  網易雲音樂App給用戶的體驗效果一直都很是好,尤爲是流暢的動畫和滑動的聯動效果,都給人一種如絲滑般的感覺,這一點在其歌手詳情頁面體現得尤其突出。那麼咱們就來實現這樣的效果,可是咱們不能只侷限在實現當中,不然當需求變化就須要改動大量的代碼,同時也不能保證它的複用性,放到其餘界面則須要寫許多重複代碼。所以咱們須要跳出實現的限制,將其中的元素抽取出來,製做成一個通用的庫,而且保證其可拓展性和充分的用戶自定義性。通過研究,最終實現了此控件,並取名爲HeaderLayout,那麼咱們先來看看實現效果以便直觀的感覺一下。java

### 如何使用

  效果圖中全部的頭部控件滑動聯動效果都只須要在xml中配置幾行代碼便可完成,因爲HeaderLayout是根據CoordinatorLayout的機制來實現的,因此HeaderLayout須要包裹在CoordinatorLayout中才會有效果。android

  • 引入依賴 git

    Download

    implementation "com.imurluck:headerlayout:$lastVersion"
    複製代碼
  • 編寫佈局
       ​ HeaderLayout繼承自FrameLayout,且並無改寫FrameLayout的測量和佈局邏輯,因此子控件的佈局方式和FrameLayout相同便可,咱們只須要關注HeaderLayout新增的幾個屬性。這裏以效果圖爲例。github

    <androidx.coordinatorlayout.widget.CoordinatorLayout ...>
    
        <com.zzx.headerlayout_kotlin.HeaderLayout android:layout_width="match_parent" android:layout_height="wrap_content" //新增屬性 app:extend_height="30%">
    
            <androidx.appcompat.widget.AppCompatImageView android:layout_width="match_parent" android:layout_height="300dp" android:src="@drawable/singer" android:scaleType="centerCrop" //新增屬性 app:transformation="scroll|extend_scale" />
    
            ...
    
        </com.zzx.headerlayout_kotlin.HeaderLayout>
    
        <androidx.viewpager.widget.ViewPager android:id="@+id/viewPager" android:layout_width="match_parent" android:layout_height="match_parent" //配置依賴佈局的layout_behavior app:layout_behavior="@string/header_layout_scrolling_view_behavior"/>
    
    </androidx.coordinatorlayout.widget.CoordinatorLayout>
    複製代碼

  如上所示,HeaderLayout工做在CoordinatorLayout中而且是其直接子View。ViewPager因爲須要根據HeaderLayout的滑動作出界面的調整,因此須要配置layout_behavior,而且其值爲@string/header_layout_scrolling_view_behavior,這裏和AppBarLayout的使用方式一致。咱們的工做重點是頭部控件的聯動效果,所以我們聚焦於HeaderLayout和其子View。咱們看AppCompatImageView,它用來展現效果圖中的歌手。仔細分析效果圖中AppCompatImageView的變換方式,能夠發現它是根據父控件HeaderLayout的滑動而作出的相應的變化效果,HeaderLayout向上滑動,其跟隨向上,HeaderLayout向下滑動,則跟着向下。而且,在HeaderLayout滑動到底部繼續向下拓展時,AppCompatImageView作了一個收縮的變換。這一切的一切都須要歸功於app:transformation屬性,能夠在代碼中看見其值爲"scroll|extend_scale",那麼其含義是什麼呢?對此,咱們引出了一個概念----Transformation,它是一個接口,其意在爲根據HeaderLayout的滑動及狀態而作出相應的變化行爲。在介紹Transformation以前,有必要介紹一下HeaderLayout滑動中的幾種狀態。 app

HeadeerLayout狀態圖
  HeaderLayout的滑動其實是HeaderLayout高度的動態變化,因此須要瞭解圖中三種高度的含義。maxHeight是HeaderLayout第一次加載測量後的高度,minHeight是設置了app:sticky_until_exit="true"屬性的子View的高度之和,此屬性表示子View不隨着HeaderLayout而滑出屏幕,造成一種粘連在屏幕頂部的效果,且子View是按照順序排列的。extendHeight則是拓展的高度,展現在效果圖中就是圖片收縮scale時下滑的高度,extendHeight能夠在xml中爲HeaderLayout設置,其值能夠爲dimension,百分數,或者float比例,百分數和float比例是按照maxHeight而計算的。

​ 而圖中五種狀態用來表示HeaderLayout高度變化過程當中的滑動狀態,Transformation就是根據這五種狀態而生,Transformation做用於HeaderLayout的直接子View或者間接子View(間接子View須要本身進行處理,能夠參考CommonToolbarTransformation),一個子View能夠同時擁有多個Transformation,HeaderLayout在其狀態變化時,則會遍歷子View的全部Transformation,通知其作出改變。佈局

​ XML中做用於AppCompatImageView的app:transformation="scroll|extend_scale"屬性,scroll 和 extend_scale則是內置的兩種Transformation,以下表所示。學習

屬性 說明 做用對象
transformation
scroll 隨着HeaderLayout滑動而滑動 HeaderLayout直接子View
alpha STATE_MIN_HEIGHT到STATE_MAX_HEIGHT對應alpha爲0->1 HeaderLayout直接子View
alpha_contray 與alpha相反,STATE_HEIGHT到STATE_MAX_HEIGHT對應alpha爲1->0 HeaderLayout直接子View
extendScale 在STATE_MAX_HEIGHT到STATE_EXTEND_MAX_END之間作scale變換 HeaderLayout直接子View
common_toolbar 專爲Toolbar設計,在STATE_MIN_HEIGHT時顯示Title和Subtitle,不然隱藏,此屬性必須設置給HeaderLayout的直接子View,可是Toolbar不須要爲其直接子View HeaderLayout直接子View
sticky_until_exit true|false 子View不隨HeaderLayout而滑出屏幕,粘連在頂部 HeaderLayout直接子View
custom_transformation @string 自定義Transformation的全路徑 HeaderLayout直接子View
extend_height n(dp)|n%|0.n 設置HeaderLayout的extendHeight,能夠是dimension、百分比數或者小數比例 HeaderLayout

transformation表示內置的幾中Transformation,可是想要自定義Transformation應該如何作呢?動畫

自定義Transformation


  custom_transformation屬性則是專爲自定義Transformation而服務,其值爲本身實現的Transformation類的全路徑。自定義Transformation有兩種方式,其一是實現Transformation接口,另外一種方式是繼承TransformationAdapter類,TransformationAdapter是Transformation是Transformation接口的空實現,繼承於此則不須要實現全部的方法。url

interface Transformation<in V: View> {
    /** * @see [HeaderLayout.scrollState]爲STATE_MIN_HEIGHT, 這個方法回調錶示[HeaderLayout]的Bottom已經收縮到了最小高度 * @param child 當前須要作變換的view * @param parent [HeaderLayout] * @param unConsumedDy 由其餘狀態到此狀態未消耗完的dy */
    fun onStateMinHeight(child: V, parent: HeaderLayout, unConsumedDy: Int)

    /** * @see [HeaderLayout.scrollState]爲STATE_NORMAL_PROCESS, 在STATE_MIN_HEIGHT和STATE_MAX_HEIGHT之間 * 這個方法回調錶示[HeaderLayout]的Bottom正在最小高度與最大高度之間 * @param child 當前須要作變換的view * @param parent [HeaderLayout] * @param percent 0<percent<1, 值爲([HeaderLayout.getBottom] - [HeaderLayout.minHeight]) / ([HeaderLayout.maxHeight] - [HeaderLayout.minHeight]]) * 且值不會爲0或者1, 爲0至關因而回調了[onStateMinHeight], 爲1至關於回調了[onStateMaxHeight], 因爲值不會爲0或1, * 因此在回調[onStateMinHeight]和[onStateMaxHeight]時會有一個未消耗的dy * @param dy 滑動的距離 */
    fun onStateNormalProcess(child: V, parent: HeaderLayout, percent: Float, dy: Int)

    /** * @see [HeaderLayout.scrollState]爲STATE_MAX_HEIGHT * 這個方法回調錶示[HeaderLayout]的Bottom正處於[HeaderLayout.maxHeight] * @param child 當前須要作變換的view * @param parent [HeaderLayout] * @param unConsumedDy 由其餘狀態到此狀態未消耗完的dy */
    fun onStateMaxHeight(child: V, parent: HeaderLayout, unConsumedDy: Int)

    /** * @see [HeaderLayout.scrollState]爲STATE_EXTEND_PROCESS, 在STATE_MAX_HEIGHT和STATE_EXTEND_MAX_END之間 * 這個方法回調錶示[HeaderLayout]的Bottom正處於[HeaderLayout.maxHeight] 和 [HeaderLayout.maxHeight] + [HeaderLayout.extendHeight]之間 * @param child 當前須要作變換的view * @param parent [HeaderLayout] * @param percent 0<percent<1, 值爲([HeaderLayout.getBottom] - [HeaderLayout.maxHeight]) / [HeaderLayout.extendHeight] * 且值不會爲0或者1, 爲0至關因而回調了[onStateMaxHeight], 爲1至關於回調了[onStateExtendMaxEnd], 因爲值不會爲0或1, * 因此在回調[onStateMaxHeight]和[onStateExtendMaxEnd]時會有一個未消耗的dy */
    fun onStateExtendProcess(child: V, parent: HeaderLayout, percent: Float, dy: Int)

    /** * @see [HeaderLayout.scrollState]爲STATE_EXTEND_MAX_END, * 這個方法回調錶示[HeaderLayout]的Bottom正處於[HeaderLayout.maxHeight] + [HeaderLayout.extendHeight] * @param child 當前須要作變換的view * @param parent [HeaderLayout] * @param unConsumedDy 由其餘狀態到此狀態未消耗完的dy * */
    fun onStateExtendMaxEnd(child: V, parent: HeaderLayout, unConsumedDy: Int)
}
複製代碼

  HeaderLayout在狀態變化的時候會遍歷子View的全部Transformation,也便是會回調Transformation接口中的這幾個方法,使用者能夠根據這幾個方法的含義來變換子View。spa

Tips


  開發者在使用app:transformation和app:sticky_until_exit等屬性時,最好用AppCompatImageView代替ImageView,AppCompatTextView代替TextView,這樣在XML文件中則不會由於系統控件沒法使用自定義屬性而報紅線,即便報紅線也不會影響程序正常的執行,只是看着彆扭。

總結


  HeaderLayout是根據參照網易雲音樂的效果而實現的,但又跳出了「實現」的限制,提取出來了一個公共而又與業務無關的控件,其思想則是學習了CoordinatorLayout的behavior和ViewGroup事件的分發思想,將HeaderLayout的滑動狀態分發給其子View,從而產生聯動效果。

​ 最後放上Github的地址把。HeaderLayout

相關文章
相關標籤/搜索