30/09/2018, 21:53

Vấn đề với RecyclerView trong android

Hi anh em,
Đang dev trên android sử dùng RecyclerView, Khi adding infinite scroll, sử dụng thỉnh thoảng bị crash với lôi như dưới:

java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 10(offset:10).state:11
 at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4659)
 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)

anh em có ai đã làm và gặp phải chưa? giải quyết thế nào?
ref: https://code.google.com/p/android/issues/detail?id=77846

X viết 23:56 ngày 30/09/2018

Không có code nên không rõ lắm.
Mỗi khi thêm, xóa, sửa liên quan đến thằng adapter thì gọi notifyDataSetChanged() (Đoán mò là do sai index vì chưa được cập nhật lại)

Hoặc có thể thử tạo một custom layout manager rồi set nó cho RecyclerView:
Answer 1 tại đây: http://stackoverflow.com/questions/30220771/recyclerview-inconsistency-detected-invalid-item-position
http://stackoverflow.com/questions/31367599/how-to-update-recyclerview-adapter-data

Nguyen Ca viết 00:03 ngày 01/10/2018

đã thử tất cả các cách, sai index là do RecyclerView tính sai ây, khi thêm phần tử đã gọi rồi notifyDataSetChanged(), mà vân bi,
link mình đưa projecr member của cái Rycycler View có phán:

Project Member #161 ybo...@google.com
hundreds of thousands of apps are using recycler view so problems are normal (see: ListView bugs).

RecyclerView has AsyncListUtil as a replacement for CursorAdapter (even better because avoids any ANR by never querying data on main thread).

Many problems with RecyclerView happens due to expectations coming from ListView. Some of these are things we regret in ListView and do not want to carry over to RecyclerView.

You are free to use ListView and its components if it works better for your use case. (it is not deprecated). There are many features that we want to implement in RecyclerView that ListView had but RecyclerView's main goal is to provide things that were not feasible with ListView so those features get the priority (animations, layout managers, components etc etc).
Nguyen Ca viết 00:03 ngày 01/10/2018

Đã resolve:, thỉnh thoảng bị nhưng rất it, chấp nhận được:
Cho ai bị và cách giải quyết như dưới:

EndlessOnScrollListener.java
public abstract class EndlessOnScrollListener extends RecyclerView.OnScrollListener {

    // The minimum amount of items to have below your current scroll position
    // before loading more.
    private int visibleThreshold = 5;
    // The current offset index of data you have loaded
    private int currentPage = 0;
    // The total number of items in the dataset after the last load
    private int previousTotalItemCount = 0;
    // True if we are still waiting for the last set of data to load.
    private boolean loading = true;
    // Sets the starting page index
    private int startingPageIndex = 0;

    RecyclerView.LayoutManager mLayoutManager;

    public EndlessOnScrollListener(LinearLayoutManager layoutManager) {
        this.mLayoutManager = layoutManager;
    }

    public EndlessOnScrollListener(GridLayoutManager layoutManager) {
        this.mLayoutManager = layoutManager;
        visibleThreshold = visibleThreshold * layoutManager.getSpanCount();
    }

    public EndlessOnScrollListener(StaggeredGridLayoutManager layoutManager) {
        this.mLayoutManager = layoutManager;
        visibleThreshold = visibleThreshold * layoutManager.getSpanCount();
    }

    public int getLastVisibleItem(int[] lastVisibleItemPositions) {
        int maxSize = 0;
        for (int i = 0; i < lastVisibleItemPositions.length; i++) {
            if (i == 0) {
                maxSize = lastVisibleItemPositions[i];
            } else if (lastVisibleItemPositions[i] > maxSize) {
                maxSize = lastVisibleItemPositions[i];
            }
        }
        return maxSize;
    }

    // This happens many times a second during a scroll, so be wary of the code you place here.
    // We are given a few useful parameters to help us work out if we need to load some more data,
    // but first we check if we are waiting for the previous load to finish.
    @Override
    public void onScrolled(RecyclerView view, int dx, int dy) {
        int lastVisibleItemPosition = 0;
        int totalItemCount = mLayoutManager.getItemCount();

        if (mLayoutManager instanceof StaggeredGridLayoutManager) {
            int[] lastVisibleItemPositions = ((StaggeredGridLayoutManager) mLayoutManager).findLastVisibleItemPositions(null);
            // get maximum element within the list
            lastVisibleItemPosition = getLastVisibleItem(lastVisibleItemPositions);
        } else if (mLayoutManager instanceof LinearLayoutManager) {
            lastVisibleItemPosition = ((LinearLayoutManager) mLayoutManager).findLastVisibleItemPosition();
        } else if (mLayoutManager instanceof GridLayoutManager) {
            lastVisibleItemPosition = ((GridLayoutManager) mLayoutManager).findLastVisibleItemPosition();
        }

        // If the total item count is zero and the previous isn't, assume the
        // list is invalidated and should be reset back to initial state
        if (totalItemCount < previousTotalItemCount) {
            this.currentPage = this.startingPageIndex;
            this.previousTotalItemCount = totalItemCount;
            if (totalItemCount == 0) {
                this.loading = true;
            }
        }
        // If it’s still loading, we check to see if the dataset count has
        // changed, if so we conclude it has finished loading and update the current page
        // number and total item count.
        if (loading && (totalItemCount > previousTotalItemCount)) {
            loading = false;
            previousTotalItemCount = totalItemCount;
        }

        // If it isn’t currently loading, we check to see if we have breached
        // the visibleThreshold and need to reload more data.
        // If we do need to reload some more data, we execute onLoadMore to fetch the data.
        // threshold should reflect how many total columns there are too
        if (!loading && (lastVisibleItemPosition + visibleThreshold) > totalItemCount) {
            currentPage++;
            onLoadMore(currentPage, totalItemCount);
            loading = true;
        }
    }

    // Defines the process for actually loading more data based on page
    public abstract void onLoadMore(int page, int totalItemsCount);
}

Gọi hàm setRecyclerView chỗ nào cần load more data như dưới

 private void setRecyclerView(final ArrayList<TopicDto> data, RecyclerView recyclerView) {
        final TopicAdapter adapter = new TopicAdapter(data);

        recyclerView.setHasFixedSize(true);
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity());
        recyclerView.setLayoutManager(linearLayoutManager);
        recyclerView.setAdapter(adapter);

        recyclerView.addOnScrollListener(new EndlessOnScrollListener(linearLayoutManager) {
            @Override
            public void onLoadMore(int page, int totalItemsCount) {
                if (!adapter.isLoaded()) {
                    data.add(null);
                    adapter.notifyItemInserted(data.size() - 1);
                    adapter.notifyDataSetChanged();
                    adapter.setLoaded(true);
                    new LoadMoreTask((String) getArguments().get(ApplConst.TOPIC_TYPE), data, adapter).execute((Void) null);
                }


            }
        });
    }
Bài liên quan
0