alibaba / vlayout

Project vlayout is a powerfull LayoutManager extension for RecyclerView, it provides a group of layouts for RecyclerView. Make it able to handle a complicate situation when grid, list and other layouts in the same recyclerview.
http://tangram.pingguohe.net/
MIT License
10.79k stars 1.79k forks source link

NullPointerException #186

Open iielse opened 7 years ago

iielse commented 7 years ago
AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.andy.kot, PID: 22098
java.lang.NullPointerException
  at com.alibaba.android.vlayout.DelegateAdapter.findAdapterByIndex(DelegateAdapter.java:459)
  at com.alibaba.android.vlayout.DelegateAdapter.onCreateViewHolder(DelegateAdapter.java:122)
  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 com.alibaba.android.vlayout.ExposeLinearLayoutManagerEx$LayoutState.next(ExposeLinearLayoutManagerEx.java:1612)
  at com.alibaba.android.vlayout.VirtualLayoutManager$LayoutStateWrapper.next(VirtualLayoutManager.java:996)
  at com.alibaba.android.vlayout.layout.SingleLayoutHelper.layoutViews(SingleLayoutHelper.java:78)
  at com.alibaba.android.vlayout.layout.BaseLayoutHelper.doLayout(BaseLayoutHelper.java:313)
  at com.alibaba.android.vlayout.VirtualLayoutManager.layoutChunk(VirtualLayoutManager.java:592)
  at com.alibaba.android.vlayout.ExposeLinearLayoutManagerEx.fill(ExposeLinearLayoutManagerEx.java:1145)
  at com.alibaba.android.vlayout.ExposeLinearLayoutManagerEx.onLayoutChildren(ExposeLinearLayoutManagerEx.java:363)
  at com.alibaba.android.vlayout.VirtualLayoutManager.onLayoutChildren(VirtualLayoutManager.java:416)
  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:14873)
  at android.view.ViewGroup.layout(ViewGroup.java:4651)
  at in.srain.cube.views.ptr.PtrFrameLayout.layoutChildren(PtrFrameLayout.java:260)
  at in.srain.cube.views.ptr.PtrFrameLayout.onLayout(PtrFrameLayout.java:229)
  at android.view.View.layout(View.java:14873)
  at android.view.ViewGroup.layout(ViewGroup.java:4651)
  at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
  at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
  at android.view.View.layout(View.java:14873)
  at android.view.ViewGroup.layout(ViewGroup.java:4651)
  at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1697)
  at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1551)
  at android.widget.LinearLayout.onLayout(LinearLayout.java:1460)
  at android.view.View.layout(View.java:14873)
  at android.view.ViewGroup.layout(ViewGroup.java:4651)
  at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
  at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
  at android.view.View.layout(View.java:14873)
  at android.view.ViewGroup.layout(ViewGroup.java:4651)
  at android.support.v4.view.ViewPager.onLayout(ViewPager.java:1795)
  at android.view.View.layout(View.java:14873)
  at android.view.ViewGroup.layout(ViewGroup.java:4651)
  at android.widget.RelativeLayout.onLayout(RelativeLayout.java:1055)
  at android.view.View.layout(View.java:14873)
  at android.view.ViewGroup.layout(ViewGroup.java:4651)
  at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1697)
  at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1551)
  at android.widget.LinearLayout.onLayout(LinearLayout.java:1460)
  at android.view.View.layout(View.java:14873)
  at android.view.ViewGroup.layout(ViewGroup.java:4651)
  at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
  at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
  at android.view.View.layout(View.java:14873)
  at android.view.ViewGroup.layout(ViewGroup.java:4651)
  at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1697)
  at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1551)
  at android.widget.LinearLayout.onLayout(LinearLayout.java:1460)
  at android.view.View.layout(View.java:14873)
