Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

在自定义viewholder中 嵌套 recyclerview 时会出现 findviewbyid(R.id.recyclerview) 为空的情况,而且点击加载更多会报ClassCastException #1240

Closed
MirkoWu opened this issue Jun 22, 2017 · 35 comments

Comments

@MirkoWu
Copy link

MirkoWu commented Jun 22, 2017

在自定义viewholder中 嵌套 recyclerview 时会出现 findviewbyid(R.id.recyclerview) 为空的情况,
public class ViewHolder extends BaseRVHolder {
public ChildCommentAdapter childAdapter;
public RecyclerView childRcyclerView;

public ViewHolder(View itemView) {
    super(itemView);
    childRcyclerView = getView(R.id.childRecyclerView);
    childAdapter = new ChildCommentAdapter(itemView.getContext(), R.layout.item_child_comment, isDetail);
    L.d("ViewHolder", " childRcyclerView " + (childRcyclerView == null) );//上拉列表时 childRcyclerView 会出现空指针 
    childRcyclerView.setAdapter(childAdapter);
}

}
此时会有异常如下:
java.lang.reflect.InvocationTargetException
at java.lang.reflect.Constructor.newInstance(Native Method)
at com.mirkowu.baserecyclerviewadapterlibrary.BaseRVAdapter.createGenericKInstance(BaseRVAdapter.java:555)
at com.mirkowu.baserecyclerviewadapterlibrary.BaseRVAdapter.createBaseViewHolder(BaseRVAdapter.java:533)
at com.mirkowu.baserecyclerviewadapterlibrary.BaseRVAdapter.creatLoadingViewHolder(BaseRVAdapter.java:498)
at com.mirkowu.baserecyclerviewadapterlibrary.BaseRVAdapter.onCreateViewHolder(BaseRVAdapter.java:277)
at com.mirkowu.baserecyclerviewadapterlibrary.BaseRVAdapter.onCreateViewHolder(BaseRVAdapter.java:55)
at android.support.v7.widget.RecyclerView$Adapter.createViewHolder(RecyclerView.java:6367)
at android.support.v7.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:5555)
at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5440)
at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5436)
at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2224)
at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1551)
at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1511)
at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:595)
at android.support.v7.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:3583)
at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:3312)
at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:3844)
at android.view.View.layout(View.java:16754)
at android.view.ViewGroup.layout(ViewGroup.java:5462)
at android.support.v4.widget.SwipeRefreshLayout.onLayout(SwipeRefreshLayout.java:636)
at android.view.View.layout(View.java:16754)
at android.view.ViewGroup.layout(ViewGroup.java:5462)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1764)
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1607)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1516)
at android.view.View.layout(View.java:16754)
at android.view.ViewGroup.layout(ViewGroup.java:5462)
at android.widget.RelativeLayout.onLayout(RelativeLayout.java:1079)
at android.view.View.layout(View.java:16754)
at android.view.ViewGroup.layout(ViewGroup.java:5462)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1764)
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1607)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1516)
at android.view.View.layout(View.java:16754)
at android.view.ViewGroup.layout(ViewGroup.java:5462)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:338)
at android.widget.FrameLayout.onLayout(FrameLayout.java:273)
at android.view.View.layout(View.java:16754)
at android.view.ViewGroup.layout(ViewGroup.java:5462)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1764)
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1607)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1516)
at android.view.View.layout(View.java:16754)
at android.view.ViewGroup.layout(ViewGroup.java:5462)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:338)
at android.widget.FrameLayout.onLayout(FrameLayout.java:273)
at android.view.View.layout(View.java:16754)
at android.view.ViewGroup.layout(ViewGroup.java:5462)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1764)
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1607)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1516)
at android.view.View.layout(View.java:16754)
at android.view.ViewGroup.layout(ViewGroup.java:5462)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:338)
at android.widget.FrameLayout.onLayout(FrameLayout.java:273)
at com.android.internal.policy.PhoneWindow$DecorView.onLayout(PhoneWindow.java:2748)
at android.view.View.layout(View.java:16754)
at android.view.ViewGroup.layout(ViewGroup.java:5462)
at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2226)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1983)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1139)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6238)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:884)
at android.view.Choreographer.doCallbacks(Choreographer.java:696)
at android.view.Choreographer.doFrame(Choreographer.java:631)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:870)
at android.os.Handler.handleCallback(Handler.java:743)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:150)
at android.app.ActivityThread.main(ActivityThread.java:5621)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:794)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:684)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.support.v7.widget.RecyclerView.setAdapter(android.support.v7.widget.RecyclerView$Adapter)' on a null object reference
at com.softgarden.moduo.ui.comment.ViewHolder.(ViewHolder.java:25)

