RecyclerView会在刷新

时间:2017-03-18 15:25:56

标签: android asynchronous android-asynctask android-recyclerview

我正在使用此library.

的Recyclerview

当我刷新recyclerview并同时滚动应用程序将强制关闭。

public class NewsFeed extends Fragment implements MyDynamicRecyclerView.SwipeRefreshListener, MyDynamicRecyclerView.LoadMoreListener {

MyDynamicRecyclerView myRecyclerview;
NewsFeedAdapter testAdapter;
ArrayList<ProgramModel> modelArrayList;
LinearLayoutManager linearLayoutManager;
Dialog dialog;
int limit;
private boolean firstSwipe;


public NewsFeed() {
    // Required empty public constructor
}

@Override
public void onDestroy() {
    super.onDestroy();
    Log.i("NewsFeedFragment", "Destroyed");
    if (dialog != null)
        dialog.dismiss();
}

@Override
public void onPause() {
    super.onPause();
    Log.i("NewsFeedFragment", "Paused");
    if (dialog != null)
        dialog.dismiss();
}

@Override
public void onResume() {
    super.onResume();
    Log.i("NewsFeedFragment", "Resumed");
    firstSwipe = false;
    if (checkInternetForNewsFeed() && firstSwipe) {
        removeItems();
        new FetchBooks(false).execute();
        firstSwipe = false;
        Log.d("Swipe", "First swipe");
    } else if (checkInternetForNewsFeed() && !firstSwipe) {
        fetchAllBooksByHeader();
        firstSwipe = true;
        Log.d("Swipe", "Not first swipe");
    } else {
        myRecyclerview.showInfoLayout();
    }

    getActivity().setTitle("Të gjithë");
}

public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

    Log.i("NewsFeedFragment", "Created");

    View v = inflater.inflate(R.layout.news_feed_fragment_layout, container, false);

    dialog = new Dialog(getActivity());
    dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
    dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
    dialog.setContentView(R.layout.loading_layout);

    myRecyclerview = (MyDynamicRecyclerView) v.findViewById(R.id.myRecyclerview);

    modelArrayList = new ArrayList<>();
    testAdapter = new NewsFeedAdapter(getActivity().getApplicationContext(), modelArrayList, (AppCompatActivity) getActivity(), this, false);

    linearLayoutManager = new LinearLayoutManager(getActivity().getApplicationContext());

    myRecyclerview.setScrollContainer(false);
    myRecyclerview.setBackgroundColor("#FFFFFF");
    myRecyclerview.setSwipeRefresh(true);
    myRecyclerview.setColorSchemeColors(Color.RED, Color.YELLOW, Color.BLACK, Color.GREEN, Color.BLUE);
    myRecyclerview.setLoadMore(true, linearLayoutManager);


    myRecyclerview.setLoadMoreListener(this);
    myRecyclerview.setOnRefreshListener(this);


    myRecyclerview.setLayoutManager(linearLayoutManager);
    myRecyclerview.setItemAnimator(new DefaultItemAnimator());
    myRecyclerview.setSimpleDivider(false);
    myRecyclerview.setAdapter(testAdapter);


    limit = 0;
    if (checkInternetForNewsFeed()) {
        firstSwipe = false;
        fetchAllBooksFromDB();
    } else {
        firstSwipe = true;
        myRecyclerview.showInfoLayout();
    }

    return v;
}

public void fetchAllBooksByHeader() {
    limit = 0;
    if (modelArrayList.size() >= 1) {
        removeItems();
    }

    new FetchBooks(true).execute();
    testAdapter.notifyDataSetChanged();
}

public void fetchAllBooksFromDB() {
    if (modelArrayList.size() >= 1) {
        removeItems();
    }

    new FetchBooks(false).execute();
    testAdapter.notifyDataSetChanged();
}

public void addHeader() {
    if (modelArrayList.size() == 0)
        modelArrayList.add(new ProgramModel());
}

public void addItem(String title, String author, String imageName) {
    modelArrayList.add(new ProgramModel(title, author, imageName));
    testAdapter.notifyDataSetChanged();
}

public void removeItems() {
    modelArrayList.clear();
    addHeader();
}

@Override
public void OnRefresh() {

    getActivity().setTitle("Të gjithë");
    limit = 0;
    if (checkInternetForNewsFeed() && firstSwipe) {
        removeItems();
        new FetchBooks(false).execute();
        firstSwipe = false;
        Log.d("Swipe", "First swipe");
    } else if (checkInternetForNewsFeed() && !firstSwipe) {
        removeItems();
        new FetchBooks(true).execute();
        Log.d("Swipe", "Not first swipe");
    } else {
        myRecyclerview.showInfoLayout();
    }

    myRecyclerview.stopSwipeRefresh();


}

private boolean checkInternetForNewsFeed() {
    if (new ConnectivityState(getActivity()).isConnected()) {
        myRecyclerview.hideInfoLayout();
        return true;
    } else {
        // Fonts for the title and the message of the content
        Typeface messageTypeface = Typeface.createFromAsset(getActivity().getAssets(), "fonts/Roboto-Light.ttf");
        Typeface titleTypeface = Typeface.createFromAsset(getActivity().getAssets(), "fonts/Roboto-Bold.ttf");

        // Setting up the content
        myRecyclerview.setInfoIcon(R.drawable.warning_icon, "#000000");
        myRecyclerview.setInfoTitle("Nuk jeni i lidhur me internet.", 18, Color.GRAY, titleTypeface);
        myRecyclerview.setInfoMessage("Rifreskoni edhe njëherë për të kontrolluar aksesin në internet.", 13, Color.GRAY, messageTypeface);
        return false;
    }
}

