RecyclerView之ItemDecoration

RecyclerView应该是最常使用的几个Android原生组件了,深入了解RecyclerView的使用将是提升个人技能的必经之路。

首先我们通过三张效果图来看看ItemDecoration能做什么?第一张图仅添加了基础的分割线,第二张图添加了名次效果,第三张图则将各武将按照所属国家进行了分组显示,并针对分割线进行了些许调整。

实现分割线

针对第一张效果图,如果使用ListView的话,我们仅需添加以下两个属性即可实现分割线效果:

1
2
android:divider="@android:color/darker_gray"
android:dividerHeight="1dp"

可是现在ListView已经几乎被RecyclerView取代,而RecyclerView并不支持上述实现方式。那么如何在RecyclerView中实现分割线效果呢?答案是DividerItemDecorationDividerItemDecoration是Android提供的类,专供实现分割线效果。

通过查看源代码,可以注意到DividerItemDecoration默认会去寻找listDivider属性。

1
private static final int[] ATTRS = new int[]{ android.R.attr.listDivider };

在当前theme中设置该属性:

1
2
3
4
5
6
7
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:listDivider">@color/colorAccent</item>
</style>

如果对分割线有颜色以及高度要求,也可以自定义shape来实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- styles.xml -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="android:listDivider">@drawable/defalut_divider</item>
</style>

<!-- defalut_divider.xml -->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@android:color/darker_gray"/>
<size android:height="5dp"/>
</shape>

<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@android:color/darker_gray"/>
<gradient
android:endColor="#00FFFF"
android:startColor="#FF0000"/>
<size android:height="2dp"/>
</shape>

以上就是使用DividerItemDecoration配合自定义shape来画分割线的基本流程,基本能完成简单的分割线绘制要求。但如果我们需要实现一开始图三的分割线,DividerItemDecoration就无法实现了,因为它画出的分割线对于所有itemView来说是一样的,无法进行动态设定。如果是以前用ListView来实现该效果的话,通常会把不同的分割线画在item.xml里面,然后通过代码来动态判断显示哪一条:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!-- item.xml -->
<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="56dp"
android:orientation="vertical">
<View
android:id="@+id/divider_short"
android:layout_width="match_parent"
android:layout_height="1dp"
android:paddingStart="16dp"/>

<View
android:id="@+id/divider_long"
android:layout_width="match_parent"
android:layout_height="1dp"
android:paddingStart="16dp"/>

<!-- Your main content -->
</LinearLayout>
1
2
3
4
5
6
7
if (显示长分割线) {
divider_long.setVisibility(View.VISIBLE);
divider_short.setVisibility(View.GONE);
} else {
divider_long.setVisibility(View.GONE);
divider_short.setVisibility(View.VISIBLE);
}

但是通过该方式设置分割线,对代码具有较强的侵入性,UI和业务的耦合比较大,不符合通常的代码规范。也正是因为ListView在一系列的扩展与性能上都已经跟不上我们的设计要求的情况下,对RecyclerView的使用才变得越来越重要。

认识装饰器

ItemDecoration字面翻译过来就是起到一个对RecyclerView每一行子布局的装饰效果。它不再满足于对简单线条的绘制,只要你会Canvas和自定义View简单使用,你就可以对Item绘制任何你想要的内容。

首先来认识一下ItemDecoration类:

1
2
3
4
5
6
7
8
9
10
11
public abstract static class ItemDecoration {

public void onDraw(Canvas c, RecyclerView parent, State state) {}

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

public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(),
parent);
}
}

根据定义我们大概知道这3个方法各自具有的功能:

  • onDraw:将内容绘制在原先itemView的下方
  • onDrawOver:将内容绘制在原先itemView的上方
  • getItemOffsets:确定每个itemView的偏移距离

首先需要来看一下getItemOffsets,确定了偏移距离,才能决定onDrawonDrawOver的可绘制局域。

0%