虽然报了异常 但程序还是可以正常运行的,
但问题在点击 底部的 加载更多item时(开启了上拉加载),就会报viewholder 转换异常 如下
java.lang.ClassCastException: com.mirkowu.baserecyclerviewadapterlibrary.BaseRVHolder cannot be cast to com.softgarden.moduo.ui.comment.ViewHolder

@MirkoWu
Copy link
Author

MirkoWu commented Jun 22, 2017

用的是楼主最新的版本测试的,可以试试
public class TestAdapter extends BaseQuickAdapter<String, TestAdapter.ViewHolder> {

public TestAdapter(@LayoutRes int layoutResId, @Nullable List<String> data) {
    super(layoutResId, data);
}

@Override
protected void convert(ViewHolder helper, String item) {
    helper.mChildAdapter.setNewData(DataServer.getStrData());
}

@Override
protected ViewHolder createBaseViewHolder(View view) {

    return new ViewHolder(view);
}

public class ViewHolder extends BaseViewHolder {
    public RecyclerView mRecyclerView;
    public ChildAdapter mChildAdapter;

    public ViewHolder(View view) {
        super(view);
        mRecyclerView = getView(R.id.mRecyclerView);
        mChildAdapter = new ChildAdapter(R.layout.item_image_view);
        mRecyclerView.setAdapter(mChildAdapter);
    }
}

public class ChildAdapter extends BaseQuickAdapter<String, BaseViewHolder> {


    public ChildAdapter(@LayoutRes int layoutResId) {
        super(layoutResId);
    }

    @Override
    protected void convert(BaseViewHolder helper, String item) {

    }
}

}

@FrankKwok
Copy link
Contributor

为什么要复写createBaseViewHolder这个方法?

@MirkoWu
Copy link
Author

MirkoWu commented Jun 23, 2017

@FrankKwok 复写是为了写databinding
@OverRide
protected ViewHolder createBaseViewHolder(ViewGroup parent, int layoutResId) {
ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), layoutResId, parent, false);
View view = binding.getRoot();
view.setTag(R.id.BaseRVAdapter_databinding_support, binding);
return createBaseViewHolder(view);
}

我试过只要复写了 createBaseViewHolder(view) 内容什么都不改,只写一个super,都会crash

@MirkoWu
Copy link
Author

MirkoWu commented Jun 23, 2017

我觉得实际上是有可能出现重写该方法的情况,毕竟每个人的用途不同,而且你自己也标注了 if you want to use subclass of BaseViewHolder in the adapter, you must override the method to create new ViewHolder。出现问题不可怕,问题是怎么解决。

@FrankKwok
Copy link
Contributor

@MirkoWu 复写了 createBaseViewHolder(view) 内容什么都不改,只写一个super,都会crash,这里crash的错误能贴出来吗

@MirkoWu
Copy link
Author

MirkoWu commented Jun 23, 2017

@FrankKwok java.lang.ClassCastException: com.chad.library.adapter.base.BaseViewHolder cannot be cast to com.chad.baserecyclerviewadapterhelper.adapter.TestAdapter$ViewHolder
at com.chad.baserecyclerviewadapterhelper.adapter.TestAdapter.createBaseViewHolder(TestAdapter.java:32)
at com.chad.baserecyclerviewadapterhelper.adapter.TestAdapter.createBaseViewHolder(TestAdapter.java:19)
at com.chad.library.adapter.base.BaseQuickAdapter.getLoadingView(BaseQuickAdapter.java:769)
at com.chad.library.adapter.base.BaseQuickAdapter.onCreateViewHolder(BaseQuickAdapter.java:747)
at com.chad.library.adapter.base.BaseQuickAdapter.onCreateViewHolder(BaseQuickAdapter.java:68)
at android.support.v7.widget.RecyclerView$Adapter.createViewHolder(RecyclerView.java:6321)
at android.support.v7.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:5509)
at android.support.v7.widget.GapWorker.prefetchPositionWithDeadline(GapWorker.java:270)
at android.support.v7.widget.GapWorker.flushTaskWithDeadline(GapWorker.java:324)
at android.support.v7.widget.GapWorker.flushTasksWithDeadline(GapWorker.java:337)
at android.support.v7.widget.GapWorker.prefetch(GapWorker.java:344)
at android.support.v7.widget.GapWorker.run(GapWorker.java:370)
at android.os.Handler.handleCallback(Handler.java:743)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:150)
at android.app.ActivityThread.main(ActivityThread.java:5621)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:794)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:684)

@MirkoWu
Copy link
Author

MirkoWu commented Jun 23, 2017

@FrankKwok 而且发现了
//只要重写第一个这个方法 不管改不改都会carsh 报上面那个错误---- 而后面二个却不会

@OverRide
protected ViewHolder createBaseViewHolder(View view) {
return super.createBaseViewHolder(view);
}

