preface
RecyclerView for everyone, the basic use of RecyclerView should already be more familiar, here I will directly skip, talk about ItemDecoration, also can be considered to know the new.
- RecyclerView ItemDecoration (a)
- RecyclerView Cache (2)
- 【Android advanced 】RecyclerView drawing process (three)
- RecyclerView group list add top effect (4)
What is theItemDecoration
From the literal meaning, it is decoration, decoration RecyclerView. How do you understand this? Just a few examples. For example, how should RecyclerView add spacing line? So this is going to be ItemDecoration
DividerItemDecoration mDivider = new DividerItemDecoration(this, DividerItemDecoration.VERTICAL);
rvDemo.addItemDecoration(mDivider);
Copy the code
DividerItemDecoration inherits from ItemDecoration
public class DividerItemDecoration extends ItemDecoration {}
Copy the code
How to write a cool effect?
This involves customizing item decorations
To customize ItemDecoration, three methods are mainly involved
1.getItemOffsets
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {}
Copy the code
Where, outRect is the spread distance of the item, which defaults to 0. The view as the item; Parent is RecyclerView itself; State is RecyclerView state, which can also be used to transfer parameters between components, which is not involved here temporarily.
For example
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ff0000"
tools:context=".deco.DecoActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/rv_demo"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.constraint.ConstraintLayout>
Copy the code
Define a DemoAdapter, BaseRecyclerViewAdapterHelper is used here
public class DemoAdapter extends BaseQuickAdapter<String.BaseViewHolder> {
public DemoAdapter(@Nullable List<String> data) {
super(R.layout.item_demo, data);
}
@Override
protected void convert(BaseViewHolder helper, String item) { helper.setText(R.id.tv_demo, item); }}Copy the code
Then assemble the data and add ItemDecoration
List<String> list = new ArrayList<>();
for (int i = 0; i < 100; i++) {
list.add("The first" + i + "Item");
}
DemoAdapter demoAdapter = new DemoAdapter(list);
rvDemo.setLayoutManager(new LinearLayoutManager(this));
rvDemo.addItemDecoration(new DemoDecoration());
rvDemo.setAdapter(demoAdapter);
Copy the code
DemoDecoration#getItemOffsets
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
//outRect is the distance between the upper, lower, and left sides of the item. The default value is 0
outRect.set(10.20.30.40);
}
Copy the code
As you can see, each item executes getItemOffsets, so we can use this to add spacing lines to RecyclerView. For example, if I set bottom to 1, I won’t do it here, but if you’re interested, you can try it out for yourself.
2.onDraw
public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {}
Copy the code
Where, C is the canvas of the area supported by getItemOffsets, which can be arbitrarily drawn within the area. If it is outside the area, it will be blocked by Item.
So for example,
@Override
public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.onDraw(c, parent, state);
// The canvas corresponding to the blank area of the getItemOffsets can be arbitrarily drawn on the area of the getItemOffsets.
c.drawCircle(50.50.30, mPaint);
}
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
//outRect is the distance between the upper, lower, and left sides of the item. The default value is 0
outRect.set(100.0.0.5);
}
Copy the code
The renderings are as follows
As you can see, the onDraw method is executed only once. What if I wanted to draw a circle before each item?
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
int itemCount = parent.getChildCount();
for (int i = 0; i < itemCount; i++) {
View child = parent.getChildAt(i);
int cx = child.getWidth() / 2;
int cy = child.getTop() + child.getHeight() / 2;
c.drawCircle(cx, cy, 30, mPaint); }}}Copy the code
The renderings are as follows
Combining these two methods, can we make a group effect? Look at the renderings first
public DemoDecoration(a) {
mPaint = new Paint();
mPaint.setColor(Color.GREEN);
mPaint.setTextSize(DensityUtils.dp2px(DecoActivity.this.16));
}
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
//outRect is the distance between the upper, lower, and left sides of the item. The default value is 0
int index = parent.getChildAdapterPosition(view);
if (index % 3= =0) {
outRect.set(0.100.0.5);
} else {
outRect.set(0.0.0.5); }}@Override
public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.onDraw(c, parent, state);
// The canvas corresponding to the blank area of the getItemOffsets can be arbitrarily drawn on the area of the getItemOffsets.
int itemCount = parent.getChildCount();
for (int i = 0; i < itemCount; i++) {
View child = parent.getChildAt(i);
int cx = child.getWidth() / 2 - 50;
int cy = child.getTop() - 50 + DensityUtils.dp2px(DecoActivity.this.16) / 2;
int index = parent.getChildAdapterPosition(child);
if (index % 3= =0) {
String text = "The first" + index / 3 + "Group";
c.drawText(text, 0, text.length(), cx, cy, mPaint); }}}Copy the code
Of course, the group of the title position calculation is not prepared, here is just to show the effect. For ideal effect, you can refer to the “Android View Effect” group list to achieve the top suction effect
3.onDrawOver
public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {}
Copy the code
OnDrawOver, like onDraw, is only done once, and it’s drawn last, so it can be drawn on the item.
@Override
public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.onDrawOver(c, parent, state);
int itemCount = parent.getChildCount();
for (int i = 0; i < itemCount; i++) {
View child = parent.getChildAt(i);
int cx = child.getWidth() / 2;
int cy = child.getTop() + child.getHeight()/2 ;
c.drawCircle(cx, cy, 30, mPaint); }}Copy the code
The renderings are as follows
What does this approach do? Look at the renderings first
You can see that there is a fade effect at the top of the list. The complete code is as follows
public class TransDecoration extends RecyclerView.ItemDecoration {
private Paint mPaint;
private Xfermode xfermode;
private LinearGradient linearGradient;
private int layerId;
public TransDecoration(a) {
mPaint = new Paint();
xfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
linearGradient = new LinearGradient(0.0 f.0.0 f.0.0 f.100.0 f.new int[] {0, Color.BLACK}, null, Shader.TileMode.CLAMP);
}
@Override
public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.onDraw(c, parent, state);
// The Paint parameter is passed null, and the first black screen will blink when mPaint is passed
// Note that saveLayer cannot be saved or moved into the onDrawOver method
layerId = c.saveLayer(0.0 f.0.0 f, (float) parent.getWidth(), (float) parent.getHeight(), null, Canvas.ALL_SAVE_FLAG);
}
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
}
@Override
public void onDrawOver(@NonNull Canvas canvas, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.onDrawOver(canvas, parent, state);
mPaint.setXfermode(xfermode);
mPaint.setShader(linearGradient);
canvas.drawRect(0.0 f.0.0 f, parent.getRight(), 200.0 f, mPaint);
mPaint.setXfermode(null); canvas.restoreToCount(layerId); }}Copy the code
PorterDuffXfermode is the blending Mode of the image. For details, please refer to the separate breaking to figure out porterDuff. Mode LinearGradient is the gradual effect. For details, please refer to the drawing part of the Custom controls trilogy (19) – LinearGradient and flashing text effects