Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

android recyclerview inconsistency detected invalid recycler viewholder position

I have recycler view in my app. The UI is similar to Google Play Store app. It has two view pagers and the list and grid of items in alternate manner. All data is fetched from the web services and is bifurcated in two API calls. The data for list and grid is populated from another API. The problem is I am getting this crash when I scroll the recyclerview very fast. On scrolling the recyclerview, API call for loading the data in list/grid is from bindData(). Read numerous questions on this topic but couldn't reached upto solution.

Fatal Exception: java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder{396df09 position=3 id=-1, oldPos=0, pLpos:0 scrap [attachedScrap] tmpDetached no parent}
       at android.support.v7.widget.RecyclerView$Recycler.validateViewHolderForOffsetPosition(RecyclerView.java:4505)
       at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4636)
       at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4617)
       at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:1994)
       at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1390)
       at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1353)
       at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:574)
       at android.support.v7.widget.RecyclerView.dispatchLayoutStep1(RecyclerView.java:2979)
       at android.support.v7.widget.RecyclerView.onMeasure(RecyclerView.java:2619)
       at android.view.View.measure(View.java:18811)
       at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5952)
       at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
       at android.support.v7.widget.CardView.onMeasure(CardView.java:208)
       at android.view.View.measure(View.java:18811)
       at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5952)
       at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1465)
       at android.widget.LinearLayout.measureVertical(LinearLayout.java:748)
       at android.widget.LinearLayout.onMeasure(LinearLayout.java:630)
       at android.view.View.measure(View.java:18811)
       at android.support.v7.widget.RecyclerView$LayoutManager.measureChildWithMargins(RecyclerView.java:7487)
       at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1416)
       at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1353)
       at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:574)
       at android.support.v7.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:3028)
       at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:2906)
       at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:3283)
       at android.view.View.layout(View.java:16653)
       at android.view.ViewGroup.layout(ViewGroup.java:5438)
       at android.widget.FrameLayout.layoutChildren(FrameLayout.java:336)
       at android.widget.FrameLayout.onLayout(FrameLayout.java:273)
       at android.view.View.layout(View.java:16653)
       at android.view.ViewGroup.layout(ViewGroup.java:5438)
       at android.widget.FrameLayout.layoutChildren(FrameLayout.java:336)
       at android.widget.FrameLayout.onLayout(FrameLayout.java:273)
       at android.view.View.layout(View.java:16653)
       at android.view.ViewGroup.layout(ViewGroup.java:5438)
       at android.support.design.widget.HeaderScrollingViewBehavior.layoutChild(HeaderScrollingViewBehavior.java:122)
       at android.support.design.widget.ViewOffsetBehavior.onLayoutChild(ViewOffsetBehavior.java:42)
       at android.support.design.widget.AppBarLayout$ScrollingViewBehavior.onLayoutChild(AppBarLayout.java:1192)
       at android.support.design.widget.CoordinatorLayout.onLayout(CoordinatorLayout.java:814)
       at android.view.View.layout(View.java:16653)
       at android.view.ViewGroup.layout(ViewGroup.java:5438)
       at android.support.v4.widget.DrawerLayout.onLayout(DrawerLayout.java:1187)
       at android.view.View.layout(View.java:16653)
       at android.view.ViewGroup.layout(ViewGroup.java:5438)
       at android.widget.FrameLayout.layoutChildren(FrameLayout.java:336)
       at android.widget.FrameLayout.onLayout(FrameLayout.java:273)
       at android.view.View.layout(View.java:16653)
       at android.view.ViewGroup.layout(ViewGroup.java:5438)
       at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1743)
       at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1586)
       at android.widget.LinearLayout.onLayout(LinearLayout.java:1495)
       at android.view.View.layout(View.java:16653)
       at android.view.ViewGroup.layout(ViewGroup.java:5438)
       at android.widget.FrameLayout.layoutChildren(FrameLayout.java:336)
       at android.widget.FrameLayout.onLayout(FrameLayout.java:273)
       at android.view.View.layout(View.java:16653)
       at android.view.ViewGroup.layout(ViewGroup.java:5438)
       at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1743)
       at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1586)
       at android.widget.LinearLayout.onLayout(LinearLayout.java:1495)
       at android.view.View.layout(View.java:16653)
       at android.view.ViewGroup.layout(ViewGroup.java:5438)
       at android.widget.FrameLayout.layoutChildren(FrameLayout.java:336)
       at android.widget.FrameLayout.onLayout(FrameLayout.java:273)
       at com.android.internal.policy.PhoneWindow$DecorView.onLayout(PhoneWindow.java:2680)
       at android.view.View.layout(View.java:16653)
       at android.view.ViewGroup.layout(ViewGroup.java:5438)
       at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2198)
       at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1958)
       at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1134)
       at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6045)
       at android.view.Choreographer$CallbackRecord.run(Choreographer.java:860)
       at android.view.Choreographer.doCallbacks(Choreographer.java:672)
       at android.view.Choreographer.doFrame(Choreographer.java:608)
       at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:846)
       at android.os.Handler.handleCallback(Handler.java:739)
       at android.os.Handler.dispatchMessage(Handler.java:95)
       at android.os.Looper.loop(Looper.java:148)
       at android.app.ActivityThread.main(ActivityThread.java:5441)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:738)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:628)