at android.view.ViewGroup.layout(ViewGroup.jav
457    public Adapter findAdapterByIndex(int index) {
458        Pair<AdapterDataObserver, Adapter> rs = mIndexAry.get(index);
459        return rs.second;
460    }

当一个 DelegateAdapter.Adapter 实例内的元素数量刚好为2时,删除其中任何一个都会引发这个崩溃

Adapter代码

class ConcernAdapter internal constructor() : DelegateAdapter.Adapter<RecyclerView.ViewHolder>() {
    interface OnItemRemovedListener {
        fun onItemRemoved(view: View, oi: OrangeInfo, position: Int)
    }

    val singleLayoutHelper = SingleLayoutHelper()
    val dataList = ArrayList<OrangeInfo>()
    val avatarOptions = RequestOptions().centerCrop().circleCrop().placeholder(R.mipmap.default_avatar_183_test)!!
    var onItemRemovedListener: OnItemRemovedListener? = null

    override fun onCreateLayoutHelper(): LayoutHelper {
        return singleLayoutHelper
    }

    fun set(sourceList: List<OrangeInfo>) {
        dataList.clear()
        dataList.addAll(sourceList)
        notifyDataSetChanged()
    }

    fun add(sourceList: List<OrangeInfo>) {
        dataList.addAll(sourceList)
        notifyDataSetChanged()
    }

    private inner class ViewHolder internal constructor(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val vCancel = itemView.findViewById(R.id.vCancel) as TextView
        val vDisplayName = itemView.findViewById(R.id.vDisplayName) as TextView
        val vAvatar = itemView.findViewById(R.id.vAvatar) as ImageView
        val vPraiseRate = itemView.findViewById(R.id.vPraiseRate) as TextView
        val vLevel = itemView.findViewById(R.id.vLevel) as ImageView

        lateinit var oi: OrangeInfo
        var pos: Int = -1

        init {
            vCancel.setOnClickListener {
                ApiServiceImpl.get().concern(itemView.context as RxAppCompatActivity, oi.userId, false, object : SimpleObserver<EmptyEntity, EmptyEntity>() {
                    override fun onSuccess(o: EmptyEntity) {
                        dataList.remove(oi)
//                        notifyDataSetChanged()
                        notifyItemRemoved(pos)

                        onItemRemovedListener?.onItemRemoved(vCancel, oi, pos)
                    }
                }.sneaker(itemView.context as Activity, "正在取消关注", "已取消关注"))
            }
        }

        fun refresh(orangeInfo: OrangeInfo, position: Int) {
            oi = orangeInfo
            pos = position

            vDisplayName.text = oi.displayName()
            vLevel.setImageResource(oi.lvRes())
            if (oi.icon.isNullOrEmpty()) vAvatar.setImageResource(R.mipmap.default_avatar_183_test)
            else Glide.with(vAvatar.context).load(oi.icon).apply(avatarOptions).into(vAvatar)

            vPraiseRate.text = "好评率:${oi.rate()}"
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        return ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.concern_list_content, parent, false))

    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        (holder as ViewHolder).refresh(dataList[position], position)
    }

    override fun getItemCount(): Int {
        return dataList.size
    }
}

崩溃触发代码

dataList.remove(oi)
//  notifyDataSetChanged()  
notifyItemRemoved(pos) // 这2中更新方式都会触发
iielse commented 7 years ago
compile ('com.alibaba.android:vlayout:1.0.10@aar') {
        transitive = true
}

'com.android.support:recyclerview-v7:25.3.1'

recyclerView 设置代码

val virtualLayoutManager = VirtualLayoutManager(context)
val delegateAdapter = DelegateAdapter(virtualLayoutManager)
var vRecycler: RecyclerView
lateinit var concernAdapter: ConcernAdapter
lateinit var loadMoreAdapter: LoadMoreAdapter

...

vRecycler.apply {
    itemAnimator = FadeInAnimator()
    layoutManager = virtualLayoutManager
    adapter = delegateAdapter

    concernAdapter = ConcernAdapter().apply {
        onItemRemovedListener = object : ConcernAdapter.OnItemRemovedListener {
            override fun onItemRemoved(view: View, oi: OrangeInfo, position: Int) {
                notifyTotalCountChanged(--totalCount)
            }
        }
    }
    delegateAdapter.addAdapter(concernAdapter)
    loadMoreAdapter = LoadMoreAdapter()

    delegateAdapter.addAdapter(loadMoreAdapter)
    delegateAdapter.notifyDataSetChanged()
}
iielse commented 7 years ago

https://github.com/iielse/vlayoutTest1

这里有个简单的demo , 点击删除 value b后,再删除 value c value d 就会出现

iielse commented 7 years ago

不过我后来把那个 ConcernAdapter 中的 SingleLayoutHelper 改成 LinearLayoutHelper 似乎就好啦

longerian commented 7 years ago

ok, 我测一下看看。

raulbest commented 6 years ago

不过我后来把那个 ConcernAdapter 中的 SingleLayoutHelper 改成 LinearLayoutHelper 似乎就好啦

你好,请问你这个报错最终是怎么解决的?谢谢

iielse commented 6 years ago

不使用SingleLayoutHelper; adapter只设置一次;

raulbest commented 6 years ago

不使用SingleLayoutHelper; adapter只设置一次;

也就是说你代码里面SingleLayoutHelper全部用LinearLayoutHelper 代替,adapter只设置一次这个说法可以具体一点么?或者贴一下你的代码,谢谢