【Flutter】自定義ListView開發記錄(一)——設計滑動效果的處理方式

語言: CN / TW / HK

theme: condensed-night-purple

「這是我參與11月更文挑戰的第8天,活動詳情檢視:2021最後一次更文挑戰」。

前言

隨著熊孩子拆元件之ListView系列的結束,裝元件系列迎來了開始,從這篇開始我會記錄實現自定義ListView過程中,自己的思考和實現方式;也方便以後整理程式碼~

那麼首先從控制滑動方式這部分開始:

方案選擇——滑動效果控制

首先看看需要的滑動效果:

  1. 模擬翻頁 模擬翻頁

在這個模式下,不需要任何滑動效果;唯一所做的事便是將item從上往下依次羅列;

  1. 覆蓋翻頁

覆蓋翻頁

覆蓋翻頁模式下,在上面模擬翻頁效果基礎上,僅僅將滑動效果應用於當前最頂層的可見頁;

  1. 滾動翻頁

image.png

滾動翻頁效果跟ListView本身的效果沒什麼區別;

總結一下:

  • 在第一個模式下,position 的改變,僅僅是在一頁翻過去的時候觸發;
  • 在第二個模式下,position 的改變等同於現在的ListView,但是隻應用於第一個可見頁;
  • 在第三個模式下,等同於ListView,不需要任何處理;

現在具體分析設計一下:

在上一篇文章中,對於滑動效果的控制,我設想提出了三種實現方式:

  • 自定義ScrollerController;
  • 自定義ViewPort;
  • 自定義SliverList;

在ListView中,ScrollController 負責的部分就是控制ScrollPosition;ViewPort負責處理展示區域;SliverLit負責載入繪製內容,這三者之間是一個上級呼叫下級的關係;

雖說上級能呼叫下級,但是這三者其實並沒有什麼關聯,比如說ScrollController只關心自己的Positiion,SliverList展示的什麼,內容這麼樣並不關心,換句話說,ScrollController不知道SliverList的展示細節,除非它提供到ScrollPosition中;

這樣的話,ScrollController是無法得知SliverList的展示狀態;ViewPort也同理,僅僅通過position的修改,是無法區分當前position是在第幾頁,偏移量多少,讓position中的pixels生效有意義的地方,就是SliverList;

所以修改方式,應該是自定義SliverList;

具體實現方式:

這裡就參考之前的中秋投稿,說白了,對SliverList的RenderObject層進行修改:

在中秋投稿那次,我重寫了RenderSliverList 的 paint方法,在原本的SliverList中,是這麼規定繪製child的:

image.png

可以看到,其實paint方法中才是真正去計算 child 位置的地方,先看childMainAxisPosition方法:

image.png

image.png

在這裡會去計算主軸的位置,依據便是之前在layout方法中,遍歷child並給其parentData設定的layoutOffset引數,並減去當前已經滑動的距離;

交叉軸這塊,則直接返回0

image.png

自此就算出了具體的相對位置,之後就傳給paint方法進行繪製;

之後就是獲取下一個child,再次迴圈上述流程;

而如果要實現上面提到的效果,要做的事也比較簡單:

  • 如果是效果1,那麼一直將所有child的繪製位置改為0即可;
  • 如果是效果2,那麼僅僅在第一個child上加上scrollOffset的偏移量計算即可;

當然,由於繪製順序的關係,獲取child要先從lastChild獲取,並不斷取之前的child,直到第一個child;

如果再結合自定義路徑,加入變化Item的能力,那就要引入設想的LayoutManager的能力;

在此先將這部分邏輯抽離,等LayoutManager呼叫使用;

結語:

這個滑動效果實現方式非常簡單,麻煩的地方反而是如何將自定義SliverList的接進去~

下一步看下對Element層面的改造,暴露RenderObject 的layout、paint方法出去

「其他文章」