Android RecyclerView實(shí)現(xiàn)吸頂動(dòng)態(tài)效果流程分析
一、ItemDecoration
ItemDecoration 允許應(yīng)用給具體的 View 添加具體的圖畫或者 layout 的偏移,對(duì)于繪制 View之間的分割線,視覺(jué)分組邊界等等是非常有用的。
當(dāng)我們調(diào)用 addItemDecoration() 方法添加 decoration 的時(shí)候,RecyclerView 就會(huì)調(diào)用該類的 onDraw 方法去繪制分割線,也就是說(shuō):分割線是繪制出來(lái)的。
RecyclerView.addItemDecoration()
RecyclerView.ItemDecoration,該類為抽象類,官方目前只提供了一個(gè)實(shí)現(xiàn)類 DividerItemDecoration。
public abstract static class ItemDecoration
public class DividerItemDecoration extends RecyclerView.ItemDecoration
里面有3個(gè)方法:
onDraw(): 在提供給RecyclerView的畫布上繪制任何適當(dāng)?shù)难b飾。通過(guò)此方法繪制的任何內(nèi)容都將在繪制項(xiàng)目視圖之前被繪制,因此將出現(xiàn)在視圖的下方。
public void onDraw(Canvas c, RecyclerView parent, State state) { onDraw(c, parent); }
繪制效果
onDrawOver():在提供給RecyclerView的畫布上繪制任何適當(dāng)?shù)难b飾。通過(guò)這種方法繪制的任何內(nèi)容都將在項(xiàng)目視圖被繪制之后被繪制,因此將出現(xiàn)在視圖上方。
public void onDrawOver(Canvas c, RecyclerView parent, State state) { onDrawOver(c, parent); }
繪制效果
getItemOffsets():檢索給定項(xiàng)的任何偏移量。outRect的每個(gè)字段指定項(xiàng)目視圖應(yīng)該插入的像素?cái)?shù),類似于padding或margin。默認(rèn)實(shí)現(xiàn)將outRect的界限設(shè)置為0,并返回。
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) { getItemOffsets(outRect, ((LayoutParams)view.getLayoutParams()).getViewLayoutPosition(), parent); }
二、實(shí)現(xiàn)RecyclerView吸頂效果
1、實(shí)現(xiàn)一個(gè)簡(jiǎn)單的RecyclerView
下面這個(gè)RecyclerView的實(shí)現(xiàn)細(xì)節(jié)略。
2、通過(guò)ItemDecoration畫分割線
自定義ItemDecoration。
創(chuàng)建 fruitDecotion 類 繼承 ItemDecoration,并實(shí)現(xiàn)onDraw、onDrawover、getItemOffsets三個(gè)方法。
RecyclerView調(diào)用自定義的fruitDecoration
RecyclerView.addItemDecoration(new fruitDecoration(this))
在getItemOffsets畫分割線,當(dāng)是組名時(shí),預(yù)留更大的空間(即分割線的height比普通分割線大)。
@Override public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { super.getItemOffsets(outRect, view, parent, state); if (parent.getAdapter() instanceof fruitAdapter){ // parent.getAdapter()獲取到當(dāng)前RecyclerView的Adapter fruitAdapter adapter = (fruitAdapter) parent.getAdapter(); // 獲取當(dāng)前view的位置 int position = parent.getChildLayoutPosition(view); if (adapter.isGroupHeader(position)) { // 如果是頭部,則預(yù)留更大的地方 outRect.set(0, groupHeaderHeight, 0, 0); }else { outRect.set(0, 4, 0, 0); } }
實(shí)現(xiàn)效果
3、畫出每個(gè)分組的組名
當(dāng)是組名時(shí),在較大分割線哪里畫出組名。在onDraw()方法里實(shí)現(xiàn)
@Override public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { super.onDraw(c, parent, state); if (parent.getAdapter() instanceof fruitAdapter) { fruitAdapter adapter = (fruitAdapter) parent.getAdapter(); //當(dāng)前屏幕中item的個(gè)數(shù) int count = parent.getChildCount(); // 獲取當(dāng)前RecyclerView距離屏幕左邊的padding int left = parent.getPaddingLeft(); // right == RecyclerView的寬度 - RecyclerView的右padding int right = parent.getWidth() - parent.getPaddingRight(); for (int i = 0; i < count; i++) { //獲取對(duì)應(yīng) i 的 view View view = parent.getChildAt(i); // 獲取 i 的 view 的布局位置 int position = parent.getChildLayoutPosition(view); // 判斷是否是頭部 boolean isGroupHeader = adapter.isGroupHeader(position); // i 的 view 是頭部并且 當(dāng)前view的top位置距離屏幕的頂部還有距離 if(isGroupHeader && view.getTop() - groupHeaderHeight - parent.getPaddingTop() >=0) { c.drawRect(left, view.getTop() - groupHeaderHeight, right, view.getTop(), headPaint); String groupName = adapter.getGroupName(position); textPaint.getTextBounds(groupName, 0, groupName.length(), textRect); c.drawText(groupName, left + 20, view.getTop() - groupHeaderHeight / 2f + textRect.height() / 2f, textPaint); }else if(view.getTop() - groupHeaderHeight - parent.getPaddingTop() >=0){ //分割線 c.drawRect(left, view.getTop() - 4, right, view.getTop(), headPaint); } } } }
實(shí)現(xiàn)效果
4、實(shí)現(xiàn)吸頂效果
因?yàn)閛nDrawOver方法是在itemView畫了之后才畫,所以組名的吸頂是寫在onDrawOver方法里。需要注意的時(shí),當(dāng)我們的第二個(gè)組名到頂部的時(shí)候,要把當(dāng)前頂部的組名給替換掉。
@Override public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { super.onDrawOver(c, parent, state); if (parent.getAdapter() instanceof fruitAdapter) { fruitAdapter adapter = (fruitAdapter) parent.getAdapter(); //TODO 返回可見區(qū)域內(nèi)的第一個(gè)item的position int position =((LinearLayoutManager) parent.getLayoutManager()).findFirstVisibleItemPosition(); // 獲取第一個(gè)item的view View itemView = parent.findViewHolderForAdapterPosition(position).itemView; int left = parent.getPaddingLeft(); int right = parent.getWidth() - parent.getPaddingRight(); int top = parent.getPaddingTop(); //TODO 當(dāng)?shù)诙€(gè)是頭部時(shí), boolean isGroupHeader = adapter.isGroupHeader(position + 1); if (isGroupHeader) { int bottom = Math.min(groupHeaderHeight, itemView.getBottom() - parent.getPaddingTop()); c.drawRect(left, top, right, top + bottom, headPaint); String groupName = adapter.getGroupName(position); textPaint.getTextBounds(groupName, 0, groupName.length(), textRect); c.drawText(groupName, left + 20, top + bottom - groupHeaderHeight / 2f + textRect.height() / 2f, textPaint); }else { // 如果不是頭部, 即普通的itemView,則當(dāng)前的頭部一直固定在頂部 c.drawRect(left, top, right, top + groupHeaderHeight, headPaint);; String groupName = adapter.getGroupName(position); textPaint.getTextBounds(groupName, 0, groupName.length(), textRect); c.drawText(groupName, left + 20, top + groupHeaderHeight / 2f + textRect.height() / 2f, textPaint); } } }
實(shí)現(xiàn)效果
完整demo
鏈接: https://pan.baidu.com/s/11I8dtWts3BZjYMgNvzanTw?pwd=bvx9
到此這篇關(guān)于Android RecyclerView實(shí)現(xiàn)吸頂動(dòng)態(tài)效果流程分析的文章就介紹到這了,更多相關(guān)Android RecyclerView內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 詳解Android使用CoordinatorLayout+AppBarLayout+CollapsingToolbarLayou實(shí)現(xiàn)手指滑動(dòng)效果
- 詳解Android使用CoordinatorLayout+AppBarLayout實(shí)現(xiàn)拉伸頂部圖片功能
- Android進(jìn)階NestedScroll嵌套滑動(dòng)機(jī)制實(shí)現(xiàn)吸頂效果詳解
- Android itemDecoration接口實(shí)現(xiàn)吸頂懸浮標(biāo)題
- Android Jetpack Compose實(shí)現(xiàn)列表吸頂效果
- Android實(shí)現(xiàn)上拉吸頂效果
- Android進(jìn)階CoordinatorLayout協(xié)調(diào)者布局實(shí)現(xiàn)吸頂效果
相關(guān)文章
Kotlin注解實(shí)現(xiàn)Parcelable序列化流程詳解
有時(shí)我們會(huì)在界面跳轉(zhuǎn)的過(guò)程中,做對(duì)象傳值,這時(shí)就需要對(duì)該對(duì)象做序列化處理了。Android中對(duì)對(duì)象的序列化處理有兩種方式,這篇文章主要介紹了Kotlin注解實(shí)現(xiàn)Parcelable序列化2022-12-12Android實(shí)現(xiàn)手勢(shì)滑動(dòng)多點(diǎn)觸摸縮放平移圖片效果(二)
這篇文章主要介紹了Android實(shí)現(xiàn)手勢(shì)滑動(dòng)多點(diǎn)觸摸縮放平移圖片效果,實(shí)現(xiàn)圖片支持多點(diǎn)觸控,自由的進(jìn)行縮放、平移的注意事項(xiàng),感興趣的小伙伴們可以參考一下2016-02-02Android App開發(fā)中ViewPager組件的入門使用教程
這篇文章主要介紹了Android App開發(fā)中ViewPager組件的入門使用教程,ViewPager主要用來(lái)實(shí)現(xiàn)通過(guò)滑動(dòng)來(lái)切換頁(yè)面的效果,需要的朋友可以參考下2016-03-03Android實(shí)現(xiàn)刮刮樂(lè)示例分析
本文實(shí)現(xiàn)了Android刮刮樂(lè)示例分析,刮獎(jiǎng)在生活中常常見到,網(wǎng)上現(xiàn)在也有各種各樣的抽獎(jiǎng)活動(dòng),下面我們就要實(shí)現(xiàn)一個(gè)刮刮樂(lè)程序。2016-10-10詳解Flutter網(wǎng)絡(luò)圖片本地緩存的實(shí)現(xiàn)
這篇文章主要為大家介紹了詳解Flutter網(wǎng)絡(luò)圖片本地緩存的實(shí)現(xiàn)示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04Android使用Notification在狀態(tài)欄上顯示通知
這篇文章主要為大家詳細(xì)介紹了Android使用Notification在狀態(tài)欄上顯示通知,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12Android自定義View實(shí)現(xiàn)標(biāo)簽流效果
這篇文章主要為大家詳細(xì)介紹了Android自定義View實(shí)現(xiàn)標(biāo)簽流效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02