Tencent/QMUI_Android · error · IllegalStateException

bottomView must implement from…

Error message

bottomView must implement from IQMUIContinuousNestedBottomView

What it means

QMUIContinuousNestedScrollLayout.setBottomAreaView() requires the bottom view to implement IQMUIContinuousNestedBottomView so the layout can coordinate scrolling and paging with it. A view not implementing this interface is rejected with an IllegalStateException before it is added.

Solutions

  1. Make the view class implement IQMUIContinuousNestedBottomView and its methods (injectScrollNotifier, consumeScroll, getScrollHeight, getCurrentScroll, isBottomAreaOverlay...).
  2. Use QMUI's provided implementations: QMUIContinuousNestedBottomScrollLayout, QMUIContinuousNestedBottomRecyclerView or QMUIContinuousNestedBottomViewPager.
  3. Verify you are passing a bottom-area view, not a top-area view, to setBottomAreaView.

Example fix

// before
layout.setBottomAreaView(new RecyclerView(context), null);

// after
QMUIContinuousNestedBottomRecyclerView bottom = new QMUIContinuousNestedBottomRecyclerView(context);
layout.setBottomAreaView(bottom, null);
Defensive patterns

Strategy: type-guard

Validate before calling

if (!(bottomView instanceof IQMUIContinuousNestedBottomView)) {
    throw new IllegalArgumentException("bottomView must implement IQMUIContinuousNestedBottomView");
}
layout.setBottomAreaView(bottomView, null);

Type guard

boolean isBottomAreaViewValid(View v) {
    return v instanceof IQMUIContinuousNestedBottomView;
}

Try / catch

try {
    layout.setBottomAreaView(bottomView, layoutParams);
} catch (IllegalStateException e) {
    Log.e(TAG, "bottomView invalid", e);
    layout.setBottomAreaView(new QMUIContinuousNestedBottomRecyclerView(context), layoutParams);
}

Prevention

When it happens

Trigger: Calling setBottomAreaView(view, layoutParams) with a view (e.g. plain RecyclerView, LinearLayout, custom pager) that does not implement IQMUIContinuousNestedBottomView.

Common situations: Happens when substituting QMUIContinuousNestedBottomScrollLayout/QMUIContinuousNestedBottomViewPager with a custom content area, or when a class implements the TOP interface but not the BOTTOM one (they are distinct interfaces).

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of Tencent/QMUI_Android@026e7d4866 (2026-09-06). Data as JSON: /api/errors/b150dc90166315ae. Report an issue: GitHub.

Appendix: source

Thrown at qmui/src/main/java/com/qmuiteam/qmui/nestedScroll/QMUIContinuousNestedScrollLayout.java:242

    public IQMUIContinuousNestedTopView getTopView() {
        return mTopView;
    }

    public IQMUIContinuousNestedBottomView getBottomView() {
        return mBottomView;
    }

    public QMUIContinuousNestedTopAreaBehavior getTopAreaBehavior() {
        return mTopAreaBehavior;
    }

    public QMUIContinuousNestedBottomAreaBehavior getBottomAreaBehavior() {
        return mBottomAreaBehavior;
    }

    public void setBottomAreaView(View bottomView, @Nullable LayoutParams layoutParams) {
        if (!(bottomView instanceof IQMUIContinuousNestedBottomView)) {
            throw new IllegalStateException("bottomView must implement from IQMUIContinuousNestedBottomView");
        }
        if (mBottomView != null) {
            removeView(((View) mBottomView));
        }
        mBottomView = (IQMUIContinuousNestedBottomView) bottomView;
        mBottomView.injectScrollNotifier(new IQMUIContinuousNestedBottomView.OnScrollNotifier() {
            @Override
            public void notify(int innerOffset, int innerRange) {
                int topCurrent = mTopView == null ? 0 : mTopView.getCurrentScroll();
                int topRange = mTopView == null ? 0 : mTopView.getScrollOffsetRange();
                int offsetCurrent = mTopAreaBehavior == null ? 0 : -mTopAreaBehavior.getTopAndBottomOffset();
                dispatchScroll(topCurrent, topRange, offsetCurrent, getOffsetRange(), innerOffset, innerRange);
            }

            @Override
            public void onScrollStateChange(View view, int newScrollState) {
                dispatchScrollStateChange(newScrollState, false);
            }

View on GitHub (pinned to 026e7d4866)