Just add this decoration to your recyclerview.
recyclerView.addItemDecoration(new HeaderDecoration(this, recyclerView, R.layout.test_header)); This code is highly reusable, since all it does is inflate a layout and draw it on top of a given view. There is no need to create custom adapter or recyclerview implementations.
In the case of GridLayouts one would add the offset on the first [columnCount] items and just draw the header once. You get the idea.
By modifying the code you can also use it on the last item (Footer), some other positions, or every Xth element.
public static class HeaderDecoration extends RecyclerView.ItemDecoration { private View mLayout; private boolean isVerticalScroll = true; public HeaderDecoration(final Context context, RecyclerView parent, @LayoutRes int resId) { // inflate and measure the layout mLayout = LayoutInflater.from(context).inflate(resId, parent, false); mLayout.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)); isVerticalScroll = !parent.getLayoutManager().canScrollHorizontally(); } @Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { super.onDraw(c, parent, state); // layout basically just gets drawn on the reserved space on top of the first view mLayout.layout(parent.getLeft(), 0, parent.getRight(), mLayout.getMeasuredHeight()); for (int i = 0; i < parent.getChildCount(); i++) { View view = parent.getChildAt(i); if (parent.getChildAdapterPosition(view) == 0) { c.save(); if(isVerticalScroll) { final int height = mLayout.getMeasuredHeight(); final int top = view.getTop() - height; c.translate(0, top); } else { final int width = mLayout.getMeasuredWidth(); final int left = view.getLeft() - width; c.translate(left, 0); } mLayout.draw(c); c.restore(); break; } } } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { if (parent.getChildAdapterPosition(view) == 0) { if(isVerticalScroll) { outRect.set(0, mLayout.getMeasuredHeight(), 0, 0); } else outRect.set(mLayout.getMeasuredWidth(), 0, 0, 0); } else { outRect.setEmpty(); } } }