本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布
毫无疑问,RecyclerView 是现在 Android 世界中最重要的系统组件之一,它的出现就是为了高效代替 ListView 和 GridView。当时它的出现解决了我一个大的需求,这个需求就是在电视盒子界面上横向加载应用列表,由于 ListView 没有横向加载的功能,而网络上开源的那些 HorizontalListView 又不满足需求,所以我们只能自定义 ViewGroup 来实现需求,但是回收机制不是很完善,所以性能并不好,所以当 RecyclerView 横空出世时,我第一时间拥抱了它,并推荐 Android 开发小组成员们去了解它。
但后来,我发现 RecyclerView 除了比 ListView 好用外,某些地方它却更复杂了,它将更多的权力交给了开发者自己,比如布局,比如 ITEM 的分割线,比如点击监听等等。但总归它是好东西,所以我们得多花些时间来学习,平常开发我们一般按照 RecyclerView 的基本用法便可以实现绝大多数需求,但是某些场景下却远远不够,比如我们不想局限于 LinearLayoutManager 想自己定义 LayoutManager,我们需要定义时光轴的效果,我们想实现美妙的添加删除动画等等,这些情况下解决问题的话需要我们对 RecyclerView 本身有足够的了解。
今天,这篇文章不讲 RecyclerView 基本的知识和用法,讲它一个有趣的知识点 ItemDecoration。
ItemDecoration
Decoration 的英文意思是装饰物的意思,引申到这里来,肯定也是与 RecyclerView 的界面装饰有关。我们常见的就是分割线了。
我们在使用 ListView 的时候只要在 xml 文件中,使用 android:divider
就可以,但是很遗憾 RecyclerView 却没有相应的控制。
我们新建一个工程,然后在一个页面里面添加一个 RecyclerView。创建相关的 Adapter,加载布局文件,这里布局文件很简单,就是一个 TextView,再之后在 Activity 初始化它。
public class DividerActivity extends AppCompatActivity {
RecyclerView mRecyclerView;
List<String> data;
TestAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_divider);
mRecyclerView = (RecyclerView) findViewById(R.id.divider_recyclerview);
initDatas();
mAdapter = new TestAdapter(data);
mRecyclerView.setAdapter(mAdapter);
LinearLayoutManager layoutmanager = new LinearLayoutManager(this);
layoutmanager.setOrientation(LinearLayoutManager.VERTICAL);
mRecyclerView.setLayoutManager(layoutmanager);
}
private void initDatas() {
data = new ArrayList<>();
for (int i = 0; i < 56;i++) {
data.add(i+" test ");
}
}
}
可以看到所有的选项都混在一起,为了美观应该需要 1 px 的分割线,之前我一般在 Item 的布局文件中设置它的 topMargin 或者是 bottomMargin,所以我们可以在相关的 Adapter 中这样修改。
public TestHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_item,parent,false);
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view.getLayoutParams();
layoutParams.topMargin = 1;
view.setLayoutParams(layoutParams);
TestHolder holder = new TestHolder(view);
return holder;
}
效果如下:
现在我们同样可以通过给 RecyclerView 添加 ItemDecoration 来实现它。
首先,我们需要自定义一个 ItemDecoration,按照目前的需求,我们只需要实现它的一个方法就可以了。
public class TestDividerItemDecoration extends RecyclerView.ItemDecoration {
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
// //如果不是第一个,则设置top的值。
if (parent.getChildAdapterPosition(view) != 0){
//这里直接硬编码为1px
outRect.top = 1;
}
}
}
然后在 Activity 中添加它到 RecyclerView 就可以了。
mRecyclerView = (RecyclerView) findViewById(R.id.divider_recyclerview);
initDatas();
mAdapter = new TestAdapter(data);
mRecyclerView.setAdapter(mAdapter);
LinearLayoutManager layoutmanager = new LinearLayoutManager(this);
layoutmanager.setOrientation(LinearLayoutManager.VERTICAL);
mRecyclerView.setLayoutManager(layoutmanager);
mRecyclerView.addItemDecoration(new TestDividerItemDecoration());
效果如图:
getItemOffsets()
我们可以看到自定义的 TestDividerItemDeoration 只实现了一个方法 getItemOffsets()。方法里面有四个参数。
- Rect outRect
- View view
- RecyclerView parent
- RecyclerView.State state
这四个参数分别干什么的呢?我们不妨在 AndroidStudio 中按 Ctrl 键点击方法名,就可以到了它被调用的位置。
Rect getItemDecorInsetsForChild(View child) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (!lp.mInsetsDirty) {
return lp.mDecorInsets;
}
if (mState.isPreLayout() && (lp.isItemChanged() || lp.isViewInvalid())) {
// changed/invalid items should not be updated until they are rebound.
return lp.mDecorInsets;
}
final Rect insets = lp.mDecorInsets;
insets.set(0, 0, 0, 0);
final int decorCount = mItemDecorations.size();
for (