How to get an offset in RecyclerView ItemDecorator

后端 未结 1 1621
Happy的楠姐
Happy的楠姐 2021-02-19 21:47

I have written two ItemDecorator\'s for RecyclerView. Each adds some top offset in getItemOffsets(). Let\'s say:

  • First decor
1条回答
  •  别那么骄傲
    2021-02-19 22:33

    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.

    0 讨论(0)
提交回复
热议问题