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

动态替换adapter的时候,导致adapter复用错误,强转异常 #182

Closed
blesslp opened this Issue Aug 17, 2017 · 7 comments

Comments

Projects
None yet
3 participants
@blesslp

blesslp commented Aug 17, 2017

事情是这样, 购物app的首页, 用到了多种多样的布局,其中有一些会根据服务器端给的模版来变化
这样,
因为数据的变化可能会导致我这边的adapter变化,所以我维护了一系列的List来保存一些模板Adapter
每次刷新界面,我就写了
delegateAdapter.removeAdapters(xx)
delegateAdapter.addAdapters(xx),
这样,首次进页面,没问题, 但是在有数据的前提下, 再次刷新后,就会报Adapter强转异常,这个强转是我写了ViewHolder导致的, 但是AAdapter里面传入了BAdapter的ViewHolder.
后来我寻思着,可能是复用出了问题, 然后我给每个Adapter都加了getItemType返回一个递增且唯一的int,就好了.
我是好奇这是我用法的问题,还是VLayout的复用逻辑有一些瑕疵?

@longerian

This comment has been minimized.

Show comment
Hide comment
@longerian

longerian Aug 17, 2017

Contributor

应该是复用相关的问题,参考FAQ:https://github.com/alibaba/vlayout/blob/master/docs/VLayoutFAQ.md 先检查一下代码

Contributor

longerian commented Aug 17, 2017

应该是复用相关的问题,参考FAQ:https://github.com/alibaba/vlayout/blob/master/docs/VLayoutFAQ.md 先检查一下代码

@blesslp

This comment has been minimized.

Show comment
Hide comment
@blesslp

blesslp Aug 18, 2017

嗯, 但是

当hasConsistItemType=false的时候,即使不同DelegateAdapter.Adapter里返回的相同的itemType,对于DelegateAdapter也会将它转换成不同的值,对于RecyclerView来说它们是不同的类型。

这里我的hasConsistItemtype=false了,感觉Adapter的itemType返回了相同类型了,DelegateAdapter转换后的itemType还是同一个 , 我发现是这样的,就是Aadapter在位置3,返回type为0,后来数据变了,3这个位置变成BAdapter了,然后给BAdapter里面的ViewHolder被强转成Aadapter里面的ViewHolder导致了报错, 我把没个Adapter的itemType都写成不一样的, 这个问题就不存在了.

blesslp commented Aug 18, 2017

嗯, 但是

当hasConsistItemType=false的时候,即使不同DelegateAdapter.Adapter里返回的相同的itemType,对于DelegateAdapter也会将它转换成不同的值,对于RecyclerView来说它们是不同的类型。

这里我的hasConsistItemtype=false了,感觉Adapter的itemType返回了相同类型了,DelegateAdapter转换后的itemType还是同一个 , 我发现是这样的,就是Aadapter在位置3,返回type为0,后来数据变了,3这个位置变成BAdapter了,然后给BAdapter里面的ViewHolder被强转成Aadapter里面的ViewHolder导致了报错, 我把没个Adapter的itemType都写成不一样的, 这个问题就不存在了.

@longerian

This comment has been minimized.

Show comment
Hide comment
@longerian

longerian Aug 18, 2017

Contributor

恩,你加日志或者 debug,把 getItemViewType(int position) 方法的结果捞一把,确认一下在 Adapter 的 itemType 一样的情况下,最终的类型是否一样,应该是要不一样。

Contributor

longerian commented Aug 18, 2017

恩,你加日志或者 debug,把 getItemViewType(int position) 方法的结果捞一把,确认一下在 Adapter 的 itemType 一样的情况下,最终的类型是否一样,应该是要不一样。

@blesslp

This comment has been minimized.

Show comment
Hide comment
@blesslp

blesslp Aug 18, 2017

感谢您百忙中抽空解答 : )

Pair<AdapterDataObserver, Adapter> p = findAdapterByPosition(position);
        if (p == null) {
            return RecyclerView.INVALID_TYPE;
        }
        int subItemType = p.second.getItemViewType(position - p.first.mStartPosition);

        if (subItemType < 0) {
            // negative integer, invalid, just return
            return subItemType;
        }
        if (mHasConsistItemType) {
            mItemTypeAry.put(subItemType, p.second);
            return subItemType;
        }
        int index = p.first.mIndex;
        return (int) getCantor(subItemType, index);
  int index = p.first.mIndex;
  return (int) getCantor(subItemType, index);

我觉得这里好像是中了我说的情况了, 在同一个位置,由于数据的变动导致这里由Aadapter变成了BAdapter,但是如果我在adapter里面没有覆盖getItemType,默认返回0的话,

//例如:
index = 3;
subItemType=0;
//这样getCantor算出来的值是一样的,

所以这种情况会导致表层Adapter变了,而RecyclerView误认为是同一个,所以把之前的AAdapter的ViewHolder给了BAdapter,导致强转异常吧,
这种情况需要我们手动给A,B,C,Adapter指定不一样的itemType来保证在同一位置算出的Cantor唯一.
大神您看我说的对吗?

blesslp commented Aug 18, 2017

感谢您百忙中抽空解答 : )

Pair<AdapterDataObserver, Adapter> p = findAdapterByPosition(position);
        if (p == null) {
            return RecyclerView.INVALID_TYPE;
        }
        int subItemType = p.second.getItemViewType(position - p.first.mStartPosition);

        if (subItemType < 0) {
            // negative integer, invalid, just return
            return subItemType;
        }
        if (mHasConsistItemType) {
            mItemTypeAry.put(subItemType, p.second);
            return subItemType;
        }
        int index = p.first.mIndex;
        return (int) getCantor(subItemType, index);
  int index = p.first.mIndex;
  return (int) getCantor(subItemType, index);

我觉得这里好像是中了我说的情况了, 在同一个位置,由于数据的变动导致这里由Aadapter变成了BAdapter,但是如果我在adapter里面没有覆盖getItemType,默认返回0的话,

//例如:
index = 3;
subItemType=0;
//这样getCantor算出来的值是一样的,

所以这种情况会导致表层Adapter变了,而RecyclerView误认为是同一个,所以把之前的AAdapter的ViewHolder给了BAdapter,导致强转异常吧,
这种情况需要我们手动给A,B,C,Adapter指定不一样的itemType来保证在同一位置算出的Cantor唯一.
大神您看我说的对吗?

@longerian

This comment has been minimized.

Show comment
Hide comment
@longerian

longerian Aug 19, 2017

Contributor

嗯,是的,这里有个瑕疵,没有考虑到替换adapter的情况,所以getCantor(subItemType, index)算出来恰好是一样的,导致复用成一个了。在没有想出好的解决办法之前,先通过指定不一样的itemType来保证吧。

Contributor

longerian commented Aug 19, 2017

嗯,是的,这里有个瑕疵,没有考虑到替换adapter的情况,所以getCantor(subItemType, index)算出来恰好是一样的,导致复用成一个了。在没有想出好的解决办法之前,先通过指定不一样的itemType来保证吧。

@wengqizun

This comment has been minimized.

Show comment
Hide comment
@wengqizun

wengqizun Feb 27, 2018

这个bug修复了吗?

wengqizun commented Feb 27, 2018

这个bug修复了吗?

@longerian

This comment has been minimized.

Show comment
Hide comment
@longerian

longerian Feb 27, 2018

Contributor

建议使用 tangram 来使用vlayout,直接使用vlayout成本略高。 @wengqizun

Contributor

longerian commented Feb 27, 2018

建议使用 tangram 来使用vlayout,直接使用vlayout成本略高。 @wengqizun

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment