I have written two ItemDecorator
's for RecyclerView
. Each adds some top offset in getItemOffsets()
. Let's say:
Now, when I add both of them to RecyclerView
, each item is correctly offsetted by 50dp, that's good.
But here comes the question:
How do I get this offset in onDraw
/onDrawOver
?
Usually decorators draw their stuff by traversing parent.getChildAt(i)
stuff and getting child.getTop()
for example to draw above child view of RecyclerView
.
But in this case, doing so would mix up the drawing of other decorator, because it would also use child.getTop()
.
So at the moment it seems like both decorators need to know about each other and each other's height.
Am I missing something here? I hope I am.
EDIT: I reported an issue to Android issue tracker and it seems this will be worked on. Star it to keep track of progress: https://code.google.com/p/android/issues/detail?id=195746
tl;dr No you are not missing anything. But you can get the values needed in getItemOffsets
, albeit it seems a little bit dirty to me.
Basically there is only one option of getting the decorated height other than managing decorations yourself: LayoutManager.getDecoratedTop();
onDraw
In onDraw
you get the whole canvas of the recyclerView, c.getClipBounds()
does not hold any information. Albeit the javadoc of adding decorations says that decorations should just draw withing their bounds.
Also, getting parent.getLayoutManager().getDecoratedTop()
will give you the same values in every decoration, since it's already too late here, those values are for layouting purposes.
We are too late, layouting is done and we missed it.
getItemOffsets
Please note that I tested the following with a
LinearLayoutManager
and it might as well not work with the others (Most definitely not with most custom ones). Since I am relying on measuring happening between those calls, which is not documented, the given solution might break with some future version.
I just felt I needed that disclaimer. I found a working solution (watch the mOffset
):
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
mOffset = parent.getLayoutManager().getTopDecorationHeight(view);
outRect.set(0, 10, 0, 0);
}
This works, because the recyclerview, when calculating the total inset of the child, is updating the views package local LayoutParams variable. While we cannot access the layout parameter variable itself, calling getTopDecorationHeight actually uses the (currently) dirty inset, giving us the proper value.
Hence, you can get the offset of the previous decorations before applying your own!
To apply it, just remove the other decorations offset when drawing your decoration:
c.drawRect(child.getLeft() + child.getTranslationX(),
mOffset + layoutManager.getDecoratedTop(child) + child.getTranslationY(),
child.getRight() + child.getTranslationX(),
child.getBottom() + child.getTranslationY(), paint);
This will actually apply the other decorations offset, then draw your own from the correct top.
Some Problems
This is now a basic, working solution for the default usecase. BUT. If you have different offsets depending on the item VIEW_TYPE or the adapter position things get tricky.
You will either duplicate your logic and keep various offsets for various view types or you have to store / retrieve the offset for every view or viewtype.
To do so, you could either add some custom tag with view.setTag(key, object)
or doing something similar with the RecyclerView.State
object that's getting passed around.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With