1. 程式人生 > >自定義 ItemDecoration 這個問題你真的注意到了嗎

自定義 ItemDecoration 這個問題你真的注意到了嗎

本文討論的是關於自定義ItemDecoration容易被忽略的問題,此文適合有過自定義ItemDecoration經驗的同學閱讀,還沒有學習過的可以先去看看相關文章再來看本文。

ItemDecoration 我相信只要使用過RecyclerView的同學肯定都比較熟悉了,我們在使用 RecyclerView 的時候一般都是用這個來畫分隔線的,不得不說十分的好用。但是在最近發現在使用自定義的ItemDecoration上遇到了一些細節上的問題,我這裡自定義了一個GridDividerItemDecoration ,用於網格佈局的分隔,大概效果如下圖所示:

繪製的邏輯大概是這樣的:當 itemView 不是最後一列或者最後一行的時候就繪製右側和底部分隔線,如果是最後一列時則不繪製右側分隔線,如果是最後一行則不繪製底部分隔線。

程式碼大概是這樣的

12345678910 if(isLastRow&&isLastColumn){//最後一行最後一列什麼都不繪製outRect.set(0,0,0,0);}elseif(isLastRow){// 如果是最後一行,則不需要繪製底部outRect.set(0,0,mDividerHeight,0);}elseif(isLastColumn){// 如果是最後一列,則不需要繪製右邊outRect.set(0,0,0,mDividerHeight);}else{outRect.set(0,0,mDividerHeight,mDividerHeight
);}

這裡的分割線設定的寬度只有1dp,看起來似乎沒有什麼問題,但是如果把分隔線的寬度設定為20dp效果如下圖所示:

會明顯的感覺到最後一列itemView的寬度會比前幾列寬一些,具體的數值就是我們設定的 dividerWidth (也就是分隔線的寬度),正常情況下我們在自定義的 ItemDocration 設定 ItemOffsets 不會影響 itemView 的的大小,然而這裡卻出現了這個問題(其實網上絕大部分流行的關於網格的ItemDocration都存在這個問題),什麼原因呢,看看下面兩段原始碼就會知道了

