Android開源控制元件InboxRecyclerView,支援點選展開(Click-to-expand),下拉摺疊(Pull-to-dismiss)的...
前言
本文原作者 ofollow,noindex">Saket Narayan ,文章翻譯自作者個人部落格 Introducing InboxRecyclerView, a library for building expandable descendant navigation 。專案Github 傳送門 。
正文
簡介
InboxRecyclerView是一款基於RecyclerView的開源控制元件,支援點選時展開以及下拉摺疊的動畫切換效果。

demo.gif
)
如果你有興趣瞭解InboxRecyclerView的工作原理感興趣,下文將對專案的一些細節進行描述。
Click to Expand
InboxRecyclerView包含主要兩部分:列表項的 InboxRecyclerView
以及用於顯示可擴充套件內容的 ExpandablePageLayout
。單擊某個專案時InboxRecyclerView將執行三個步驟:
1. 準備展開
InboxRecyclerView將詳細內容與列表項對齊。在展開的過程中,列表項(ListItem)和內容項(Content)同時進行淡出淡如操作,使列表項看起來自身正在展開。

align_content_with_item_on_click.gif
val itemLocation: Rect = captureViewLocation(clickedItem) contentPage.visibility = View.VISIBLE contentPage.translationY = itemLocation.y contentPage.setDimensions(itemLocation.width, itemLocation.height)
此時,該列表項的詳細內容將被載入入 ExpandablePageLayout
中,具體內容請參考 Activity.kt#L88" target="_blank" rel="nofollow,noindex">示例程式
2. 展開列表項
在對齊列表項和內容項後,下一步是為展開設定動畫效果。為了保證展開動畫流暢,InboxRecyclerView使用 View#setClippedBounds(Rect) 對View的可見部分進行動畫處理,以營造一種它正在展開的錯覺。

fun animateDimensions(toWidth: Int, toHeight: Int) { val fromWidth = clipBounds.width() val fromHeight = clipBounds.height() ObjectAnimator.ofFloat(0F, 1F) .addUpdateListener { val scale = it.animatedValue as Float val newWidth = (toWidth - fromWidth) * scale + fromWidth val newHeight = (toHeight - fromHeight) * scale + fromHeight) contentPage.clipBounds = Rect(0, 0, newWidth, newHeight) } .start() }
3.給列表項新增動畫效果
為了實現展開內容正在推開其他列表項的動畫效果,在動畫期間其他專案也會隨著被展開的內容同步移動。這在 ItemExpandAnimator 內實現,當然,展開動畫支援自定義。

animate_list_items.gif
Pull to Collapse
Pull to Collapse的手勢動作利用了Android View的特性:ViewGroup可以先於子View攔截觸控事件,詳細的內容請參考作者的另一篇文章: Designing a flick dismissible image viewer (計劃會在之後翻譯)。

pull_to_collapse_2.gif
當垂直手勢被檢測到的時候,頁面將隨著手勢滑動。有趣的是,頁面並沒有隨著使用者手指同步移動,作者在頁面滑動的過程中增加了一個摩擦力:
override fun onTouch(view, event): Boolean { when (event.action) { ACTION_MOVE -> { val deltaY = event.rawY - lastTouchY val friction = 4F var deltaYWithFriction = deltaY / frictionFactor view.translationY += deltaYWithFriction val lastTouchY = event.rawY } ACTION_UP -> { if (isEligibleForCollapse()) { collapsePage() } else { smoothlyResetPage() } } } }
一旦頁面滾動超出了它的移動範圍,這個摩擦力將會變得非常大,從而產生如下效果:

pull_to_collapse_friction.gif
if (isEligibleForCollapse()) { val extraFriction = collapseDistanceThreshold / view.translationY deltaYWithFriction *= extraFriction }
InboxRecyclerView用白色覆蓋了ListView的不可見部分,在下拉摺疊的過程中,這層白色會逐漸淡出以顯示被覆蓋的內容,從而強調了下拉的動畫效果。

background_tint_2.gif
,使用狀態列去指示內容是否可摺疊。

dank_status_bar_tint.gif
總結
這是我翻譯的第一篇有關開源控制元件的部落格,希望各位喜歡。
如果對我的文章感興趣,請移駕 輕量級自定義NumberPicker (給我個星吧求求你們了, Github傳送門 )
總而言之,千山萬水總是情,點個贊再走唄。