2

I need to add a separator only to the absolute first item of my recycle view.

I have already read How to selectively decorate RecyclerView items , and i understand that

The (onDraw) method loops over all the child views currently in the RecyclerView visible on the screen.

and my problem is exactly that. since it executes every time the views in the RecycleView change, even if i am able to locate and decorate only the first item, as soon as i scroll down, the decoration shifts to the current first item.

In that link the selection is done by the method isDecorated which looks at the instance of the current child's ViewHolder. My guess is that the guy wanted to decorate with respect to the ViewHolder type, which is not my problem since i have only one type of element in my RecyclerView

This is my DividerItemDecoration.java

public class DividerItemDecoration extends RecyclerView.ItemDecoration {
    private Drawable mDivider;

    public DividerItemDecoration(Drawable divider) {
        mDivider = divider;
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);

    RecyclerView.ViewHolder holder = parent.getChildViewHolder(view);
        if (parent.getChildAdapterPosition(view) == 1) {
            outRect.top = outRect.top + mDivider.getIntrinsicHeight();
        }
        return;

    }

    @Override
    public void onDraw(Canvas canvas, RecyclerView parent, RecyclerView.State state) {
        int dividerLeft = parent.getPaddingLeft();
        int dividerRight = parent.getWidth() - parent.getPaddingRight();

        int childCount = parent.getChildCount();
        for (int i = 0; i < childCount - 1; i++) {
            if(i==0 ){
                View child = parent.getChildAt(i);

                RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();

                int dividerTop = child.getBottom() + params.bottomMargin;
                int dividerBottom = dividerTop + mDivider.getIntrinsicHeight();

                mDivider.setBounds(dividerLeft, dividerTop, dividerRight, dividerBottom);
                mDivider.draw(canvas);
            }
        }
    }
}

Please consider that i have searched all day and could only find examples and gists to add decorator to every item, to selectively add it based on the type, but nothing really tackling with the absolute position. Also, please no libraries like https://github.com/yqritc/RecyclerView-FlexibleDivider, i want to learn , not to copy.

Community
  • 1
  • 1
MaX
  • 489
  • 7
  • 18
  • you have `parent.getChildAdapterPosition()` method, so draw your "divider" inside `onDraw` only if that method returns 1 – pskink Apr 14 '17 at 10:34
  • Thanks this actually works! Funny enough, I used that method in `getItemOffset` and didn't think about reusing it in `onDraw`. Unfortunately i get some strange artifacts when i switch activity (by clicking on the row item) and go back. The divider moves slightly. How come? – MaX Apr 14 '17 at 10:49

1 Answers1

1

You can check if it's the first element, Here's an example:

Just implement getItemViewType(), and take care of the viewType parameter in onCreateViewHolder().

So you do something like:

@Override
public int getItemViewType(int position) {
    if (position == 0)
        return 1;
    else
        return 2;
}

then in onCreateViewHolder inflate your different layout according to your viewType.

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    if (viewType == 1) {
        // inflate your first item layout & return that viewHolder
    } else {
        // inflate your other item layout & return that viewHolder
    }
}
Angelo Parente
  • 796
  • 2
  • 6
  • 15
  • Is giving a different layout to the first item through the adapter any better than applying a decorator to only the first item in terms of performance? – MaX Apr 14 '17 at 11:49
  • For what I learned, this way is the most efficient way. Is just a if/else statement, and instead to use the same layout you will use a different layout for the first element. And this way is also the one with less code – Angelo Parente Apr 14 '17 at 11:53
  • Ok i like this answer and i think i'm going to implement my app in this way. Thank you very much! Any hint to change the (pretty awful) animation deriving from swiping to dismiss the first item , which results in the second item disappearing (leaving a big hole), then reappearing in the first position with the new layout applied while all the others scroll up ? – MaX Apr 14 '17 at 12:47
  • You can follow this tutorial for the swipe to delete function, works very well: https://medium.com/@ipaulpro/drag-and-swipe-with-recyclerview-b9456d2b1aaf Also this: http://stackoverflow.com/questions/40240307/correct-way-to-implement-item-touch-helper-on-recyclerview-in-android Remember to call notifyDataSetChanged() every time you make a change in the list/adapter. And please, if you like the answer accept it :) – Angelo Parente Apr 14 '17 at 13:15
  • Ah yes sorry I got distracted and almost forgot . I'll look into those links , my swipe do dismiss already works well , the problem is that when the first item get swiped the second disappears and reappears as first (with the new layout) . The transition is not very smooth , probably it has something to do with animations in RecyclerView . Anyways , thank you very much again – MaX Apr 14 '17 at 14:20
  • This does not answer the question asked, it's a hacky workaround – r3flss ExlUtr Feb 01 '18 at 16:27