{"record":{"id":"c2df505708a5dec3","repo":"Blankj/AndroidUtilCode","slug":"oncreateviewholder-get-holder-from-view-type-fail-c2df50","errorCode":null,"errorMessage":"onCreateViewHolder: get holder from view type failed.","messagePattern":"onCreateViewHolder: get holder from view type failed\\.","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"lib/utildebug/src/main/java/com/blankj/utildebug/base/rv/BaseItem.java","lineNumber":35,"sourceCode":" *     time  : 2019/03/16\n *     desc  :\n * </pre>\n */\npublic abstract class BaseItem<T extends BaseItem> {\n\n    private static final SparseIntArray    LAYOUT_SPARSE_ARRAY = new SparseIntArray();\n    private static final SparseArray<View> VIEW_SPARSE_ARRAY   = new SparseArray<>();\n\n    static ItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {\n        int layoutByType = LAYOUT_SPARSE_ARRAY.get(viewType, -1);\n        if (layoutByType != -1) {\n            return new ItemViewHolder(LayoutInflater.from(parent.getContext()).inflate(layoutByType, parent, false));\n        }\n        View viewByType = VIEW_SPARSE_ARRAY.get(viewType);\n        if (viewByType != null) {\n            return new ItemViewHolder(viewByType);\n        }\n        throw new RuntimeException(\"onCreateViewHolder: get holder from view type failed.\");\n    }\n\n    public abstract void bind(@NonNull final ItemViewHolder holder, final int position);\n\n    public void onViewRecycled(@NonNull final ItemViewHolder holder, final int position) {/**/}\n\n    public long getItemId() {\n        return RecyclerView.NO_ID;\n    }\n\n    private int viewType;\n    BaseItemAdapter<T> mAdapter;\n\n    public BaseItem(@LayoutRes int layoutId) {\n        viewType = getViewTypeByLayoutId(layoutId);\n        LAYOUT_SPARSE_ARRAY.put(viewType, layoutId);\n    }\n","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/Blankj/AndroidUtilCode/blob/7b4caf9e5440046b3fefb63b6b6e2ead7ebaf809/lib/utildebug/src/main/java/com/blankj/utildebug/base/rv/BaseItem.java#L17-L53","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nclass MyItem extends BaseItem<MyItem.VH> {\n  @Override public int getViewType() { return 42; } // never registered\n}\nadapter.setItems(Arrays.asList(new MyItem()));\n// onCreateViewHolder throws\n\n// after\nstatic { BaseItem.setLayout(42, R.layout.item_my); }\nclass MyItem extends BaseItem<MyItem.VH> {\n  @Override public int getViewType() { return 42; }\n}","handlingStrategy":"validation","validationCode":"// Verify a layout/view is registered for every viewType before binding the adapter.\nint type = item.getViewType();\nif (BaseItem.getLayoutByType(type) == -1 && BaseItem.getViewByType(type) == null) {\n  throw new IllegalStateException(\"No layout/view registered for viewType \" + type);\n}","typeGuard":null,"tryCatchPattern":"try {\n  RecyclerView.ViewHolder vh = BaseItem.onCreateViewHolder(parent, viewType);\n} catch (RuntimeException e) {\n  if (e.getMessage().contains(\"view type\")) {\n    BaseItem.setLayout(viewType, R.layout.fallback);\n    // retry once\n  } else throw e;\n}","preventionTips":["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."],"tags":["android","recyclerview","adapter","validation","config"],"backgroundTag":null,"analyzedSha":"7b4caf9e5440046b3fefb63b6b6e2ead7ebaf809","analyzedAt":"2026-08-14T02:26:54.956Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}