自定義控制元件三部曲檢視篇(五)——RecyclerView系列之二ItemDecoration
從來不跌倒不算光彩,每次跌倒後能再站起來,才是最大的榮耀。
一、新增分割線
1.1 引入ItemDecoration
在上一篇中,我們講解了RecyclerView的基本使用方法,但有個問題:為什麼Item之間沒有分割線呢?其實,給RecyclerView新增分割線也非常簡單,只需要新增上一句話:
DividerItemDecoration mDivider = new DividerItemDecoration(this,DividerItemDecoration.VERTICAL);
mRecyclerView.addItemDecoration(mDivider);
完整的程式碼如下:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_linear);![在這裡插入圖片描述](https://img-blog.csdn.net/20181007175450753?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2hhcnZpYzg4MDkyNQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
generateDatas ();
RecyclerView mRecyclerView = (RecyclerView) findViewById(R.id.linear_recycler_view);
//線性佈局
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
mRecyclerView.setLayoutManager(linearLayoutManager) ;
//初始化分隔線、新增分隔線
DividerItemDecoration mDivider = new DividerItemDecoration(this, DividerItemDecoration.VERTICAL);
mRecyclerView.addItemDecoration(mDivider);
RecyclerAdapter adapter = new RecyclerAdapter(this, mDatas);
mRecyclerView.setAdapter(adapter);
}
這裡實現的效果如下圖所示:
可以看到,這裡只需要新增一句: mRecyclerView.addItemDecoration(mDivider);
就可以給底部添加了一條橫線,那什麼是ItemDecoration呢?
首先,我們肯定理解什麼是Item,在這個佈局中,每個Item都單獨佔一行,在沒加ItemDecoration時,下面紅線框中就是一個Item:
1.2 什麼是ItemDecoration
那ItemDecoration與Item是什麼關係呢?對於英語來講,Decoration是裝飾的意思,ItemDecoration就是Item的裝飾。在Item的四周,我們可以給它新增上自定義的裝飾,比如剛才的橫線,就是在底部給它新增一個橫線的裝飾。同樣的,我們也可以在Item的上下左右各新增裝飾,而且這些裝飾是允許我們自定義的。系統只給我們提供了一個現成的Decoration類就是剛才使用的DividerItemDecoration,如果我們想實現其它的裝飾效果,就需要自定義了。下面這些漂亮的效果都可以使用自定義ItemDecoration來實現:
2、 自定義ItemDecoration
2.1 getItemOffsets
2.1.1 getItemOffsets的意義
當我們要重寫ItemDecoration時,主要涉及到三個函式:
public class LinearItemDecoration extends RecyclerView.ItemDecoration {
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
}
@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDrawOver(c, parent, state);
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
}
}
這三個函式我們會逐個講解,首先,我們來看看getItemOffsets:
getItemOffsets的主要作用就是給item的四周加上邊距,實現的效果類似於margin,將item的四周撐開一些距離,在撐開這些距離後,我們就可以利用上面的onDraw函式,在這個距離上進行繪圖了。在瞭解了getItemOffsets的作用之後,我們來看看這個函式本身:
getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state)
- Rect outRect:這個是最難理解的部分,outRect就是表示在item的上下左右所撐開的距離,後面詳細講解。
- View view:是指當前Item的View物件
- RecyclerView parent: 是指RecyclerView 本身
下面我們專門來看看Rect outRect
這個引數,outRect 中的 top、left、right、bottom四個點,並不是普通意義的座標點,而是指的在Item上、左、右、下各撐開的距離,這個值預設是0,示意圖如下所示:
2.1.2 getItemOffsets示例
我們知道,想要實現分隔線,有一種方法是在Item的上方空出一畫素的間隔,這樣就會漏出底線,看起來就是分割線了。所以我們回到剛才的示例,去掉DividerItemDecoration,改為自定義的LinearItemDecoration。
首先,給整個Activity新增上一個紅色的背景色:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ff0000"
tools:context=".LinearActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/linear_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
之後,給每個Item新增上預設的背景色白色,這樣有白色的地方就不會透出背景色的紅色了,而沒有白色的地方就會露出紅色:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:background="@android:color/white"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/item_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="10dp" />
</LinearLayout>
然後就是自定義LinearItemDecoration :
public class LinearItemDecoration extends RecyclerView.ItemDecoration {
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
}
@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDrawOver(c, parent, state);
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
outRect.top=1;
}
}
在這裡,我們將item上方面所撐開的距離硬編碼為1px;
最後,將LinearItemDecoration新增進RecyclerView:
public class LinearActivity extends AppCompatActivity {
private ArrayList<String> mDatas = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_linear);
generateDatas();
RecyclerView mRecyclerView = (RecyclerView) findViewById(R.id.linear_recycler_view);
…………
//新增分隔線
mRecyclerView.addItemDecoration(new LinearItemDecoration());
RecyclerAdapter adapter = new RecyclerAdapter(this, mDatas);
mRecyclerView.setAdapter(adapter);
}
//其它程式碼,參考原始碼
…………
}
效果如下圖所示:
可以看到每個Item的上方都出現了一條紅線。尤其從第一個Item可以看出來。
同樣的,如果我們改為底部1px,左側50px,右側100px:
public class LinearItemDecoration extends RecyclerView.ItemDecoration {
…………
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
outRect.left=50;
outRect.right=100;
outRect.bottom=1;
}
}
看下效果:
從第一個Item可以看出,頂部是沒有紅線的,因為我們沒有設定outRect.top,所以它預設是0,因為outRect.right=100,而outRect.left=50,明顯可以看出右側的紅色寬度是左側的兩倍。
2.2 onDraw
2.2.1 onDraw的用法
在理解了getItemOffsets的用法以後,我們再來看看onDraw函式:
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
}
onDraw函式有三個引數,RecyclerView parent, RecyclerView.State state的意義與getItemOffsets的相同。而
- Canvas c: 是指通過getItemOffsets撐開的空白區域所對應的畫布,通過這個canvas物件,可以在getItemOffsets所撐出來的區域任意繪圖。
那這個就厲害了,我們知道Canvas是有非常豐富的繪圖函式的,那我們先來個簡單的,通過getItemOffsets將Item的左側撐出來150的距離,然後在中間畫一個圓形:
public class LinearItemDecoration extends RecyclerView.ItemDecoration {
private Paint mPaint;
public LinearItemDecoration(){
mPaint = new Paint();
mPaint.setColor(Color.GREEN);
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
int childCount = parent.getChildCount();
for (int i=0;i<childCount;i++){
View child = parent.getChildAt(i);
int cx = 100;
int cy = child.getTop()+child.getHeight()/2;
c.drawCircle(cx,cy,20,mPaint);
}
}
@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDrawOver(c, parent, state);
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
outRect.left=200;
outRect.bottom=1;
}
}
首先,在getItemOffsets中,將左側撐出200px的距離,同樣底部留出1px的空間以顯示底部分割線。
然後在onDraw中,在每個Item的左側中間畫上半徑為20的綠色圓形。
效果如下圖所示:
需要注意的是,getItemOffsets是針對每個Item都會走一次,也就是說每個Item的outRect都可以不同,但是onDraw和onDrawOver所整個ItemDecoration只執行一次的,並不是針對Item的,所以我們需要在onDraw和onDrawOver中繪圖時,一次性將所有Item的ItemDecoration繪製完成。從上面也可以看出,這裡在onDraw函式中繪圖時,通過for迴圈對每一個item畫上一個綠色圓。
拓展:獲取outRect的各個值
在上面的例子中,我們onDraw中使用到outRect的值時,都是直接使用的數字硬編碼,比如在outRect是我們將左側撐開的距離設定為200,所以畫圓的中心點的X座標就是100,所以在onDraw函式中直接使用了int cx = 100;
很明顯,在實際工作中要嚴格避免類似的硬編碼,因為硬編碼會使程式碼變得極其難以維護。那我們怎麼在程式碼中獲取到getItemOffsets中所設定的各個item的outRect的值呢?
可以通過LayoutManager來獲取,方法如下:其中parent是指RecylerView本身,而child是指RecyclerView的Item的View物件
RecyclerView.LayoutManager manager = parent.getLayoutManager();
int left = manager.getLeftDecorationWidth(child);
int top = manager.getTopDecorationHeight(child);
int right = manager.getRightDecorationWidth(child);
int bottom = manager.getBottomDecorationHeight(child);
所以我們上面在onDraw函式中的硬編碼,就可以用下面的動態獲取程式碼來代替:
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
int childCount = parent.getChildCount();
RecyclerView.LayoutManager manager = parent.getLayoutManager();
for (int i=0;i<childCount;i++){
View child = parent.getChildAt(i);
//動態獲取outRect的left值
int left = manager.getLeftDecorationWidth(child);
int cx = left/2;
int cy = child.getTop()+child.getHeight()/2;
c.drawCircle(cx,cy,20,mPaint);
}
}
到這,大家實現開頭講解的這個效果應該不難了:
該工程原始碼地址:https://github.com/vipulasri/Timeline-View ,他是通過自定義View來實現的,大家也可以嘗試通過RecyclerView的ItemDecoration來實現出來。
2.2.2 onDraw的問題
如果我們在將上面畫圓的例子修改下,將畫圓改為繪製一個圖片:
程式碼如下:
public class LinearItemDecoration extends RecyclerView.ItemDecoration {
private Paint mPaint;
private Bitmap mBmp;
public LinearItemDecoration(Context context) {
mPaint = new Paint();
mPaint.setColor(Color.GREEN);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2;
mBmp = BitmapFactory.decodeResource(context.getResources(),R.mipmap.icon,options);
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
c.drawBitmap(mBmp,0,child.getTop(), mPaint);
}
}
@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDrawOver(c, parent, state);
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
outRect.left = 200;
outRect.bottom = 1;
}
}
首先,因為圖片比較大,在LinearItemDecoration初始化的時候,通過options.inSampleSize引數將圖片縮放小為原大小的1/2;
public LinearItemDecoration(Context context) {
…………
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2;
mBmp = BitmapFactory.decodeResource(context.getResources(),R.mipmap.icon,options);
}
同樣,在getItemOffsets中,將左側邊距設定為200,底部預留一畫素顯示分割線。
最後,在onDraw中,將圖形在每個Item的左上角顯示出來。
效果如下圖所示:
如果我們把bitmap縮放去掉:
public LinearItemDecoration(Context context) {
BitmapFactory.Options options = new BitmapFactory.Options();
//options.inSampleSize = 2;
mBmp = BitmapFactory.decodeResource(context.getResources(),R.mipmap.icon,options);
}
效果圖如下:
可以看到當圖片過大時,在超出getItemOffsets函式所設定的outRect範圍的部分將是不可見的。這是因為在整個繪製流程中,是選呼叫ItemDecoration的onDraw函式,然後再呼叫Item的onDraw函式,最後呼叫ItemDecoration的onDrawOver函式。
所以在ItemDecoration的onDraw函式中繪製的內容,當超出邊界時,會被Item所覆蓋。但是因為最後才呼叫ItemDecoration的OnDrawOver函式,所以在onDrawOver中繪製的內容就不受outRect邊界的限制,可以覆蓋Item的區域顯示。
2.3 onDrawOver
上面我們已經提到,ItemDecoration與Item的繪製順序為:decoration 的 onDraw->item的 onDraw->decoration 的 onDrawOver,這三者是依次發生的。
所以,onDrawOver 是繪製在最上層的,所以它的繪製位置並不受限制(當然,decoration 的 onDraw 繪製範圍也不受限制,只不過不可見,被item所覆蓋),所以利用 onDrawOver 可以做很多事情,例如為 RecyclerView 整體頂部繪製一個蒙層、超出itemDecoration的範圍繪製圖像。
比如,我們實現下面這樣的效果:
在這個效果中,我們在最頂部繪製了一個漸變蒙版,而且每隔五個item繪製一個勳章。動圖效果是這樣的:
可見這個蒙層是一直顯示在頂部的。下面我們就來看看具體 實現吧。
1、新增圖片
第一步,當然是將勳章圖片(xunzhang.png)加入res/mipmap資料夾中或者res/drawable資料夾中。
2、初始化
然後在LinearItemDecoration初始化時,將圖片轉為bitmap物件:
public class LinearItemDecoration extends RecyclerView.ItemDecoration {
private Paint mPaint;
private Bitmap mMedalBmp;
public LinearItemDecoration(Context context) {
mPaint = new Paint();
mPaint.setColor(Color.GREEN);
BitmapFactory.Options options = new BitmapFactory.Options();
mMedalBmp = BitmapFactory.decodeResource(context.getResources(), R.mipmap.xunzhang);
}
}
3、繪製勳章
在onDrawOver中將勳章每隔五個item繪製出來
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDrawOver(c, parent, state);
//畫勳章
RecyclerView.LayoutManager manager = parent.getLayoutManager();
int childCount = parent.getChildCount
相關推薦
自定義控制元件三部曲檢視篇(五)——RecyclerView系列之二ItemDecoration
從來不跌倒不算光彩,每次跌倒後能再站起來,才是最大的榮耀。
一、新增分割線
1.1 引入ItemDecoration
在上一篇中,我們講解了RecyclerView的基本使用方法,但有個問題:為什麼Item之間沒有分割線呢?其實,給RecyclerView新
自定義控制元件三部曲檢視篇(四)——RecyclerView系列之一簡單使用
絕望的時候不要那麼絕望,高興的時候不要那麼高興,是你慢慢會學會的。 ——董卿
轉了一年多,又回來繼續做Android。果然還是看到程式碼最讓我興奮……但有些事,沒經歷過,總歸還是遺憾的。在VIVO的遊戲中心,有一個特別炫酷的功能:
這個功能就是使
自定義控制元件三部曲檢視篇(三)——瀑布流容器WaterFallLayout實現
前言:只要在前行,夢想就不再遙遠
系列文章:
前面兩節講解了有關ViewGroup的onMeasure、onLayout的知識,這節我們深入性地探討一下,如何實現經常見到的瀑布流容器,本節將實現的效果圖如下:
從效果圖中可以看出這裡要完成的
C#自定義控制元件程式設計輕鬆入門(1)
前 言
話說,許多新手在接觸C#的時候都覺得C#使用起來特別容易方便,相對C++來說沒有那麼多的繁瑣,比如C++每次在使用一個函式,都要先在標頭檔案中宣告一遍,而C#宣告和實現都在一起,立馬可以用。而且不會一會要寫指標一會兒要寫引用,如果是遇到VC那些控制代碼就把頭給搞大。
隨著
(四十九)c#Winform自定義控制元件-下拉框(表格)
前提
入行已經7,8年了,一直想做一套漂亮點的自定義控制元件,於是就有了本系列文章。
GitHub:https://github.com/kwwwvagaa/NetWinformControl
碼雲:https://gitee.com/kwwwvagaa/net_winform_custom_contr
自定義控制元件三部曲之繪圖篇(六)——Path之貝賽爾曲線和手勢軌跡、水波紋效果
前言:好想義無反顧地追逐夢想從這篇開始,我將延續androidGraphics系列文章把圖片相關的知識給大家講完,這一篇先稍微進階一下,給大家把《android Graphics(二):路徑及文字》略去的quadTo(二階貝塞爾)函式,給大家補充一下。 本篇最終將以兩個例子給
自定義控制元件三部曲之動畫篇(一)——alpha、scale、translate、rotate、set的xml屬性及用法
前言:這幾天做客戶回訪,感觸很大,使用者只要是留反饋資訊,總是一種恨鐵不成鋼的心態,想用你的app,卻是因為你的技術問題,讓他們不得不放棄,而你一個回訪電話卻讓他們盡釋前嫌,當最後把手機號留給他們以便隨時溝通的時候,總會發來一條條的鼓勵簡訊,讓我不自主的開始內疚。哎,多麼可愛
自定義控制元件三部曲之繪圖篇(十三)——Canvas與圖層(一)
前言:猛然知道姥姥79了,我好怕,好想哭系列文章:一、如何獲得一個Canvas物件方法一:自定義view時, 重寫onDraw、dispatchDraw方法(1)、定義 我們先來看一下onDraw、dispatchDraw方法的定義protected void onDraw(
自定義控制元件三部曲之動畫篇(二)——Interpolator插值器
前言:雖然我不太能欣賞的了帕爾哈提的音樂,但我確實很欣賞他的人生態度,專心做自己,不想名利得失,有一天,你想要的東西都會來。其實我覺得,人生最可怕的就是停止不前,只要一直前行,總有一天會到達人生巔峰。相關文章:一、概述Interpolator屬性是Animation類的一個X
自定義控制元件三部曲之動畫篇(十一)——layoutAnimation與gridLayoutAnimation
前言:人或許天生是懶惰的,明知道的不足,卻不努力彌補。
前幾篇給大家講述瞭如何針對某一個控制元件應用動畫,這篇將給大家講解如何給容器中的控制元件應用統一動畫。即在容器中控制元件出現時,不必為每個控制元件新增進入動畫,可以在容器中為其新增統一的進入和
自定義控制元件三部曲之繪圖篇(二十)——RadialGradient與水波紋按鈕效果
前言:每當感嘆自己的失敗時,那我就問你,如果讓你重新來一次,你會不會成功?如果會,那說明並沒有拼盡全力。
系列文章:
最近博主實在是太忙了,部落格更新實在是太慢了,真是有愧大家。
這篇將是Shader的最後一篇,下部分,我們將講述Canvas變
自定義控制元件三部曲之動畫篇(十)——聯合動畫的XML實現與使用示例
前言:不畏人生,或許才能方得始終;大膽拼,大膽闖是要有一定資本的,在能力不到的時候,就只有選擇忍氣吞聲!
上篇給大家講了有關AnimatorSet的程式碼實現方法,這篇我們就分別來看看如何利用xml來實現ValueAnimator、ObjectAn
自定義控制元件 輪盤 來源於GITHUB(記錄,筆記)
自定義控制元件:輪盤抽獎
-------邏輯程式碼(輪盤的類)首先要寫一個類繼承SurfaceView 實現Callback和Runnable方法:
//所使用的包
import android.content.Context;
import android.graphics.Ca
自定義控制元件 輪盤 來源於GITHUB(記錄,筆記)
自定義控制元件:輪盤抽獎
-------邏輯程式碼(輪盤的類)首先要寫一個類繼承SurfaceView 實現Callback和Runnable方法:
//所使用的包
import android.content.Context;
import android.
Android自定義控制元件--圓形進度條(中間有圖diao)
智慧家居越來越流行,在智慧家居中我們常要表現一些資料的百分比 圓形度條中間加個圖是一種非常流行的自定義View
1.第一步 你首先需要對類進行繼承View
public class CircleProgressImageView extends View
2.第二步 要實
Qt編寫自定義控制元件37-發光按鈕(會呼吸的痛)
一、前言
這個控制元件是好早以前寫的,已經授權過好幾個人開源過此控制元件程式碼,比如紅磨坊小胖,此控制元件並不是來源於真實需求,而
[C#] (原創)一步一步教你自定義控制元件——02,ScrollBar(滾動條)
一、前言
技術沒有先進與落後,只有合適與不合適。
本篇的自定義控制元件是:滾動條(ScollBar)。
我們可以在網上看到很多自定義的滾動條控制元件,它們大都是使用UserControl去做,即至少使用一個Panel或其它控制元件作滑塊,使用UserControl本身或另一個控制元件作為背景條,而有的複雜的還
[C#] (原創)一步一步教你自定義控制元件——03,SwitchButton(開關按鈕)
一、前言
技術沒有先進與落後,只有合適與不合適。
本篇的自定義控制元件是:開關按鈕(SwitchButton)。
開關按鈕非常簡單,實現方式也多種多樣,比如常見的:使用兩張不同的按鈕圖片,代表開和關,然後在點選時切換這兩張圖片。
而本篇和前兩篇一脈相承,都是繼承Control,使用GDI+去實現。因為都是相同
[C#] (原創)一步一步教你自定義控制元件——04,ProgressBar(進度條)
一、前言
技術沒有先進與落後,只有合適與不合適。
本篇的自定義控制元件是:進度條(ProgressBar)。
進度條的實現方式多種多樣,主流的方式有:使用多張圖片去實現、使用1個或2個Panel放到UserControl上去實現、過載系統進度條去實現等等。
本次所實現的進度條仍是使用GDI+去實現。當然,如果
[C#] (原創)一步一步教你自定義控制元件——05,Label(原生控制元件)
一、前言
技術沒有先進與落後,只有合適與不合適。
自定義控制元件可以分為三類:
一類是“無中生有”。就如之前文章中的的那些控制元件,都是繼承基類Control,來實現特定的功能效果;
一類是“有則改之”。是對原生控制元件的改造,以達到特定的功能效果;
一類是“使用者控制元件”。是將多個控制元件進行組合,以實現