KC Blog

學習Android Kotlin 客製UI的技巧-左右切換view與動畫的下拉欄教學 - 03

10 min read
AndroidDev#Android

Hello,大家好,這裡是Elegant Access的 KC,

歡迎大家繼續回來看如何客製化的實作篇,

今天的內容會提到怎樣實作一個左右滑動的recycler view,並且提到如何調整,

讓它滑動起來有pager的滑動感以及讓item間有間距的調整,好吧,廢話不多說,接著看下去吧!

效果圖:

Cover

首先第一步,很簡單,要先定義一個recycler view用的adapter,那這邊我習慣,先做其中的item的xml,所以如下:

接著,開始定義你的adapter,並讓adapter load進你剛剛創建的item:

因為我習慣使用自定義的BaseListAdapter,

在這個類裡面我習慣會用泛型來寫,

去代替之後可能會丟進adapter的data,

然後會在我的adapter裡面創建一個屬於這個adatper的viewholder,

一樣也用了泛型,去取代未來可能會重複寫到類似code的地方,

所以會看起來像下面這樣:

在上面這個adapter裡面,

我的code變得很少,

因為我先寫好了一個抽象類,

另外我的base adapter會長這樣,

主要是抽象了一些方法:

這裡主要講一些我抽象的方法 我要求實作 ItemView

@LayoutRes protected abstract int getItemViewLayout();

用意是讓我丟入,我寫好的item的xml的

這樣下次我就不用重複寫

接著我定義了一個ViewHolder,

去繼承我BaseListAdapter裡面的BaseViewHolder,

目的也是讓我之後可以不用再重寫一些基本的viewholder內容

另外這個base adapter 我也寫了一些其他通用功能 如果有需要再看就好 有些沒照著寫也是可以用

結論是 我只要override像上方code的幾個function就好了,

就能實現一個recycler view adapter的功能了!

我在我的BaseListAdapter類別裡面繼承了RecyclerView.Adapter,

並實作了recycler view adapter需要繼承的 :

  • getItemViewType(position: Int)
  • onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int)
  • getItemViewType(position: Int)
  • getItemCount()
  • onCreateViewHolder(parent: ViewGroup, viewType: Int)...等function,

這些都是在創造recycler view會需要繼承的地方,

因為考量到可能會很常用到recycler view,並且又很懶XDD,

想說未來在其他地方,

甚至是到別的專案的時候,

可能會用到類似的,

所以會習慣寫一個未來擴充性也夠的抽象類出來,

讓我日後在寫使用時也能方便取用,

想改可以直接在父類改,

並且直接繼承就能用,

真的是省時又能節省重複的code!

目前已經完成了一個基本的recycler view的建構了 ,

只要呼叫下方code,

就能有基本的recycler view了:

那這邊的switchRecyclerView是上一篇 [Custom View]客製UI心得分享(1):實作篇 有提到的customer view,忘記的可以看看

接著,我們為了要讓recycler view達到以下效果:

  1. 滑到最邊邊的時候,recycler view能離開最旁邊並看起來滑動到最中間
  2. 每個item之間有固定的間隔距離

所以我們需要繼承RecyclerView.ItemDecoration(),如下:

這邊為了達到每個item之間有間隔,

設定了mSpace變數,

用來指定item之間的間隔,

所以只要在 override getItemOffsets之後,

取用裡面的Rect,來設定每個item間格,如:

outRect.left = mSpace

但因為我們要達到滑到最左或最右拿讓該item看起來滑動到中間,

所以我們定義了一個 sideVisibleWidth 變數,

這個變數的算法是:

(螢幕範圍width的pixel - 每個item的pixel)/2 - 預期間隔最左/右邊距離的pixel

上面這個算法雖然有點難理解,

但其實只要稍微想過,也是能想通的,

這邊切細一點來說,recycler view 一開始會從螢幕最左邊或最上面起始,

在我這個例子是橫向,所以是最左邊:那思路是這樣的,

我希望我滑到最左/右邊的時候會間隔最左邊一定距離,

並且讓該recycler view的item是在正中間,

所以我需要取得範圍內螢幕width的長度的一半,

又需要讓item只移動一半,所以取得item的長度的一半,所以是 :

螢幕範圍width的pixel/2 - 每個item的pixel/2

這個意思也就代表,計算半個螢幕並算滑動半個item的距離,

使得他看起來就會在中間,但是因為我們在最左或最右時,

還需要額外定義的間距,所以必需再去減掉指定的pixel,

所以合併以後會變成最一開始給的算式:

(螢幕範圍width的pixel - 每個item的pixel)/2 - 預期間隔最左/右邊距離的pixel

所以換成code就是:

var sideVisibleWidth =      
//整個螢幕width的pixel  
(context.resources.displayMetrics.widthPixels
//每個item的一半
- ScreenUtil().convertDpToPixel(70F, context).roundToInt()) / 2
//預期間隔多少的pixel
- ScreenUtil().convertDpToPixel(28F, context) .roundToInt()

接著,只需要在指定條件下,去指定我們的item間隔即可,如下:

所以我上面的code,就是在第一個item的時候間隔我的sideVisibleWidth與每個他的右邊間隔mSpace的pixel

到這裡間距的設定就都完成了,再來就是需要進入算出每個移動距離並做出相對應動作的步驟了

接著我們繼承 RecyclerView.OnScrollListener()來計算滑動時的監聽器:

這邊說難不難,說簡單也有點花時間,但只要釐清他要做的事情,你就會有股茅塞頓開的感覺,

在這個類裡,我們要記錄我的item目前滑動到的位置,

以及我們每個recycler view item的pixel大小,

因為我們要紀錄目前滑動位置,並且透過計算滑動量去算與每個item的比例,

來算出我們移動了幾個item的位置,所以我們一開始在建構子裡傳入:

SwitchRecyclerScrollerListener(private var mPosition: Int, private val itemWith: Int)

接著override onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int),去取得左右橫移時的滑動量,

我們在onscroll裡面取得dx,他代表左右橫移的滑動量,

他是當你滑動都會觸發一次,所以在滑動期間,紀錄並累加該值,

之後使用onScrollStateChanged,當滑動停止時,

也就是SCROLL_STATE_IDLE,會去算出總移動量並算出移動的比例:

val offset = scrolledWidth.toFloat() / itemWith.toFloat()

算出的比例就是我移動了幾個item,用這個值去觸發我每移動了要做什麼事,

例如我這邊,就是如果移動的item數不是0,我就設定文字顏色變化,如:

if (moveTotalCount != 0) {        
   mPosition += moveTotalCount     
   scrolledWidth -= itemWith * moveTotalCount
   setItemAnim(recyclerView, mPosition)      
}

這樣在滑動時,看起來就會是你在滑動時,文字的顏色會轉換的動畫了!

那這邊動畫的code我就不寫出來了,

有興趣的可以去我分享的source code,裡面有詳細的寫法,

當然你也可以依照你的需求,自己去寫!

最後,你只要把你剛剛寫的那些繼承的類,拿出來用,

就可以得到一個左右滑動的recycler view了,這邊是我實際使用時的範例: