Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best way to implement header over a RecyclerView using a grid layout?

I'm trying to implement the below image. My first thought was to have everything above the grid layout be the first row of the grid and use SpanSizeLookup to set the span size to the number of columns in the RecyclerView, but this feels like something that will give me a lot of problems.

I've been reading about putting a RecyclerView inside a NestedScrollView, people say it works, but I can't seem to get it to work properly. The scrolling doesn't seem to work right, I can't get the grid to even show up without setting a minHeight, but then it just looks bad.

Is there another option I'm not considering or is one of these the direction I should be going?

Here's what I'm trying to achieve

like image 792
jmickela Avatar asked Nov 07 '15 06:11

jmickela


2 Answers

What kind of problems are you anticipating from SpanSizeLookup? You can implement it with a few lines as follows (I'd recommend using values from integers.xml for flexibility).

GridLayoutManager glm = new GridLayoutManager(getContext(), 3);
glm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
    @Override public int getSpanSize(int position) {
        return (position == 0) ? 3 : 1;
    }
});

If your header layout needs views and fields that your regular layout doesn't have, you'll want to create separate views and tell your adapter about them. Something like this.

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

@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    if (viewType == TYPE_HEADER) {
        MyHeaderView view = (MyHeaderView) LayoutInflater
                .from(parent.getContext())
                .inflate(R.layout.my_header_view, parent, false);
        return new MyHeaderViewHolder(view);
    } else {
        MyRegularView view = (MyRegularView) LayoutInflater
                .from(parent.getContext())
                .inflate(R.layout.my_regular_view, parent, false);
        return new MyRegularViewHolder(view);
    }
}

An example header view could be like this (you'd call bindTo() from MyHeaderViewHolder).

public final class MyHeaderView extends LinearLayout {
    @Bind(R.id.image) ImageView imageView;
    @Bind(R.id.title) TextView titleView;

    public MyHeaderView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        ButterKnife.bind(this);
    }

    public void bindTo(String imageUrl, String title) {
        Glide.with(getContext())
                .load(imageUrl).into(imageView);
        titleView.setText(title);
    }
}
like image 171
Jimeux Avatar answered Oct 13 '22 21:10

Jimeux


  • use StaggeredGridLayoutManager
  • use a different layout for your first item (the whole complex view)
  • get its layout params and setFullSpan

This makes the item as wide as the RecyclerView itself (similar to match_parent).

Set various click listeners in the specific ViewHolder that's responsible for this item. Using this approach would set the whole complex view to behave (scroll) as part of the RecyclerView while still making it available for (input) events.

like image 34
stan0 Avatar answered Oct 13 '22 20:10

stan0