Android分页与滑动刷新内部CoordinatorLayout得到错误

Posted

技术标签:

【中文标题】Android分页与滑动刷新内部CoordinatorLayout得到错误【英文标题】:Android paging with swipe to refresh inside CoordinatorLayout getting error 【发布时间】:2020-09-25 14:11:39 【问题描述】:

recyclerview 一致性错误第一次刷新在第二次刷新时正常工作得到错误。堆栈中没有行号。

    <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:aapt="http://schemas.android.com/aapt"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/parent_layout"
        android:layout_
        android:layout_
        android:fitsSystemWindows="true"
        android:background="@color/white_color">
    <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
            android:id="@+id/swipe_refresh_layout"
            android:layout_
            android:layout_

            android:layout_marginStart="4dp"
            android:layout_marginEnd="4dp"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">

            <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/rvPost"
                android:layout_
                android:layout_
                android:clipToPadding="false"
                android:orientation="vertical"
                android:paddingTop="19dp"
                app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
                app:layout_behavior="@string/appbar_scrolling_view_behavior"
                app:spanCount="3" />
        </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
    </androidx.coordinatorlayout.widget.CoordinatorLayout>

SwipeRefreshLayout   swipeRefreshLayout = getView().findViewById(R.id.swipe_refresh_layout);
        swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() 
            @Override
            public void onRefresh() 
                viewModel.refresh();
            
        );

适配器代码

class DiscoverAdapter(private val action: (b: Int?) -> Unit)
    : PagedListAdapter<PostModel, RecyclerView.ViewHolder>(NewsDiffCallback) 

    private val DATA_VIEW_TYPE = 1
    private val FOOTER_VIEW_TYPE = 2

    private var state = State.LOADING

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder 
        return if (viewType == DATA_VIEW_TYPE) DiscoverViewHolder.create(parent) else ListFooterViewHolder.create(parent)
    

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) 
        if (getItemViewType(position) == DATA_VIEW_TYPE)
            (holder as DiscoverViewHolder).bind(action, getItem(position))
        else (holder as ListFooterViewHolder).bind(action, state)
    

    override fun getItemViewType(position: Int): Int 
        return if (position < super.getItemCount()) DATA_VIEW_TYPE else FOOTER_VIEW_TYPE
    

    companion object 
        val NewsDiffCallback = object : DiffUtil.ItemCallback<PostModel>() 
            override fun areItemsTheSame(oldItem: PostModel, newItem: PostModel): Boolean 
                return oldItem.id == newItem.id
            

            override fun areContentsTheSame(oldItem: PostModel, newItem: PostModel): Boolean 
                return oldItem == newItem
            
        
    

    override fun getItemCount(): Int 
        return super.getItemCount() + if (hasFooter()) 1 else 0
    

    private fun hasFooter(): Boolean 
        return super.getItemCount() != 0 && (state == State.LOADING || state == State.ERROR)
    

    fun setState(state: State) 
        this.state = state
        notifyItemChanged(super.getItemCount())
    

    fun getData(position: Int): PostModel 
        return getItem(position) as PostModel
    

View Holder 1 是物品和另一个用于加载更多数据的用途

class DiscoverViewHolder(view: View) : RecyclerView.ViewHolder(view) 

    fun bind(retry: (x: Int?) -> Unit, postModel: PostModel?) 
        val width = DeviceScreenUtil.getInstance().width
        val newWidth = width / 3
        val layoutParams = itemView.rl_main_layout.getLayoutParams()
        layoutParams.height = Math.round(newWidth * 1.2f)
        layoutParams.width = Math.round(newWidth.toFloat())
        itemView.rl_main_layout.setPadding(3, 3, 4, 4)
        itemView.rl_main_layout.setLayoutParams(layoutParams)

        if (postModel != null) 
//            itemView.tv_total_like.text = news.title
            if (!postModel.thumbnail.isNullOrEmpty())
                Picasso.get().load(postModel.thumbnail).into(itemView.iv_image)
            itemView.setOnClickListener  retry(adapterPosition) 
            if (postModel.likes > 0) itemView.tv_total_like.setText(postModel.likes.toString() + "")
        
    

    companion object 
        fun create(parent: ViewGroup): DiscoverViewHolder 
            val view = LayoutInflater.from(parent.context)
                    .inflate(R.layout.item_myprofile, parent, false)
            return DiscoverViewHolder(view)
        
    

设置适配器代码

DiscoverAdapter discoverAdapter = DiscoverAdapter 
        if (it == null) 
            viewModel.retry()
         else 
            click(it)
        

    
    rvPost.adapter = discoverAdapter
    viewModel.newsList.observe(this,
            Observer 
                discoverAdapter.submitList(it)
            )

刷新时数据源无效

错误日志