@OverRide
protected ViewHolder createBaseViewHolder(ViewGroup parent, int layoutResId) {
return super.createBaseViewHolder(parent, layoutResId);
}
@OverRide
protected ViewHolder onCreateDefViewHolder(ViewGroup parent, int viewType) {
return super.onCreateDefViewHolder(parent, viewType);
}

但是三个都会报都这个异常
java.lang.reflect.InvocationTargetException
at java.lang.reflect.Constructor.newInstance(Native Method)
at com.chad.library.adapter.base.BaseQuickAdapter.createGenericKInstance(BaseQuickAdapter.java:1024)
at com.chad.library.adapter.base.BaseQuickAdapter.createBaseViewHolder(BaseQuickAdapter.java:1005)
at com.chad.library.adapter.base.BaseQuickAdapter.getLoadingView(BaseQuickAdapter.java:769)
at com.chad.library.adapter.base.BaseQuickAdapter.onCreateViewHolder(BaseQuickAdapter.java:747)
at com.chad.library.adapter.base.BaseQuickAdapter.onCreateViewHolder(BaseQuickAdapter.java:68)
at android.support.v7.widget.RecyclerView$Adapter.createViewHolder(RecyclerView.java:6321)
at android.support.v7.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:5509)
at android.support.v7.widget.GapWorker.prefetchPositionWithDeadline(GapWorker.java:270)
at android.support.v7.widget.GapWorker.flushTaskWithDeadline(GapWorker.java:324)
at android.support.v7.widget.GapWorker.flushTasksWithDeadline(GapWorker.java:337)
at android.support.v7.widget.GapWorker.prefetch(GapWorker.java:344)
at android.support.v7.widget.GapWorker.run(GapWorker.java:370)
at android.os.Handler.handleCallback(Handler.java:743)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:150)
at android.app.ActivityThread.main(ActivityThread.java:5621)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:794)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:684)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.support.v7.widget.RecyclerView.setAdapter(android.support.v7.widget.RecyclerView$Adapter)' on a null object reference
at com.chad.baserecyclerviewadapterhelper.adapter.TestAdapter$ViewHolder.(TestAdapter.java:54)
... 20 more

@FrankKwok
Copy link
Contributor

好的,我写个demo试一下

@FrankKwok
Copy link
Contributor

@MirkoWu 为什么我这里没问题呢

@FrankKwok
Copy link
Contributor

@MirkoWu 修改了一下,都复现了。。。

@MirkoWu
Copy link
Author

MirkoWu commented Jun 23, 2017

我就说。。。,我都折腾一天了,实在解决不了没办法才来问你们

@MirkoWu
Copy link
Author

MirkoWu commented Jun 23, 2017

而且我试过 即使不重写creatBaseViewholder ,我把databinding 的设置放到你写的那样里
@OverRide
protected View getItemView(int layoutResId, ViewGroup parent) {
ViewDataBinding binding = DataBindingUtil.inflate(mLayoutInflater, layoutResId, parent, false);
if (binding == null) {
return super.getItemView(layoutResId, parent);
}
View view = binding.getRoot();
view.setTag(R.id.BaseQuickAdapter_databinding_support, binding);
return view;
}
还是会出现 java.lang.ClassCastException: com.chad.library.adapter.base.BaseViewHolder cannot be cast to com.chad.baserecyclerviewadapterhelper.adapter.TestAdapter$ViewHolder 上面提到的异常

@FrankKwok
Copy link
Contributor

@MirkoWu 这个异常java.lang.reflect.InvocationTargetException我已经知道原因了,是因为,复写方法后LoadingView用的也是ViewHolder,从LoadingView里是找不到R.id.mRecyclerView的,会触发NPE,引起这个异常

@FrankKwok
Copy link
Contributor

@MirkoWu 这些个异常全是因为从LoadingView里是找不到R.id.mRecyclerView引起的

@MirkoWu
Copy link
Author

MirkoWu commented Jun 23, 2017

@FrankKwok 嗯嗯,这里的异常虽说被catch到了,一般情况是不会出现问题的,除了重写才会暴露出来

@1109993488
Copy link
Collaborator

@MirkoWu 你可以把需要做的操作方onCreateDefViewHolder这里,这里只是data集合的Item,不包含header、footer、loadMore

@MirkoWu
Copy link
Author

MirkoWu commented Jun 23, 2017

@FrankKwok 我试过,但是onCreateDefViewHolder 里的参数获取不到layout,因为它是private

@MirkoWu
Copy link
Author

MirkoWu commented Jun 23, 2017

@FrankKwok 要不然就得重写getDefItemViewType(),还是可以的,但感觉这只是零时的解决办法

@1109993488
Copy link
Collaborator

@Override
    protected BaseViewHolder onCreateDefViewHolder(ViewGroup parent, int viewType) {
        BaseViewHolder holder = super.onCreateDefViewHolder(parent, viewType);
        //能从holder中拿到view
        return holder;
    }

