ItemDecorationの使い方
7414 ワード
1、ItemDecoration概念
Recycler Viewを使用してリストを表示する場合は、itemを区切り線で区切る必要があります.この場合、Recyclerの内部クラスItemDecorationを使用できます.ItemDecorationはandroidシステムが提供するベースクラスであり、RecyclerViewの区切り線を描くために使用され、ItemDecorationを継承することで豊富な区切り線効果を実現することができます.(システムはまた、良いDividerItemDecorationを実現するために使用されています.)
2、ItemDecorationの使用
ItemDecorationを使用する場合は、このようなgetItemOffsets()メソッドとonDraw()メソッドを継承して上書きする必要があります.ここでgetItemOffsets()には、次の2つのリロード方法があります.
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state)
public void getItemOffsets(@NonNull Rect outRect, int itemPosition, @NonNull RecyclerView parent)
その中の2つ目の方法はもう古くなったので、お勧めしません.
3、getItemOffsets()
3.1、分析
Android公式サイトの説明は以下の通りです.
Retrieve any offsets for the given item. Each field of outRect specifies the number of pixels that the item view should be inset by, similar to padding or margin.
このメソッドのoutRectパラメータはitemとinset演算を行い,表示領域型ドメインを決定する.Rectのinset演算規則は以下の通りである.
上のコードから,outRectの4つの値に基づいてitemの領域を拡大し,分割線に描画する空間を残すことが分かる.
If you need to access Adapter for additional data, you can call getChildAdapterPosition(View) to get the adapter position of the View.
itemのデータ自然順序を取得する必要がある場合は、recyclerのgetChildAdapterPosition(View)メソッドを呼び出すことができます.
3.2、上書き
このメソッドを上書きする最も核心的なのはoutRectの値を定義することであり,すなわち分割線の描画領域を決定することである.注意:LinearLayoutManagerかGridLayoutManagerかを区別する必要があります.
次のコードはLinearLayoutを使用するときに区切り線を追加します.
各itemを描画すると、itemの対応するエッジに区切り線の幅を付けるメソッドがコールバックされます.開発中に最後の行や最後の列の区切り線を必要としない場合がある場合は、論理的な判断で区切り線の領域を設定できます.
次のように最終行かどうかを判断します.
次のように最終列かどうかを判断します.
このようにして、分割線の描画領域を0に設定すれば、分割線を表示しなくてもよい.LinearLayoutManagerでは、最後の要素かどうかを直接判断すればよい.しかし,実際の符号化過程では,このように処理が加わっても次のような問題があることが分かった.
recyclerviewの内容がrecyclerviewの高さを超えていない場合(つまりスクロールが発生していない場合)、分割線の幅の高さを予め残さなくても分割線が描画されます.
まだこの問題がはっきりしていませんか.
この問題は、onDrawメソッドでは描画を行わずに最後の行の要素のシーケンス番号を決定することによって回避できます.
4、onDraw方法分析
分割線の描画領域を決定したら、次に領域に基づいて具体的な内容を描画します.システムは、2つの分割線を描画する方法を提供します.
public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state)
このメソッドはitemを描画する前に呼び出されます
public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state)
このメソッドはitemを描画した後に呼び出され、実際の効果の両者の違いは明らかではなく、一般的に最初のメソッドが選択されます.
次のコードは、水平分割線(幅はitemと同じ)を描画し、描画領域はgetItemOffsets()に基づいて決定され(各itemの底辺で描画)、各itemは動的に計算する必要があります.
垂直分割線(高さはitemと同じ、各itemの右側に描画)を上に描画します.
5、描画領域の改善
分割線の幅を拡大すると、分割線がitemの幅を占め、最後の行のバンドや最後の列を描かないとitemの幅の大きさが一致しないことがわかりやすい.具体的な分析はこの文章を見て、分析が全面的だ.具体的には、各itemのオフセット幅を以下のように計算します.
eachWidth =(spanCount-1)* dividerWidth/spanCount;//spanCountは列数,dividerWidthは分割線幅である.(2つのdividerの幅を3つのitemに均等に分ける)
各itemによるrightと次のitemのleftの加算はdividerWidthに等しい.各itemのleft式を渡す
left = itemPosition % spanCount * (dividerWidth - eachWidth);//itemPositionはitemの順番です
では、rightは次のように表すことができます.
right = eachWidth - left;
したがってGridLayoutManagerでitemの幅が一致しない問題は以下のように修正できます.
6、オープンソースライブラリ及び参考 item decoration simple item decorationでは、StartOffsetItemDecorationがRecyclerViewのオフセットを追加することについて学ぶことができます. 幅ムラ問題 参照啓艦のこの文章はもっと理解しやすい.
Recycler Viewを使用してリストを表示する場合は、itemを区切り線で区切る必要があります.この場合、Recyclerの内部クラスItemDecorationを使用できます.ItemDecorationはandroidシステムが提供するベースクラスであり、RecyclerViewの区切り線を描くために使用され、ItemDecorationを継承することで豊富な区切り線効果を実現することができます.(システムはまた、良いDividerItemDecorationを実現するために使用されています.)
2、ItemDecorationの使用
ItemDecorationを使用する場合は、このようなgetItemOffsets()メソッドとonDraw()メソッドを継承して上書きする必要があります.ここでgetItemOffsets()には、次の2つのリロード方法があります.
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state)
public void getItemOffsets(@NonNull Rect outRect, int itemPosition, @NonNull RecyclerView parent)
その中の2つ目の方法はもう古くなったので、お勧めしません.
3、getItemOffsets()
3.1、分析
Android公式サイトの説明は以下の通りです.
Retrieve any offsets for the given item. Each field of outRect specifies the number of pixels that the item view should be inset by, similar to padding or margin.
このメソッドのoutRectパラメータはitemとinset演算を行い,表示領域型ドメインを決定する.Rectのinset演算規則は以下の通りである.
/**
* Insets the rectangle on all sides specified by the insets.
* @hide
* @param left The amount to add from the rectangle's left
* @param top The amount to add from the rectangle's top
* @param right The amount to subtract from the rectangle's right
* @param bottom The amount to subtract from the rectangle's bottom
*/
public void inset(int left, int top, int right, int bottom) {
this.left += left;
this.top += top;
this.right -= right;
this.bottom -= bottom;
}
上のコードから,outRectの4つの値に基づいてitemの領域を拡大し,分割線に描画する空間を残すことが分かる.
If you need to access Adapter for additional data, you can call getChildAdapterPosition(View) to get the adapter position of the View.
itemのデータ自然順序を取得する必要がある場合は、recyclerのgetChildAdapterPosition(View)メソッドを呼び出すことができます.
3.2、上書き
このメソッドを上書きする最も核心的なのはoutRectの値を定義することであり,すなわち分割線の描画領域を決定することである.注意:LinearLayoutManagerかGridLayoutManagerかを区別する必要があります.
次のコードはLinearLayoutを使用するときに区切り線を追加します.
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
LinearLayoutManager lm = (LinearLayoutManager) parent.getLayoutManager();
if (lm.getOrientation() == LinearLayoutManager.HORIZONTAL) {
//
outRect.set(0, 0, mDividerWidth, 0); // item , 10
} else {
//
outRect.set(0, 0, 0, mDividerWidth); // item , 10
}
}
各itemを描画すると、itemの対応するエッジに区切り線の幅を付けるメソッドがコールバックされます.開発中に最後の行や最後の列の区切り線を必要としない場合がある場合は、論理的な判断で区切り線の領域を設定できます.
次のように最終行かどうかを判断します.
private boolean isLastRow(GridLayoutManager layoutManager, RecyclerView parent, View view) {
int spanCount = layoutManager.getSpanCount(); //
int itemCount = parent.getAdapter().getItemCount();
int currentPosition = parent.getChildAdapterPosition(view);
int lastRowCount = itemCount % spanCount == 0 ? spanCount : itemCount % spanCount;
if ((currentPosition + lastRowCount) - itemCount >= 0) {
return true;
}
return false;
}
次のように最終列かどうかを判断します.
private boolean isLastColumn(GridLayoutManager layoutManager, RecyclerView parent, View view) {
int spanCount = layoutManager.getSpanCount(); // , ,
int currentPosition = parent.getChildAdapterPosition(view);
if ((currentPosition + 1) % spanCount == 0) {
return true;
}
return false;
}
このようにして、分割線の描画領域を0に設定すれば、分割線を表示しなくてもよい.LinearLayoutManagerでは、最後の要素かどうかを直接判断すればよい.しかし,実際の符号化過程では,このように処理が加わっても次のような問題があることが分かった.
recyclerviewの内容がrecyclerviewの高さを超えていない場合(つまりスクロールが発生していない場合)、分割線の幅の高さを予め残さなくても分割線が描画されます.
まだこの問題がはっきりしていませんか.
この問題は、onDrawメソッドでは描画を行わずに最後の行の要素のシーケンス番号を決定することによって回避できます.
4、onDraw方法分析
分割線の描画領域を決定したら、次に領域に基づいて具体的な内容を描画します.システムは、2つの分割線を描画する方法を提供します.
public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state)
このメソッドはitemを描画する前に呼び出されます
public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state)
このメソッドはitemを描画した後に呼び出され、実際の効果の両者の違いは明らかではなく、一般的に最初のメソッドが選択されます.
次のコードは、水平分割線(幅はitemと同じ)を描画し、描画領域はgetItemOffsets()に基づいて決定され(各itemの底辺で描画)、各itemは動的に計算する必要があります.
private void drawHorizontal(@NonNull RecyclerView parent, Canvas canvas) {
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
RecyclerView.LayoutParams rlp = (RecyclerView.LayoutParams) child.getLayoutParams();
int left = child.getLeft() - rlp.leftMargin;
int right = child.getRight();
int top = child.getBottom() + rlp.bottomMargin;
int bottom = top + mDividerWidth;
p.setColor(Color.GRAY);
Rect r = new Rect(left, top, right, bottom);
canvas.drawRect(r, p);
}
}
垂直分割線(高さはitemと同じ、各itemの右側に描画)を上に描画します.
private void drawVertical(@NonNull RecyclerView parent, Canvas canvas) {
//
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
RecyclerView.LayoutParams rlp = (RecyclerView.LayoutParams) child.getLayoutParams();
int left = child.getRight() + rlp.rightMargin;
int top = child.getTop() - rlp.topMargin;
int right = left + 10;
int bottom = child.getBottom() + rlp.bottomMargin;
p.setColor(Color.RED);
Rect r = new Rect(left, top, right, bottom);
canvas.drawRect(r, p);
}
}
5、描画領域の改善
分割線の幅を拡大すると、分割線がitemの幅を占め、最後の行のバンドや最後の列を描かないとitemの幅の大きさが一致しないことがわかりやすい.具体的な分析はこの文章を見て、分析が全面的だ.具体的には、各itemのオフセット幅を以下のように計算します.
eachWidth =(spanCount-1)* dividerWidth/spanCount;//spanCountは列数,dividerWidthは分割線幅である.(2つのdividerの幅を3つのitemに均等に分ける)
各itemによるrightと次のitemのleftの加算はdividerWidthに等しい.各itemのleft式を渡す
left = itemPosition % spanCount * (dividerWidth - eachWidth);//itemPositionはitemの順番です
では、rightは次のように表すことができます.
right = eachWidth - left;
したがってGridLayoutManagerでitemの幅が一致しない問題は以下のように修正できます.
if (parent.getLayoutManager() instanceof GridLayoutManager) {
GridLayoutManager layoutManager = (GridLayoutManager) parent.getLayoutManager();
int itemPosition = ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition();
int spanCount = layoutManager.getSpanCount(); //
boolean isLastRow = isLastRow(layoutManager, parent, view);
int top = 0;
int left;
int right;
int bottom;
int eachWidth = (spanCount - 1) * mDividerWidth / spanCount;
left = itemPosition % spanCount * (mDividerWidth - eachWidth);
right = eachWidth - left;
bottom = mDividerWidth;
if (isLastRow){
bottom = 0;
}
outRect.set(left, top, right, bottom);
}
6、オープンソースライブラリ及び参考