@Override
public void OnLoadMore() {
    new FetchBooks(true).execute();
    MyDynamicToast.informationMessage(AppController.getInstance(), "...");
}

public static void hideLoadingDialogFromBookActivity(Dialog d) {
    d.hide();
}

private class FetchBooks extends AsyncTask<Void, Void, Void> {

    private boolean reload;

    FetchBooks(boolean reload) {
        this.reload = reload;
    }

    @Override
    protected void onPreExecute() {

    }

    @Override
    protected Void doInBackground(Void... params) {
        getBooksFromDB();
        return null;
    }

    private void getBooksFromDB() {
        StringRequest requestBooks = new StringRequest(Request.Method.GET, AppConfig.URL_FETCH_BOOKS, new Response.Listener<String>() {
            @Override
            public void onResponse(String response) {
                try {
                    JSONArray jsonArray = new JSONArray(response);

                    for (int i = limit; i < jsonArray.length(); i++) {
                        if (i < (limit + 5)) {
                            JSONObject jsonObject = jsonArray.getJSONObject(i);
                            String imageName = "http://ec2-52-39-232-168.us-west-2.compute.amazonaws.com/files/books/" + jsonObject.getString("cover");
                            modelArrayList.add(new ProgramModel(jsonObject.getString("title"), jsonObject.getString("author"), imageName));
                        } else {
                            break;
                        }
                    }
                    limit += 5;
                    if (reload) {
                        testAdapter.notifyDataSetChanged();
                    }
                } catch (JSONException e) {
                    e.printStackTrace();
                    MyDynamicToast.errorMessage(AppController.getInstance(), "" + e.getMessage());
                }
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                MyDynamicToast.errorMessage(AppController.getInstance(), "Request did not work!");
            }
        });

        AppController.getInstance().addToRequestQueue(requestBooks);

    }

}

}

所以基本上我将onRefreshListener添加到recyclerView并调用“onRefresh”。在这个类中,我创建了一个内部的,它将从特定的URL中获取一些数据,并添加填充了从该URL接收的数据的项目。 当我在滚动时尝试刷新时,它给了我这个:

  

03-18 16:11:22.855 5970-5970 / com.libraryhf.libraryharryfultz E / AndroidRuntime:FATAL EXCEPTION:main                                                                                  处理:com.libraryhf.libraryharryfultz,PID:5970                                                                                  java.lang.IndexOutOfBoundsException:检测到不一致。无效的项目位置4(偏移量:4).state:10                                                                                      在android.support.v7.widget.RecyclerView $ Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:5458)                                                                                      在android.support.v7.widget.GapWorker.prefetchPositionWithDeadline(GapWorker.java:270)                                                                                      在android.support.v7.widget.GapWorker.flushTaskWithDeadline(GapWorker.java:324)                                                                                      在android.support.v7.widget.GapWorker.flushTasksWithDeadline(GapWorker.java:337)                                                                                      在android.support.v7.widget.GapWorker.prefetch(GapWorker.java:344)                                                                                      在android.support.v7.widget.GapWorker.run(GapWorker.java:370)                                                                                      在android.os.Handler.handleCallback(Handler.java:751)                                                                                      在android.os.Handler.dispatchMessage(Handler.java:95)                                                                                      在android.os.Looper.loop(Looper.java:154)                                                                                      在android.app.ActivityThread.main(ActivityThread.java:6724)                                                                                      at java.lang.reflect.Method.invoke(Native Method)                                                                                      在com.android.internal.os.ZygoteInit $ MethodAndArgsCaller.run(ZygoteInit.java:1520)                                                                                      在com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1410)

我在网上查了一下,发现一些信息声称这是谷歌应该解决的问题。我一直在努力解决这个问题并没有结果。

如果有人能指出我正确的方向,我会非常感激:解决这个问题或至少处理它。非常感谢你!

1 个答案:

答案 0 :(得分:1)

在适配器上设置后,不要修改列表。特别是不是来自不同的主题。

getBooksFromDB中,您调用modelArrayList.add()这很可能是导致崩溃的原因,因为您修改了从其他线程支持适配器的列表。

当您调用notifyDataSetChanged时,会在实际开始修改列表之前调用它,因为修改会在后台进行。

new FetchBooks(false).execute(); // runs in background and modifies list
testAdapter.notifyDataSetChanged(); // executes immediately

如果您需要修改此列表(很可能不需要),您应该立即通知适配器更改,并且必须进行更改关于ui线程
您还可以使用notifyItem*方法更新更改的适配器,例如notifyItemRangeInserted。或者您可以查看DiffUtil来帮助您更新适配器。

处理列表更改的最常用方法是交换支持适配器的整个列表,然后调用notifyDataSetChanged

创建列表,将项目放入其中,然后将新列表传递给适配器。在您的适配器中放置类似......

void setBooks(List<Book> books) {
  mBooks = books; // swap the whole list
  notifyDataSetChanged(); // notify of change
}

......你不会遇到这个问题。