12345678910111213141516171819202122232425 privatevoidmeasureChild(View view,intotherDirParentSpecMode,booleanalreadyMeasured){finalLayoutParams lp=(LayoutParams)view.getLayoutParams();finalRect decorInsets=lp.mDecorInsets;finalintverticalInsets=decorInsets.top+decorInsets.bottom+lp.topMargin+lp.bottomMargin;finalinthorizontalInsets=decorInsets.left+decorInsets.right+lp.leftMargin+lp.rightMargin;finalintavailableSpaceInOther=getSpaceForSpanRange(lp.mSpanIndex,lp.mSpanSize);finalintwSpec;finalinthSpec;if(mOrientation==VERTICAL){//最後一個引數用來標識在當前的mOrientation 下是否可以滾動,//當mOrientation 是VERTICAL的時候水平方向肯定是不能滾動的wSpec=getChildMeasureSpec(availableSpaceInOther,otherDirParentSpecMode,horizontalInsets,lp.width,false);hSpec=getChildMeasureSpec(mOrientationHelper.getTotalSpace(),getHeightMode(),verticalInsets,lp.height,true);}else{hSpec=getChildMeasureSpec(availableSpaceInOther,otherDirParentSpecMode,verticalInsets,lp.height,false);wSpec=getChildMeasureSpec(mOrientationHelper.getTotalSpace(),getWidthMode(),horizontalInsets,lp.width,true);}measureChildWithDecorationsAndMargin(view,wSpec,hSpec,alreadyMeasured);}
123456789101112131415161718192021222324252627282930313233343536373839404142434445 publicstaticintgetChildMeasureSpec(intparentSize,intparentMode,intpadding,intchildDimension,booleancanScroll){intsize=Math.max(0,parentSize-padding);intresultSize=0;intresultMode=0;if(canScroll){if(childDimension>=0){resultSize=childDimension;resultMode=MeasureSpec.EXACTLY;}elseif(childDimension==LayoutParams.MATCH_PARENT){switch(parentMode){caseMeasureSpec.AT_MOST:caseMeasureSpec.EXACTLY:resultSize=size;resultMode=parentMode;break;caseMeasureSpec.UNSPECIFIED:resultSize=0;resultMode=MeasureSpec.UNSPECIFIED;break;}}elseif(childDimension==LayoutParams.WRAP_CONTENT){resultSize=0;resultMode=MeasureSpec.UNSPECIFIED;}}else{if(childDimension>=0){resultSize=childDimension;resultMode=MeasureSpec.EXACTLY;}elseif(childDimension==LayoutParams.MATCH_PARENT){resultSize=size;resultMode=parentMode;}elseif(childDimension==LayoutParams.WRAP_CONTENT){resultSize=size;if(parentMode==MeasureSpec.AT_MOST||parentMode==MeasureSpec.EXACTLY){resultMode=MeasureSpec.AT_MOST;}else{resultMode=MeasureSpec.UNSPECIFIED;}}}//noinspection WrongConstantreturnMeasureSpec.makeMeasureSpec(resultSize,resultMode);}

由於我們這裡討論的是垂直方向上的Grid,所以 mOrientation == VERTICA,從上面的程式碼可以看出當我們的itemView寬度不是精確數值的時候,然後測量出的寬度就為 Math.max(0, parentSize – padding)(這裡的 padding 就是 horizontalInsets = decorInsets.left + decorInsets.right + lp.leftMargin + lp.rightMargin),原來這裡在實際的寬度下還減去了ItemDecoration的左右偏移量,這也就解釋了上面的那個問題。有人會問我們可不可以把寬度設定為固定值呢?可以當然是可以的,但是又會出現其他問題,下來你可以去嘗試一下,這裡我就不再去細究了。

一般情況下當 mOrientation == VERTICA 的時候itemView的寬度是 match_parent的,當 mOrientation == HORIZONTAL的時候itemView的高度就是 match_parent的,這樣才能更好的去適配各種螢幕的手機。

這裡我們找到了問題的原因所在,應該怎樣去解決呢? 其實也很簡單,就是均勻的分配offset給每一個itemView。

下面我們來計算一下偏移量。

// 每一個itemView的總偏移量(left+right)
eachOffset =(spanCount-1)* dividerWidth / spanCount;

L0=0 , R0=eachOffset;
L1=dividerWidth-R0 , R1=eachOffset-L1;
L2=dividerWidth-R1 , R2=eachOffset-L2;

其中:
Ln:表示第n列itemView left 偏移量。
Rn:表示第n列itemView right 偏移量。

可能有些同學看到上面式子會有點凌亂,這裡我直接告訴你最後推算出的結論好了,Ln 是一個以 dividerWidth-eachOffset 為差值的一個等差數列,Rn就等於 eachWidth-Ln。所以我們最後對 getItemOffsets 做了改進,程式碼如下:

123456789101112131415 intleft=0;inttop=0;intright=0;intbottom=0;inteachWidth=(spanCount-1)*mDividerHeight/spanCount;intdl=mDividerHeight-eachWidth;left=itemPosition%spanCount *dl;right=eachWidth-left;bottom=mDividerHeight;if(isLastRow){bottom=0;}outRect.set(left,top,right,bottom);

最後的效果圖如下:

完美的解決了上面出現的問題,這都是些細節上的問題,如果不怎麼注意,還真的很難去注意到,以後如果遇到其他類似的問題也可以很容易的解決了。本文只是討論了在使用ItemDecoration其中的一個問題,並不算難,但是也很重要,所以大家在平時的開發中還是應該多多注意細節上的問題。

最後送上本文原始碼地址:

順便給大家推薦一個十分強大的開源自定義的ItemDecoration ,適用於 LinearLayoutManager作為佈局管理器的RecyclerView : RecyclerView-FlexibleDivider

相關推薦

定義 ItemDecoration 這個問題真的注意

本文討論的是關於自定義ItemDecoration容易被忽略的問題,此文適合有過自定義ItemDecoration經驗的同學閱讀,還沒有學習過的可以先去看看相關文章再來看本文。 ItemDecoration 我相信只要使用過RecyclerView的同學肯定都比較熟悉了,我們在使用 RecyclerView

Android定義View實現類似車來軌跡圖

總體分析下:水平方向recyclewview,item包含定位點,站臺位置和站臺名稱。 下面看實現: 1.繼承framelayout,實現構造方法: public class BusStopPlateView extends FrameLayout { ... public

Spark定義累加器的實現需要注意的細節(java版)

可以參考下面博文 !!!!!! 需要注意的是 ,原始碼中給出 也就是說兩個方法的實現是不一樣的。 下面是我的實現 import constant.Constants; import org.apache.spark.AccumulatorParam;

Android定義View教一步一步實現即刻點贊效果

前言 今天朋友看了HenCoder的自定義View後說,HenCoder對自定義View講的不錯。實踐中仿寫即刻的點贊你有思路嗎,你不實現一下?二話不說,看了朋友手機效果,對他說:實現不難,用到了位移,縮放,漸變動畫和自定義View的基礎用法,好,那我實現一下,剛好加深對自定義View的理解。 素材準備

只有懂這種程式語言人才能看懂這個笑話,看懂

  下面這幾個笑話都涉及到計算機相關或程式設計相關的知識,有些笑話是隻有會使用這種語言的人才能看到其中的可笑之處,當然了,還要自身有幽默感的人才會笑!你看懂了麼? 一、這個笑話需要你知道計算機基礎知識 問:為什麼程式設計師總是分不清萬聖節和聖誕節? 答:因為Oct 31 == Dec 25! 二、這

Android定義View:需要一個簡單好用、含歷史搜尋記錄的搜尋框

前言 Android開發中,類似下圖的搜尋功能非常常見 今天,我將帶來一款 封裝了 歷史搜尋記錄功能 & 樣式 的Android 自定義搜尋框 開源庫,希望你們會喜歡。

android 定義Button,滿足對Button呈現樣式的一系列要求

我們平時自定義的一些Button通常是改改顏色啊,文字啊,圓角啊之類的。如果要做到再高階點就需要用一個大布局還拼接了,如果一個頁面有很多樣式不同的這樣的按鈕,還真不怎麼好操作。 今天給大家上一個FancyButton能很好的幫助我們簡化這些操作 專案結構

RecycleView定義ItemDecoration,實現時間軸效果

最近進行知識點掃盲,關於RecycleView的進行整理,自定義ItemDecoration,實現時間軸效果,先上圖: 此文參考了Carson_Ho的部落格:http://www.jianshu.com/p/9a796bb23a47,在此特別感謝,感覺怎麼

定義控制元件-----定義數字鍵盤,適配正則表示式的帶輸出字串的控制元件

一、主要的類:CustomAmountInputKeyboard class CustomAmountInputKeyboard constructor(context: Context, attrs: AttributeSet? = null) : LinearLayout(context,

資料科學與資料分析傻傻分不清楚?看完這個就懂

導讀:大資料已經成為當今科技界的一個重要組成部分,這要歸功於那些企業可以收集到的切實可行的見解和

定義一個類,封裝矩形的長和寬;在定義一個類,繼承定義這個類,在繼承類中根據基類中封裝的矩形的長和寬求矩形的面積。

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 封裝長方體 {    

android定義Toast——讓應用別具一格

實際上,在github上層看到這種toast,但我的感覺是:用起來太麻煩了!我需要的要求是越簡單越好! 簡化Toast 原toast形式: Toast.makeText(this,”t”,Toast.LENGTH_LONG).show();或T

kafka訊息會不會丟失?為什麼?看這個就清楚

訊息傳送方式 想清楚Kafka傳送的訊息是否丟失,需要先了解Kafka訊息的傳送方式。 Kafka訊息傳送分同步(sync)、非同步(async)兩種方式 預設是使用同步方式,可通過producer.type屬性進行配置; Kafka保證訊息被安全生產,有三

定義UTI 註冊的APP所支援的檔案型別

之前有整理過關於《根據檔案字尾開啟APP》的文章 ,請先參考它,然後接下來學習,如何自定UTI。 應用場景:APP 開啟本公司自定義格式的檔案,特殊的自定義字尾的檔案。通過QQ 微信、郵箱等等接受到手機上的特殊檔案,在開啟時,主動呼叫自己的APP。 要點:

定義值型別一定不要忘重寫Equals,否則效能和空間雙雙堪憂

## 一:背景 ### 1. 講故事 曾今在專案中發現有同事自定義結構體的時候,居然沒有重寫Equals方法,比如下面這段程式碼: ``` C# static void Main(string[] args) { var list = Enumerable.Ran

Android開發入門的正確姿勢,get到

開源 如何 com 正常 它的 接收 應用 切換 角度 在進行Android開發之前,我們先了解一下Android的生態圈現狀。Android系統是開源的,任何手機廠商和開發者都有權限去修改系統源代碼,定制專屬的系統。 這就產生了一個問題,不同手機廠商之間的ROM可能無法安

C#進階系列——WebApi 路由機制剖析:準備好

事先 blank path can tex 全局配置 dex 找不到 save 前言:從MVC到WebApi,路由機制一直是伴隨著這些技術的一個重要組成部分。 它可以很簡單:如果你僅僅只需要會用一些簡單的路由,如/Home/Index,那麽你只需要配置一個默認路由就能簡

大學生求職難,關卡在哪?真正懂

eight 聲明 道路 每年 .cn .com 求職 公司 技能 上大學、找工作、結婚、生子——原以為人生可以這樣按部就班地過下去。可誰知,既定軌跡才剛行進到第二步,就意外地卡了殼。現在,越來越多的大學畢業生找工作屢屢碰壁,“沒有經驗"成了他們被拒之門外的理由;更有

PostgreSQL 圖形化客戶端工具的使用技巧都get

scrip pos sta har 但是 聚類 發現 功能 containe PostgreSQL 數據庫作為目前功能較強大的開源數據庫,得到了廣泛應用。其中,TSA就用到了這款數據庫來存儲處理後的一些業務數據。雖然PostgreSQL自身提供了命令行交互式客戶端工具psq

大數據的正確用法get到

方式 cnblogs 化工 海量數據 osql lin target 微軟 業務 Azure 鏡像市場已於2016年9月21日正式上線,在這個統一的集成平臺中,客戶可以輕松地瀏覽、搜索和選擇一系列來自第三方的應用和解決方案,並可以將其快速一鍵部署到 Azure 實例當中。