@1109993488
Copy link
Collaborator

java.lang.ClassCastException: com.chad.library.adapter.base.BaseViewHolder cannot be cast to com.chad.baserecyclerviewadapterhelper.adapter.TestAdapter$ViewHolder 这个是因为你的TestAdapter泛型还是BaseViewHolder,需要改为TestAdapter.ViewHolder

@FrankKwok
Copy link
Contributor

@1109993488 他的泛型是对的,protected K createBaseViewHolder(View view) { Class temp = getClass(); Class z = null; while (z == null && null != temp) { z = getInstancedGenericKClass(temp); temp = temp.getSuperclass(); } K k = createGenericKInstance(z, view); return null != k ? k : (K) new BaseViewHolder(view); },是因为创建LoadingView的时候出了NPE,所以K是null了,执行了(K) new BaseViewHolder(view)这里出现的转型异常

@MirkoWu
Copy link
Author

MirkoWu commented Jun 23, 2017

@1109993488 这样确实是可以的,但是我要使用databinding
ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), layoutResId, parent, false);
View view = binding.getRoot();
view.setTag(R.id.BaseRVAdapter_databinding_support, binding);
就要获取layoutResId,这里不行
觉得还是用你demo里的,在
protected View getItemView(int layoutResId, ViewGroup parent) {
ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), layoutResId, parent, false);
if (binding == null) {
return super.getItemView(layoutResId, parent);
}
View view = binding.getRoot();
view.setTag(R.id.BaseRVAdapter_databinding_support, binding);
return view;
}
处理是不是更好点

@MirkoWu
Copy link
Author

MirkoWu commented Jun 23, 2017

@FrankKwok 是的,所以目前的逻辑来讲,只要重写了viewholder,并且用了loadingview 就一定会出现转型异常

@FrankKwok
Copy link
Contributor

@MirkoWu 复写getItemView方法设置DataBinding是可行的

@CymChad
Copy link
Owner

CymChad commented Jun 23, 2017

我在想是不是可以在执行代码之前先判断一下itemview的type类型

@MirkoWu
Copy link
Author

MirkoWu commented Jun 23, 2017

@FrankKwok 嗯嗯,我目前就是这么处理的,但是那个类型转换异常的还是存在的

@MirkoWu
Copy link
Author

MirkoWu commented Jun 23, 2017

@CymChad 在哪个方法前判断?

@CymChad
Copy link
Owner

CymChad commented Jun 23, 2017

super(itemView); 之后

@MirkoWu
Copy link
Author

MirkoWu commented Jun 23, 2017

你这个是不是想偏了,这个问题在于转换异常,为什么会有这个异常,是因为你把loadingview的holder当成了自定义那个,不信你看下源码,明明这些类型的holder就是BaseViewholder 你却要转成泛型的。
@OverRide
public VH onCreateViewHolder(ViewGroup parent, int viewType) {
this.mContext = parent.getContext();
this.mLayoutInflater = LayoutInflater.from(mContext);
VH vh = null;
switch (viewType) {
case EMPTY_VIEW:
vh = createBaseViewHolder(mEmptyLayout);
break;
case HEADER_VIEW:
vh = createBaseViewHolder(mHeaderLayout);
break;
case FOOTER_VIEW:
vh = createBaseViewHolder(mFooterLayout);
break;
case LOADING_VIEW:
vh = creatLoadingViewHolder(parent);
break;
default:
vh = onCreateDefViewHolder(parent, viewType);

    }

/**

  • return the loading viewholder
    */
    private VH creatLoadingViewHolder(ViewGroup parent) {
    View view = getItemView(mLoadMoreView.getLayoutId(), parent);
    return createBaseViewHolder(view);
    }

@FrankKwok
Copy link
Contributor

不复写这个方法createBaseViewHolder是没有转换异常的

@FrankKwok
Copy link
Contributor

@MirkoWu 不要再构造方法里面获取子RecyclerView,在convert方法里获取

@MirkoWu
Copy link
Author

MirkoWu commented Jun 23, 2017

@FrankKwok 在holder中获取RecyclerView 是为了缓存adapter ,在convert方法里每一次都要setAdapter

@FrankKwok
Copy link
Contributor

@MirkoWu 这样是不对的,你创建ViewHolder的时候并不知道要绑定哪个Adapter?如果不在convert里调用setAdapter,会导致视图错乱的

@MirkoWu
Copy link
Author

MirkoWu commented Jun 23, 2017

@FrankKwok 一个holder对应一个recycler 和adapter 怎么会错乱呢?我的问题目前已经解决了。

@FrankKwok
Copy link
Contributor

@MirkoWu holder是会被复用的,里面的recycler和adapter复用的时候没有做处理

@CymChad CymChad closed this as completed Jun 26, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants