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
- 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.
- Ensure getViewType() returns a stable value that matches the registered key for every item instance.
- Add an assertion in each BaseItem constructor verifying its layout is registered for its viewType.
- 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
- Register each item's layout/view for its viewType before adding items to the adapter.
- Keep getViewType() stable and matching the registered key.
- Assert in each BaseItem constructor that its layout is registered.
- Add a test exercising onCreateViewHolder for every item type.
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
- Set circle needn't set radius.
- Invalid orientation. It should be either HORIZONTAL or VERTI
- onCreateViewHolder: get holder from view type failed.
- Invalid orientation. It should be either HORIZONTAL or VERTI
- comparator must not be null
AI-assisted analysis of Blankj/AndroidUtilCode@7b4caf9e54 (2026-08-14).
Data as JSON: /api/errors/c2df505708a5dec3.
Report an issue: GitHub.