【譯】RecyclerView+ItemDecorations實現帶指示器ViewPager效果

原文地址 https://blog.davidmedenjak.com/android/2017/06/24/viewpager-recyclerview.htmlhtml

先貼最後的效果圖,全部完整的代碼能夠在這裏找到,以爲不錯的能夠給個贊java

viewpager.gif

咱們知道如何經過ViewPager來展現多頁面,但從 support library24.2.0推出後,你能夠經過SnapHelper這個類輕鬆給RecyclerView加上相似ViewPager的效果,這篇文章是告訴你如何給你的RecyclerView添加page indicators,若是你有閱讀過個人博客的話,你可能知道我接下來會怎麼作:android

Pager 初始化

第一步,初始化你的RecyclerView,確保你的itemlayout設置成了layout_width="match_parent",不然的話沒那麼容易一頁一頁滾動。你的RecyclerView高度也應該設置成match_parent,若是是設置成wrap_content的話要確保你的items也要有相同的高度。git

PagerSnapHelper添加到你的RecyclerViewgithub

// 給recyclerview添加background color

recyclerView.setBackgroundColor(backgroundColor);

MyAdapter adapter = ...

recyclerView.setAdapter(adapter);

recyclerView.setLayoutManager(new LinearLayoutManager(context,

LinearLayoutManager.HORIZONTAL, false));

// 添加PagerSnapHelper

PagerSnapHelper snapHelper = new PagerSnapHelper();

snapHelper.attachToRecyclerView(recyclerView);

複製代碼

咱們如今有了個簡單的能夠翻頁滾動的RecyclerView,當咱們添加一個background color 在這裏的話,在底部畫白色的decorations就會比較好看。bash

bland-pager.gif

添加Pager Indicator

提示:若是你對於decorations沒有任何瞭解的話你能夠經過 introduction to decorations 入門我是如何簡單在items之間畫一個下劃線Indicatorapp

下一步咱們須要添加decoration來畫indicator,咱們建立一個LinePagerIndicatorDecoration並把它添加到RecyclerViewide

// pager indicator

recyclerView.addItemDecoration(newLinePagerIndicatorDecoration());

複製代碼

咱們的decoration要關注2個方法測試

getItemOffsets:給每一個ItemView添加padding,不會出現overlaying動畫

onDrawOver:在上層畫decoration ,繪製的內容在itemview上層。

我喜歡用getItemOffsets方法來確保個人items沒有overdraw,若是你的indicator 傾向overdraw,你能夠忽略這個getItemOffsets方法。咱們作 的一切是須要indicatorHeight的偏移在每一個View的底部。若是你使用 GridLayoutManager,你須要確保你的items僅僅只是在底部偏移了

@Override

public void getItemOffsets(Rect outRect, View view,

RecyclerView parent, RecyclerView.State state) {

super.getItemOffsets(outRect, view, parent, state);

outRect.bottom = indicatorHeight;

}

複製代碼

這個在底部的偏移也是我爲何設置了一個RecyclerViewbackground而不是pages上面, 這個偏移讓咱們的decorationcontent留有一點距離,因此設置一個background colorpagesitem上面的話會沒有效果。若是你不偏移你的itemsoverlay他們的話,你也就不須要給RecyclerView 設置background

接下來咱們把indicators畫給咱們的pages。咱們把indicator置於RecyclerView的底部中間,而且給每一個item畫直線,每一個直線之間有一些padding

@Override

public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {

super.onDrawOver(c, parent, state);

int itemCount = parent.getAdapter().getItemCount();

//水平居中, 計算寬度減去距離中間的一半

float totalLength = mIndicatorItemLength * itemCount;

float paddingBetweenItems = Math.max(0, itemCount - 1) * mIndicatorItemPadding;

float indicatorTotalWidth = totalLength + paddingBetweenItems;

float indicatorStartX = (parent.getWidth() - indicatorTotalWidth) / 2F;

// 在剩下的space垂直居中

float indicatorPosY = parent.getHeight() - mIndicatorHeight / 2F;

drawInactiveIndicators(c, indicatorStartX, indicatorPosY, itemCount);

}

