Skip to content

Grid Support

Eli Hart edited this page Jun 15, 2017 · 4 revisions

EpoxyController can be used with RecyclerView's GridLayoutManager to allow EpoxyModels to change their span size. EpoxyModels can claim various span sizes by overriding int getSpanSize(int totalSpanCount, int position, int itemCount) to vary their span size based on the span count of the layout manager as well as the model's position in the adapter. EpoxyController#getSpanSizeLookup() returns a span size lookup object that delegates lookup calls to each EpoxyModel.

int spanCount = 2;
GridLayoutManager layoutManager = new GridLayoutManager(getContext(), spanCount);
epoxyController.setSpanCount(spanCount);
layoutManager.setSpanSizeLookup(epoxyController.getSpanSizeLookup());

This also works with EpoxyAdapter.

Dynamic Span Size Control

If you would like more fine grain control over the model's span size you can set a SpanSizeOverrideCallback on the model when you use it in your EpoxyController.

model.spanSizeOverride(new SpanSizeOverrideCallback() {
      @Override
      public int getSpanSize(int totalSpanCount, int position, int itemCount) {
        return totalSpanCount / 2;
      }
    });

You may choose to extend this with a helper class to make it easier to change the span count depending on the screen size. For example you could make something like this:

public class NumItemsInGridRow implements EpoxyModel.SpanSizeOverrideCallback {

    public final int numItemsForCurrentScreen;

    /** Shows one item per row on phone and two for all tablet sizes. */
    public static NumItemsInGridRow oneColumnPhoneTwoColumnTablet(Context context) {
        return new NumItemsInGridRow(context, 1, 2, 2);
    }

    /** Specify how many items to show per grid row on phone. Tablet will show more items per row according to a default ratio. */
    public static NumItemsInGridRow forPhoneWithDefaultScaling(Context context, int numItemsPerRowOnPhone) {
        return new NumItemsInGridRow(context, numItemsPerRowOnPhone, round(numItemsPerRowOnPhone * 1.5f), numItemsPerRowOnPhone * 2);
    }

    public NumItemsInGridRow(Context context, int forPhone, int forTablet, int forWideTablet) {
        numItemsForCurrentScreen = isWideTabletScreen(context) ? forWideTablet
                : (isTabletScreen(context) ? forTablet : forPhone);
    }

    @Override
    public int getSpanSize(int totalSpanCount, int position, int itemCount) {
        if (totalSpanCount % numItemsForCurrentScreen != 0) {
            throw new IllegalStateException(
                    "Total Span Count of : " + totalSpanCount + " can not evenly fit: " + numItemsForCurrentScreen + " cards per row");
        }

        return totalSpanCount / numItemsForCurrentScreen;
    }
}

You could then use that helper class like so:

model.spanSizeOverride(NumItemsInGridRow.forPhoneWithDefaultScaling(1);

This would automatically show one item on phone, two items on portrait tablets, and three on landscape tablets. (The total span count of the layout manager must be set to a multiple of 6)