Blankj/AndroidUtilCode · error · RuntimeException

onCreateViewHolder: get holder from view type failed.

Error message

onCreateViewHolder: get holder from view type failed.

What it means

BaseItem.onCreateViewHolder resolves a RecyclerView item by viewType: it first looks up a layout id in LAYOUT_SPARSE_ARRAY, then a pre-built View in VIEW_SPARSE_ARRAY. If neither contains the viewType, it throws RuntimeException. The contract is that every BaseItem subclass must register its layout (setLayout) or view (setView) keyed by its viewType before the adapter requests it.

Source

Thrown at lib/utildebug/src/main/java/com/blankj/utildebug/base/rv/BaseItem.java:35

 *     time  : 2019/03/16
 *     desc  :
 * </pre>
 */
public abstract class BaseItem<T extends BaseItem> {

    private static final SparseIntArray    LAYOUT_SPARSE_ARRAY = new SparseIntArray();
    private static final SparseArray<View> VIEW_SPARSE_ARRAY   = new SparseArray<>();

    static ItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        int layoutByType = LAYOUT_SPARSE_ARRAY.get(viewType, -1);
        if (layoutByType != -1) {
            return new ItemViewHolder(LayoutInflater.from(parent.getContext()).inflate(layoutByType, parent, false));
        }
        View viewByType = VIEW_SPARSE_ARRAY.get(viewType);
        if (viewByType != null) {
            return new ItemViewHolder(viewByType);
        }
        throw new RuntimeException("onCreateViewHolder: get holder from view type failed.");
    }

    public abstract void bind(@NonNull final ItemViewHolder holder, final int position);

    public void onViewRecycled(@NonNull final ItemViewHolder holder, final int position) {/**/}

    public long getItemId() {
        return RecyclerView.NO_ID;
    }

    private int viewType;
    BaseItemAdapter<T> mAdapter;

    public BaseItem(@LayoutRes int layoutId) {
        viewType = getViewTypeByLayoutId(layoutId);
        LAYOUT_SPARSE_ARRAY.put(viewType, layoutId);
    }

View on GitHub (pinned to 7b4caf9e54)

Solutions

  1. Register the layout for the item's viewType via the BaseItem.setLayout(type, layoutId)/setView(type, view) mechanism before adding the item to the adapter.
  2. Ensure getViewType() returns a stable value that matches the registered key for every item instance.
  3. Add an assertion in each BaseItem constructor verifying its layout is registered for its viewType.
  4. Write a test that creates one of every item type and asserts onCreateViewHolder succeeds.

Example fix

// before
class MyItem extends BaseItem<MyItem.VH> {
  @Override public int getViewType() { return 42; } // never registered
}
adapter.setItems(Arrays.asList(new MyItem()));
// onCreateViewHolder throws

// after
static { BaseItem.setLayout(42, R.layout.item_my); }
class MyItem extends BaseItem<MyItem.VH> {
  @Override public int getViewType() { return 42; }
}
Defensive patterns

Strategy: validation

Validate before calling

// Verify a layout/view is registered for every viewType before binding the adapter.
int type = item.getViewType();
if (BaseItem.getLayoutByType(type) == -1 && BaseItem.getViewByType(type) == null) {
  throw new IllegalStateException("No layout/view registered for viewType " + type);
}

Try / catch

try {
  RecyclerView.ViewHolder vh = BaseItem.onCreateViewHolder(parent, viewType);
} catch (RuntimeException e) {
  if (e.getMessage().contains("view type")) {
    BaseItem.setLayout(viewType, R.layout.fallback);
    // retry once
  } else throw e;
}

Prevention

When it happens

Trigger: A BaseItem subclass returns a viewType from getViewType() (or uses a default) without having registered that type via LAYOUT_SPARSE_ARRAY.put(type, layoutId) or VIEW_SPARSE_ARRAY.put(type, view). The RecyclerView adapter then calls onCreateViewHolder for that unregistered type and the lookup misses.

Common situations: Adding a new BaseItem subtype but forgetting to bind its layout to its viewType; changing a viewType constant so the registered key no longer matches; using a dynamic viewType computed at runtime that was never pre-registered; recycling/reuse where view types collide.

Related errors


AI-assisted analysis of Blankj/AndroidUtilCode@7b4caf9e54 (2026-08-14). Data as JSON: /api/errors/c2df505708a5dec3. Report an issue: GitHub.