This is my main recyclerview adapter:

public class HomeScreenDataAdapterv2 extends RecyclerView.Adapter<HomeScreenViewHolder> {
    private HomeScreenActionHandler homeScreenActionHandler;
    private ArrayList<HomeScreenParentDataModel> homeScreenParentDataModels;

    public HomeScreenDataAdapterv2(ArrayList<HomeScreenParentDataModel> homeScreenParentDataModels) {
        this.homeScreenParentDataModels = homeScreenParentDataModels;
    }

    @Override
    public HomeScreenViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View holderView;
        Context ctx = parent.getContext();
        LayoutInflater inflater = LayoutInflater.from(ctx);
        switch (viewType) {
            case HomeScreenDataViewTypes.HOME_SCREEN_MAIN_BANNER_VIEW_TYPE:
                holderView = inflater.inflate(R.layout.home_main_banner_adapter, parent, false);
                return new HomeScreenMainBannerViewHolder(ctx, holderView, homeScreenActionHandler);

            case HomeScreenDataViewTypes.HOME_SCREEN_HOT_DEALS_VIEW_TYPE:
                holderView = inflater.inflate(R.layout.home_hot_deals_adapter, parent, false);
                return new HotDealsViewHolder(ctx, holderView, homeScreenActionHandler);

            case HomeScreenDataViewTypes.HOME_SCREEN_DYNAMIC_BLOCKS_LIST_VIEW_TYPE:
                holderView = inflater.inflate(R.layout.home_screen_dynamic_blocks_parent_list_adapter, parent, false);
                return new HomeScreenDynamicBlocksListViewHolder(ctx, holderView, homeScreenActionHandler);

            case HomeScreenDataViewTypes.HOME_SCREEN_DYNAMIC_BLOCK_GRID_VIEW_TYPE:
                holderView = inflater.inflate(R.layout.home_screen_dynamic_blocks_parent_grid_adapter, parent, false);
                return new HomeScreenDynamicBlocksGridViewHolder(ctx, holderView, homeScreenActionHandler);
            default:
                return null;
        }
    }

    @Override
    public void onBindViewHolder(HomeScreenViewHolder holder, int position) {
        HomeScreenParentDataModel homeScreenParentDataModel = homeScreenParentDataModels.get(position);
        switch (homeScreenParentDataModels.get(position).getHome_screen_view_type()) {
            case HomeScreenDataViewTypes.HOME_SCREEN_MAIN_BANNER_VIEW_TYPE:
                ((HomeScreenMainBannerViewHolder) holder).bindData((HomeBannerDataModel) homeScreenParentDataModel);
                break;

            case HomeScreenDataViewTypes.HOME_SCREEN_DYNAMIC_BLOCKS_LIST_VIEW_TYPE:
                ((HomeScreenDynamicBlocksListViewHolder) holder).bindListData((HomeScreenDynamicBlocksDataModel) homeScreenParentDataModel);
                break;

            case HomeScreenDataViewTypes.HOME_SCREEN_DYNAMIC_BLOCK_GRID_VIEW_TYPE:
                ((HomeScreenDynamicBlocksGridViewHolder) holder).bindGridData((HomeScreenDynamicBlocksDataModel) homeScreenParentDataModel);
                break;
        }
    }

    @Override
    public int getItemCount() {
        return homeScreenParentDataModels.size();
    }


    @Override
    public int getItemViewType(int position) {
        return homeScreenParentDataModels.get(position).getHome_screen_view_type();
    }

    private static class HomeScreenMainBannerViewHolder extends HomeScreenViewHolder {
        private Context ctx;
        private HomeScreenActionHandler homeScreenActionHandler;
        private ViewPager main_banner_view_pager;
        private CirclePageIndicator pager_indicator;

        HomeScreenMainBannerViewHolder(Context ctx, View itemView, HomeScreenActionHandler homeScreenActionHandler) {
            super(itemView);
            this.ctx = ctx;
            this.homeScreenActionHandler = homeScreenActionHandler;
            main_banner_view_pager = (ViewPager) itemView.findViewById(R.id.main_banner_view_pager);
            main_banner_view_pager.setClipToPadding(false);


    int padding = Util.convertDptoPixel(ctx, 9.0f);
                main_banner_view_pager.setPageMargin(
Util.convertDptoPixel(ctx, 9.0f));
            main_banner_view_pager.setPadding(padding, 0, padding, 0);
            pager_indicator = (CirclePageIndicator) itemView.findViewById(R.id.pager_indicator);
        }

        public void bindData(HomeBannerDataModel homeMainBannerDataModel) {
            HomeMainBannerPagerAdapter mainBannerAdapter = new HomeMainBannerPagerAdapter(ctx, homeMainBannerDataModel.getBannerList());
            mainBannerAdapter.setHomeScreenActionHandler(homeScreenActionHandler);
            main_banner_view_pager.setAdapter(mainBannerAdapter);
            pager_indicator.setViewPager(main_banner_view_pager);
        }
    }

    private static class HomeScreenDynamicBlocksListViewHolder extends HomeScreenViewHolder implements View.OnClickListener {
        private HomeScreenActionHandler homeScreenActionHandler;
        private RubikMediumTextView section_name;
        private RubikMediumButton btn_view_all;
        private RecyclerView dynamic_widgets_container;
        private Widget widget;
        private HomeScreenBlockListAdapter blockListAdapter;

        HomeScreenDynamicBlocksListViewHolder(Context ctx, View itemView, HomeScreenActionHandler homeScreenActionHandler) {
            super(itemView);
            this.homeScreenActionHandler = homeScreenActionHandler;
            section_name = (RubikMediumTextView) itemView.findViewById(R.id.section_name);
            btn_view_all = (RubikMediumButton) itemView.findViewById(R.id.btn_view_all);
            btn_view_all.setOnClickListener(this);

            dynamic_widgets_container = (RecyclerView) itemView.findViewById(R.id.dynamic_widgets_container);
            section_name.setTextColor(Color.parseColor("#546682"));
            LinearLayoutManager linearLayoutManager = new LinearLayoutManager(ctx);
            linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
            linearLayoutManager.setAutoMeasureEnabled(true);
            dynamic_widgets_container.setLayoutManager(linearLayoutManager);
            dynamic_widgets_container.addItemDecoration(new DividerItemDecoration(ctx));
            dynamic_widgets_container.setNestedScrollingEnabled(false);
        }

        void bindListData(HomeScreenDynamicBlocksDataModel dynamicBlocksDataModel) {
            widget = dynamicBlocksDataModel.getWidget();
            if (widget != null) {
                section_name.setText(widget.getName());
                ArrayList<BuyListingsModel> dynamicModels = widget.getDynamicDataModels();
                if (blockListAdapter == null) {
                    blockListAdapter = new HomeScreenBlockListAdapter(dynamicModels, homeScreenActionHandler);
                    dynamic_widgets_container.setAdapter(blockListAdapter);
                }
                if (dynamicModels.isEmpty()) {
                    homeScreenActionHandler.fetchWidgetData(widget.getName(), getAdapterPosition(), dynamicBlocksDataModel, blockListAdapter);
                }
            }
        }

        @Override
        public void onClick(View v) {
            if (homeScreenActionHandler != null && widget != null) {
                homeScreenActionHandler.gotoSearchResult(widget.getListingFilters());
            }
        }
    }

    private static class HomeScreenDynamicBlocksGridViewHolder extends HomeScreenViewHolder implements View.OnClickListener {
        private HomeScreenActionHandler homeScreenActionHandler;
        private RubikMediumTextView section_name;
        private RubikMediumButton btn_view_all;
        private RecyclerView dynamic_widgets_container;
        private Widget widget;
        private HomeScreenBlockGridAdapter blockGridAdapter;

        HomeScreenDynamicBlocksGridViewHolder(Context ctx, View itemView, HomeScreenActionHandler homeScreenActionHandler) {
            super(itemView);
            this.homeScreenActionHandler = homeScreenActionHandler;
            section_name = (RubikMediumTextView) itemView.findViewById(R.id.section_name);
            btn_view_all = (RubikMediumButton) itemView.findViewById(R.id.btn_view_all);
            btn_view_all.setOnClickListener(this);

            dynamic_widgets_container = (RecyclerView) itemView.findViewById(R.id.dynamic_widgets_container);
            GridLayoutManager gridLayoutManager = new GridLayoutManager(ctx, 2);
            gridLayoutManager.setAutoMeasureEnabled(true);
            dynamic_widgets_container.addItemDecoration(new GridSpacingItemDecoration(2, 1, true));
            dynamic_widgets_container.setNestedScrollingEnabled(false);
            dynamic_widgets_container.setLayoutManager(gridLayoutManager);
        }

        void bindGridData(HomeScreenDynamicBlocksDataModel dynamicBlocksDataModel) {
            widget = dynamicBlocksDataModel.getWidget();
            if (widget != null) {
                section_name.setText(widget.getName());
                ArrayList<BuyListingsModel> dynamicModels = widget.getDynamicDataModels();
                if (blockGridAdapter == null) {
                    blockGridAdapter = new HomeScreenBlockGridAdapter(dynamicModels, homeScreenActionHandler);
                    dynamic_widgets_container.setAdapter(blockGridAdapter);
                }

                if (dynamicModels.isEmpty()) {
                    homeScreenActionHandler.fetchWidgetData(widget.getName(), getAdapterPosition(), dynamicBlocksDataModel, blockGridAdapter);
                }
            }
        }

        @Override
        public void onClick(View v) {
            if (homeScreenActionHandler != null && widget != null) {
                homeScreenActionHandler.gotoSearchResult(widget.getListingFilters());
            }
        }
    }

    public interface HomeScreenActionHandler {
        void gotoCategoryListing(String categoryName);

        void gotoCategoryPage();

        void handleBannerClick(Banner banner);

        void gotoSearchResult(Bundle listingFiltersBundle);

        void fetchWidgetData(String widgetName, int adapterPosition, HomeScreenDynamicBlocksDataModel dynamicBlocksDataModel, RecyclerView.Adapter listAdapter);

        void performWishlistAction(BuyListingsModel buyListingsModel, int childApdaterPosition, RecyclerView.Adapter listAdapter);

        void performBlockClick(String listing_id, boolean isInWatchlist);
    }

    public void setHomeScreenActionHandler(HomeScreenActionHandler homeScreenActionHandler) {
        this.homeScreenActionHandler = homeScreenActionHandler;
    }
}

