ViewPager中自定義title indicator

需求

繼續(xù)死磕Launcher的文件夾模式,按照UI的設(shè)計(韓國團(tuán)隊,果然思考的非主流),對類APUS文件夾滑動顯示的要求是:當(dāng)前的文件夾名字要居中高亮大字體(如果是第一個文件夾也是要居中,左側(cè)空白;如果不是第一個文件夾,那么左右顯示的文件夾名字都是小字體,而且距離當(dāng)前文件夾名字的間距相同)。如下圖所示:


folder indicator demo

話說看到這個設(shè)計的時候,我的內(nèi)心是崩潰的,為啥不用主流的顯示方式,類似頭條新聞,APUS之類的,順序排列,哪個當(dāng)前顯示,哪個下面有下劃線即可。現(xiàn)在有很多寫好的控件,拿來直接用即可。但是,還是要實現(xiàn)這個效果。

分析

需要實現(xiàn)如下的功能:

  • 當(dāng)前展示的文件夾名字居中,其他文件夾跟它等距離相間
  • 點擊當(dāng)前顯示的文件夾名字可以改變文件夾名
  • 點擊非當(dāng)前的文件夾名字,顯示這個干文件夾的內(nèi)容,并且此文件夾名字居中高亮(有下劃線)

文件夾名字顯示的實現(xiàn)

參考了github上ViewPagerIndicator其中TitlePageIndicator的實現(xiàn),即通過直接繼承View來實現(xiàn)上訴的需求。因為現(xiàn)有的控件不足于讓我們完成title的繪制和展示。
首先我們自定義的TitlePageIndicator要繼承View并且實現(xiàn)ViewPager的接口OnPageChangeListener,這樣viewpager滑動的時候,我們可以通過回調(diào)函數(shù)來控制相關(guān)title的滑動顯示。

public class TitlePageIndicator extends View implements OnPageChangeListener {

因為本身TitlePageIndicator是個自定義的view,所以我們把它加到我們的布局文件中

<com.android.launcher3.TitlePageIndicator android:id="@+id/folder_group_indicator" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="10.0dp" > </com.android.launcher3.TitlePageIndicator> <com.android.launcher3.FolderViewPager android:id="@+id/viewpager" android:layout_below="@id/folder_group" android:layout_width="match_parent" android:layout_height="match_parent" > </com.android.launcher3.FolderViewPager>

這樣,在TitlePageIndicator的onDraw函數(shù)里面就可以展示我們的文件夾名稱了。
所以重點在onDraw里面怎么畫出這些titles。

  • 首先要先算出各個title的left/right/top/bottom(主要就是left和right,因為在文件夾名稱之間我們要加入固定的padding,這樣left/right都是絕對的坐標(biāo),即有可能是在屏幕外),然后存在一個ArrayList上。這個操作每次調(diào)用onDraw的時候都要重新算,因為當(dāng)前顯示的title不一樣,那么以這個title為中心,其他title的位置就要算出來,另外隨著手指的滑動,這些值是動態(tài)變化的。難點1是這個。
  • 然后是要判斷當(dāng)前的真實位置(即有可能是在拖拽滑動中),這用要從OnPageChangeListener里面的回調(diào)函數(shù)中獲得:onPageScrollStateChanged中獲得是否在滑動isScrolling;onPageScrolled中獲得當(dāng)前的頁碼mCurrentPage,和偏移量mPageOffset。當(dāng)然,在這些函數(shù)的最后都要調(diào)用invalidate來觸發(fā)onDraw來更新UI。
  • 這樣我們的onDraw里面就有了當(dāng)前的狀態(tài):是否在滑動isScrolling,當(dāng)前顯示的頁碼mCurrentPage,偏移量mPageOffset。利用這幾個變量,我們要算出來各個title的相對之前算出來的坐標(biāo)的偏移量,即得到實際要在哪里顯示title。這里分三種情況:
    1. 是否是在滑動,如果不滑動,直接按照之前算出來的坐標(biāo)顯示即可。
    2. 如果滑動,當(dāng)前頁不是最后一頁,要通過當(dāng)前頁的下一頁來算偏移量。
    3. 如果滑動,當(dāng)前頁為最后一頁,那么要通過當(dāng)前頁的前一頁來算偏移量。
    最后調(diào)用canvas.drawText來完成title的繪制。

canvas.drawText(pageTitle, 0, pageTitle.length(), bound.left + offset, bound.bottom + mTopPadding, mPaintText);
我們最后關(guān)心的只有offset這個參數(shù)(不滑動的時候為0,不表),需要單獨算出來。

  1. 非最后一頁的offset
         gap = (rightBound.right + rightBound.left) / 2 - halfWidth(屏幕寬的一半);
            offset = (int)(gap * mPageOffset);
  1. 最后一頁的offset
         float gap = (halfWidth - w / 2) - leftBound.left;
            offset = (int)(gap * (1 - mPageOffset));

Note:
關(guān)于ViewPager左右滑動,mCurrentPage的變化是不一樣的。所以通過gap算offset的公式也不一樣。

  • 手指向左滑動,頁面向右走的時候,mCurrentPage是不變的。直到滑到下個頁面的時候,才變化。
  • 手指向右滑動,頁面向左走的時候,mCurrentPage立刻減一。

關(guān)于點擊文件夾名字切換文件夾的實現(xiàn)

重寫了onTouchEvent函數(shù),當(dāng)ACTION_UP的時候,說明完成了點擊事件。那么這個時候判斷點擊的位置落到哪個title上,然后調(diào)用viewpager的setCurrentItem函數(shù)來完成切換。比較簡單。

最后的效果

現(xiàn)有的效果

后記

實現(xiàn)的還是比較粗糙,后面還要繼續(xù)優(yōu)化。感覺好的UI控件還是要慢慢雕琢才可以。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,814評論 25 708
  • 先看看效果(單身狗請做好心理準(zhǔn)備):http://www.yewu233.com/gift/gift.html 我...
    iimT閱讀 3,557評論 2 18
  • 因為打了賭,所以每天有沒有完全500字的任務(wù),女兒是要檢查的。昨天寫了《隨想·人生》一篇,我又跟女兒說,明天我已經(jīng)...
    力牧閱讀 186評論 0 0
  • 你是否有時候突然覺得健身好枯燥,好累,想休息…… 那就來玩玩新花樣! 有氧健身舞蹈1! 站著就可以鍛煉腹部,瘦小腹...
    Eramus5閱讀 167評論 0 0