private void drawInactiveIndicators(Canvas c, float indicatorStartX,

float indicatorPosY, int itemCount) {

mPaint.setColor(colorInactive);

// item indicator的寬度包含padding

final float itemWidth = mIndicatorItemLength + mIndicatorItemPadding;

float start = indicatorStartX;

for (int i = 0; i < itemCount; i++) {

// 給每一個item畫下劃線

c.drawLine(start, indicatorPosY,

start + mIndicatorItemLength, indicatorPosY, mPaint);

start += itemWidth;

}

}

複製代碼

這個地方給了咱們機會給每一個item畫一個標記,可是這些標記在page是選中後的時候還不是高亮的。接下來咱們計算咱們滾動了多遠來實現一個水平滾動的動畫而且把高亮的indicator畫出來。

咱們經過LayoutManager找出當前活動的page,而後計算滑動距離的百分比。這個計算方法在你的Views寬度設置成了match_parent的時候會頗有效簡單,不然的話可能會有不肯定的狀況。爲了改善體驗我使用了AccelerateDecelerateInterpolator來處理這個獲得的百分比progress的值,讓它看起來更加天然。

//找到活動的page,它這時候的下劃線應該是高亮的

LinearLayoutManager layoutManager = (LinearLayoutManager) parent.getLayoutManager();

int activePosition = layoutManager.findFirstVisibleItemPosition();

if (activePosition == RecyclerView.NO_POSITION) {

return;

}

// 找到活動的page偏移的距離 (若是用戶滑動了)

final View activeChild = layoutManager.findViewByPosition(activePosition);

int left = activeChild.getLeft();

int width = activeChild.getWidth();

// 滑動時候活動的item位置在[-width, 0]

// 滑動時候加入平滑動畫

float progress = mInterpolator.getInterpolation(left * -1 / (float) width);

複製代碼

經過這個百分比progress咱們就能夠畫這個高亮的indicator,它表明着用戶滾動到了哪一個page.咱們使用這個百分比progress來畫這個局部高亮選中的indicator它表明咱們滾動到了哪一個page

public void onDrawOver(Canvas c, RecyclerView parent,

RecyclerView.State state) {

super.onDrawOver(c, parent, state);

// 畫正常狀態下的下劃線

// ...計算百分比 ...

// 畫高亮狀態下的下劃線

drawHighlights(c, indicatorStartX, indicatorPosY, activePosition, progress, itemCount);

}

private void drawHighlights(Canvas c, float indicatorStartX, float indicatorPosY,

int highlightPosition, float progress, int itemCount) {

mPaint.setColor(colorActive);

// 每一個item的下劃線是包括padding

final float itemWidth = mIndicatorItemLength + mIndicatorItemPadding;

if (progress == 0F) {

//百分比爲0沒有滑動的話第一個畫高亮下劃線

float highlightStart = indicatorStartX + itemWidth * highlightPosition;

c.drawLine(highlightStart, indicatorPosY,

highlightStart + mIndicatorItemLength, indicatorPosY, mPaint);

} else {

float highlightStart = indicatorStartX + itemWidth * highlightPosition;

// 計算局部高亮下劃線的長度

float partialLength = mIndicatorItemLength * progress;

// 畫斷開的下劃線

c.drawLine(highlightStart + partialLength, indicatorPosY,

highlightStart + mIndicatorItemLength, indicatorPosY, mPaint);

// 畫高亮的下劃線 覆蓋在下一個item 

if (highlightPosition < itemCount - 1) {

highlightStart += itemWidth;

c.drawLine(highlightStart, indicatorPosY,

highlightStart + partialLength, indicatorPosY, mPaint);

}}

}

複製代碼

經過上面全部步驟,咱們達到了預期的indicator指示器,在RecyclerView正確的實現page效果

viewpagerdecoration.gif

全部完整的代碼能夠在這裏找到

還有什麼要作的?

你可能發現了,我選擇線條來代替圓圈作indicator指示器,可是畫圓圈經過動畫來設置他們的alpha也是能夠輕鬆實現相似的效果的。經過使用相似的方法你能夠在decorations 作不少事,你不須要修改代碼就能夠拿來重複使用。

這個解決方案只是一個嘗試,可能還有一些潛在的錯誤。正如文中提到的,肯定這個progress的方法在不一樣寬度的時候可能就不許確,更好的方法多是須要在SnapHelper內部去作處理。若是你選擇使用這個在你的APP的話要確保有足夠的測試。

相關文章
相關標籤/搜索