GridLayoutManager spanSizeLookup bug

k0sh picture k0sh · Aug 7, 2015 · Viewed 7.3k times · Source

I am having an issue with GridLayoutManger setSpanSizeLookup, when an orientation of activity changes, I am changing the span count while checking if the specific position is some sort of type, this works perfectly on orientation change, my only problem is, I'm using zxing library to do barcode scanning, whenever a button is clicked I'm opening the zxing default intent and retrieve date from it, however when zxing is opening it goes to landscape and my current activity orientation is portrait this gives me the IllegalArgumentException that the layout manage throws when only opening the Xzing intent, the crash log is as this
java.lang.IllegalArgumentException: Item at position 0 requires 2 spans but GridLayoutManager has only 1 spans.
this problem doesn't occur if i rotate the phone, only happens when I launch the Xzing intent, i couldn't really figure out how how I solve this issue as its bugging me. here is my spanSizeLookup -

manager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
     @Override
     public int getSpanSize(int position) {
        return adapter.getHolders().get(position).getLabelHolder() != null ? getResources().getInteger(R.integer.span) : 1;
     }
});

the span is base on screen size so it can be 1-2, 2-3 and 3-4. i repeat this doesn't give me any error on orientation change, only the error occur when I open the zxing
P.S if i open zxing intent while my activity is on landspace the crash wont occur.

Edit

it seems like only launching the zxing default intent causing this issue, I'm having an activity where it goes landscape after it launches and the exception didn't occur, as a workaround i did, was that i created an activity that handles the barcode scanning with a delay of 1 second to launch the intent as if i didn't do that, it will throw the same exception.

Edit 2

I just found that, even without setSpanSpizeLookup the crash still occurs. by just calling manager.setSpanCount(getResources().getInteger(R.integer.span))

Answer

Trey Cai picture Trey Cai · Aug 15, 2015

Here's the source code that throws the exception in GridLayoutManager.layoutChunk()

final int spanSize = getSpanSize(recycler, state, pos);
if (spanSize > mSpanCount) {
    throw new IllegalArgumentException("Item at position " + pos + " requires " +
           spanSize + " spans but GridLayoutManager has only " + mSpanCount
           + " spans.");
}

Is your spanSize greater than your spanCount? Try to debug and probably change your spanCount and see how it works.

---- Updated ----

There're couple ways to switch between list and grid.

  1. Only changing the spanCount.

    You have to call LayoutManager.requestLayout() after you change your spanCount. Otherwise your app may crash.

  2. Only changing the SpanSizeLookUp.

    private class CustomSpanSize extends GridLayoutManager.SpanSizeLookup {
    
      private static final int LIST_SPAN_SIZE = 1;
      private static final int GRID_SPAN_SIZE = 2;
    
      private int mSpanSize = LIST_SPAN_SIZE;
    
      public void asList() {
        mSpanSize = LIST_SPAN_SIZE;
      }
    
      public void asGrid() {
        mSpanSize = GRID_SPAN_SIZE;
      }
    
      @Override
      public int getSpanSize(int position) {
        return mSpanSize;
      }
    }
    

    and use it like that

    @Override
    protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
    
      mLayoutManager = new GridLayoutManager(this, 2);
    
      mCustomSpanSize = new CustomSpanSize();
      mLayoutManager.setSpanSizeLookup(mCustomSpanSize);
      RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler);
      recyclerView.setHasFixedSize(true);
      recyclerView.setLayoutManager(mLayoutManager);
    
      ...
    }
    
    public void someFunction() {
      if (changed) {
        mCustomSpanSize.asList();
      } else {
        mCustomSpanSize.asGrid();
      }
      // use this to update the layout
      mAdapter.notifyDataSetChanged();
      // or you can use this to update the layout
      mLayoutManager.requestLayout();
    }
    

    this way won't crash even you forget to call notifyDataSetChanged or requestLayout