java.lang.IndexOutOfBoundsException:检测到不一致。无效项目位置 20(offset:20).state:21 androidx.recyclerview.widget.RecyclerViewecd9c90 VFED..... .F....ID 0,0-704,1024 #7f08021d app:id/rvPost ,适配器:com.app.ui.main.dashboard.search.discover.adapter.DiscoverAdapter@a9fb989,布局:androidx.recyclerview.widget.GridLayoutManager@97fed8e,上下文:com.app.ui.main.dashboard.MainActivity@226aa34 在 androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:6183) 在 androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6118) 在 androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6114) 在 androidx.recyclerview.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2303) 在 androidx.recyclerview.widget.GridLayoutManager.layoutChunk(GridLayoutManager.java:561) 在 androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1587) 在 androidx.recyclerview.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:665) 在 androidx.recyclerview.widget.GridLayoutManager.onLayoutChildren(GridLayoutManager.java:170) 在 androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep1(RecyclerView.java:4085) 在 androidx.recyclerview.widget.RecyclerView.dispatchLayout(RecyclerView.java:3849) 在 androidx.recyclerview.widget.RecyclerView.onLayout(RecyclerView.java:4404) 在 android.view.View.layout(View.java:16651) 在 android.view.ViewGroup.layout(ViewGroup.java:5440) 在 androidx.swiperefreshlayout.widget.SwipeRefreshLayout.onLayout(SwipeRefreshLayout.java:625) 在 android.view.View.layout(View.java:16651) 在 android.view.ViewGroup.layout(ViewGroup.java:5440) 在 com.google.android.material.appbar.HeaderScrollingViewBehavior.layoutChild(HeaderScrollingViewBehavior.java:148) 在 com.google.android.material.appbar.ViewOffsetBehavior.onLayoutChild(ViewOffsetBehavior.java:43) 在 com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior.onLayoutChild(AppBarLayout.java:1892) 在 androidx.coordinatorlayout.widget.CoordinatorLayout.onLayout(CoordinatorLayout.java:918) 在 android.view.View.layout(View.java:16651) 在 android.view.ViewGroup.layout(ViewGroup.java:5440) 在 android.widget.FrameLayout.layoutChildren(FrameLayout.java:336) 在 android.widget.FrameLayout.onLayout(FrameLayout.java:273) 在 android.view.View.layout(View.java:16651) 在 android.view.ViewGroup.layout(ViewGroup.java:5440) 在 androidx.constraintlayout.widget.ConstraintLayout.onLayout(ConstraintLayout.java:1915) 在 android.view.View.layout(View.java:16651) 在 android.view.ViewGroup.layout(ViewGroup.java:5440) 在 com.google.android.material.appbar.HeaderScrollingViewBehavior.layoutChild(HeaderScrollingViewBehavior.java:148) 在 com.google.android.material.appbar.ViewOffsetBehavior.onLayoutChild(ViewOffsetBehavior.java:43) 在 com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior.onLayoutChild(AppBarLayout.java:1892) 在 androidx.coordinatorlayout.widget.CoordinatorLayout.onLayout(CoordinatorLayout.java:918) 在 android.view.View.layout(View.java:16651) 在 android.view.ViewGroup.layout(ViewGroup.java:5440) 在 android.widget.FrameLayout.layoutChildren(FrameLayout.java:336) 在 android.widget.FrameLayout.onLayout(FrameLayout.java:273) 在 android.view.View.layout(View.java:16651) 在 android.view.ViewGroup.layout(ViewGroup.java:5440) 在 android.widget.LinearLayout.setChildFrame(LinearLayout.java:1743) 在 android.widget.LinearLayout.layoutVertical(LinearLayout.java:1586) 在 android.widget.LinearLayout.onLayout(LinearLayout.java:1495) 在 android.view.View.layout(View.java:16651) 在 android.view.ViewGroup.layout(ViewGroup.java:5440) 在 android.widget.FrameLayout.layoutChildren(FrameLayout.java:336) 在 android.widget.FrameLayout.onLayout(FrameLayout.java:273) 在 android.view.View.layout(View.java:16651) 在 android.view.ViewGroup.layout(ViewGroup.java:5440) 在安卓

【问题讨论】:

如果您提供错误日志,我可能会帮助您。过去两周我一直在研究 Android Paging Library,并且还实现了 Pull To Refresh。为 PagedList 提供 DataSource 和 LiveData 设置。 请检查我重新提交错误日志的问题 它与SwipeRefreshLayoutCoordinatorLayout 或与布局无关,错误是索引超出范围,这意味着无论您在处理List 或@987654328 @,您正在尝试访问列表本身中不存在的位置上的数据。所以,提供代码。 【参考方案1】:

recyclerview 适配器中存在问题我认为您在刷新后再次设置数据时忘记了 adapte.rnotifyDataChanged() :)

请打印您的适配器代码

【讨论】:

我正在使用分页适配器。我认为在 pagingadapter 中不需要通知。 livedata观察者将数据设置为recyclerview auto; 请打印您的 submitList 方法 -livedata 将观察活动或片段中上下文中的数据何时更改-livedata 将向您的适配器提交列表-但适配器中的列表是不同的对象列表,适配器需要知道该列表已更改,因此在适配器中使用通知数据更改方法必须调用您完全了解实时数据但是适配器呢?你明白了吗

以上是关于Android分页与滑动刷新内部CoordinatorLayout得到错误的主要内容,如果未能解决你的问题,请参考以下文章

Firestore分页与FirestoreRecyclerAdapter(Android)[重复]

Android中使用ListView实现分页刷新(线程休眠模拟)

python/Djangof分页与自定义分页

ES实战ES分页与去重

ES实战ES分页与去重

Java 分页与原理(上)