This is API call for loading data in list:

public void fetchWidgetData(final String widgetName, final int adapterPosition,
                                final HomeScreenDynamicBlocksDataModel dynamicBlocksDataModel, final RecyclerView.Adapter listAdapter) {
        final Widget widget = dynamicBlocksDataModel.getWidget();

        Bundle b = widget.getListingFilters();
        HashMap<String, String> params = new HashMap<>();
        if (!b.containsKey("recently_sold"))
            params.put("isSearch", "1");
        Set<String> keySet = b.keySet();
        Iterator<String> it = keySet.iterator();
        while (it.hasNext()) {
            String key = it.next();
            params.put(key, b.getString(key));
        }
        Response.Listener<JSONObject> responseListener = new Response.Listener<JSONObject>() {

            @Override
            public void onResponse(JSONObject response) {
                                    try {
                    String code = response.getString("code");
                    if (code.equalsIgnoreCase("success")) {
                        JSONArray dataArray = response.getJSONArray("data");
                        ArrayList<BuyListingsModel> models = HomeScreenDataParser
                                .getDynamicBlocksModelList(dataArray);
                        if (models != null && !models.isEmpty()) {
                            ArrayList<BuyListingsModel> buyListingsModels = widget.getDynamicDataModels();
                            buyListingsModels.clear();
                            if (listAdapter != null) {
                                int size = models.size();
                                int upperbound = listAdapter instanceof HomeScreenBlockListAdapter ? 3 : 4;
                                for (int i = 0; i < size && i < upperbound; i++) {
                                    buyListingsModels.add(models.get(i));
                                }
                                listAdapter.notifyItemRangeInserted(0, buyListingsModels.size());

                            }
                            //homeScreenDataAdapterv2.notifyItemChanged(adapterPosition);
                        }
                    } else if (code.equalsIgnoreCase("failed")) {
                        handleError(response);
                    }
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
        };

        Response.ErrorListener errorListener = new Response.ErrorListener() {

            @Override
            public void onErrorResponse(VolleyError error) {
                error.printStackTrace();
            }
        };
        Api.getHomeScreenWidgetDetails(widgetName, responseListener, errorListener, ctx, params);
like image 311
Nitish Avatar asked May 16 '17 07:05

Nitish


3 Answers

I had the same issue and i searched all over the internet and tried alot of the solutions. Finally my issue was fixed by setting ItemAnimator to null for RecyclerView.

recyclerView.setItemAnimator(null);

This will fix the issue

like image 83
Hammad Akram Avatar answered Nov 08 '22 10:11

Hammad Akram


Since you're actually replacing all data, calling notifyItemRangeInserted() without actually inserting anything into the adapter raises this issue. You have to call the correct notifier just after setting the actual data to the adapter or to be more precise underlying list the list.

notifyItemRangeInserted(pos, count) notifies the adapter to expect count items inserted at pos. Therefore the size of the list should have grown by count, not just be count. Since you replace all data, you should call notifyDataSetChanged() instead or use DiffUtil to calculate the actual diff of your data. But then you don't have to worry about which notification to use anymore.

listAdapter.notifyDataSetChanged();
like image 36
tynn Avatar answered Nov 08 '22 11:11

tynn


In onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) check,

if(position != RecyclerView.NO_POSITION){
   // Do your binding here
}

This worked for me.

like image 1
Kashish Sharma Avatar answered Nov 08 '22 10:11

Kashish Sharma