RecyclerView不会交换适配器onBindViewHolder IndexOutOfBounds

时间:2016-01-13 04:09:58

标签: android android-recyclerview

我尝试在我的应用中使用我的API来更新基于Google MapView位置的RecyclerView,因此这意味着API将返回不同大小的列表,并经常返回。 加载初始应用程序后,当我更改位置时,我会触发一个新的API调用,该调用会创建一个新的Adapter并调用RecyclerView.swapAdapter()方法。 这似乎是一个非常简单的解决方案,但我无法在RecyclerView或RecyclerView.ViewHolder文档中找到解决此问题的任何内容。

问题在于:

  • 如果API返回的列表大于初始列表大小,则recyclerview将保持与最初加载的列表大小相同的大小
  • 如果API返回的列表小于初始列表大小,则会出现返回IndexOutOfBoundsException的运行时错误

这是我的基本代码结构:

在我的API类

public void getFeed(final RecyclerView recView, final Location loc){

    service().getFeed(MyFeeds.API_FEED).enqueue(new Callback<NearbyPeopleProto.NearbyPeopleFeed>() {
        @Override
        public void onResponse(Response<NearbyPeopleProto.NearbyPeopleFeed> response, Retrofit retrofit) {

            Log.d(LOG_TAG, "recview Child count: " + recView.getChildCount());
            ArrayList<NearbyPeople> near = new RtGtfsParser(c).getStopsByLocation(response.body(), loc);

            /*Log.d(LOG_TAG, "onResponse: " + recView.getChildCount() + " -- " + near.get(0).getStops().size());
            if(recView.getChildCount() > near.get(0).getStops().size()) {
                //recView.removeViews(near.get(0).getStops().size() - 1, recView.getChildCount() - 1);
                recView.removeViewAt(1);
                Log.d(LOG_TAG, "onResponse: " + recView.getChildCount());
            }*/

            GroupListAdapter ta = new GroupListAdapter(c, near);
            Log.d(LOG_TAG, "onResponse: Swapping");
            //recView.setAdapter(ta);

            recView.swapAdapter(ta, true);
        }

        @Override
        public void onFailure(Throwable t) {
            Log.e(LOG_TAG, t.getMessage());
        }
    });
}

我的适配器类

public class GroupListAdapter extends
        RecyclerView.Adapter<GroupListAdapter.ViewHolder> {

    ArrayList<NearbyPeople> NearbyPeople;

    public GroupListAdapter(ArrayList<NearbyPeople> NearbyPeople) {
        this.NearbyPeople = NearbyPeople;
    }

    @Override
    public GroupListAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        View stationView = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.list_item, parent, false);

        return new ViewHolder(stationView);
    }

    @Override
    public void onBindViewHolder(GroupListAdapter.ViewHolder vh, int position) {

        if(NearbyPeople.get(0).getStops() != null) {
            Log.d(LOG_TAG, "onBindViewHolder: " + NearbyPeople.get(0).getStops().size());
            vh.locationTag.setText(NearbyPeople.get(0).getStops().get(position).stopName);
        } else {
            vh.locationTag.setText("Loading...");
        }
    }

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

    public static class ViewHolder extends RecyclerView.ViewHolder {

        public TextView locationTag;

        public ViewHolder(View itemView) {
            super(itemView);

            locationTag = (TextView) itemView.findViewById(R.id.location_tag);
        }
    }
}

LogCat输出:

01-12 22:36:27.718 6821-6821/net.mastrgamr.myapp D/TripActivity: onCameraChange: 
//API gets called on the initial app load
01-12 22:36:28.597 6821-6821/net.mastrgamr.myapp I/System.out: LOADING MAP
01-12 22:36:28.622 6821-6831/net.mastrgamr.myapp W/art: Suspending all threads took: 9.511ms
01-12 22:36:28.633 6821-6831/net.mastrgamr.myapp I/art: Background sticky concurrent mark sweep GC freed 16785(866KB) AllocSpace objects, 9(3MB) LOS objects, 0% free, 68MB/68MB, paused 16.852ms total 67.527ms
01-12 22:36:28.635 6821-6821/net.mastrgamr.myapp D/TripActivity: onMyLocationChange: Location[fused 40.882174,-73.833406 acc=50 et=+2d19h35m3s487ms]
01-12 22:36:28.685 6821-6831/net.mastrgamr.myapp I/art: Background partial concurrent mark sweep GC freed 2960(217KB) AllocSpace objects, 28(26MB) LOS objects, 24% free, 48MB/64MB, paused 5.893ms total 47.174ms
01-12 22:36:28.903 6821-6821/net.mastrgamr.myapp D/ApiService: recview Child count: 1
01-12 22:36:28.906 6821-6821/net.mastrgamr.myapp D/NearbyGroups: mapping to model
01-12 22:36:28.913 6821-6821/net.mastrgamr.myapp I/System.out: MAPPED!!: [net.mastrgamr.tpulse.models.realtime.NearbyGroups$Stops@6ebd215, net.mastrgamr.tpulse.models.realtime.NearbyGroups$Stops@da9092a]
01-12 22:36:28.913 6821-6821/net.mastrgamr.myapp D/NearbyGroups: mapping to model
01-12 22:36:28.926 6821-6821/net.mastrgamr.myapp I/System.out: MAPPED!!: [net.mastrgamr.tpulse.models.realtime.NearbyGroups$Stops@23fe31b, net.mastrgamr.tpulse.models.realtime.NearbyGroups$Stops@e481cb8]
01-12 22:36:28.927 6821-6821/net.mastrgamr.myapp D/ApiService: onResponse: Swapping
01-12 22:36:28.943 6821-6821/net.mastrgamr.myapp D/GroupListAdpter: onBindViewHolder: 2
01-12 22:36:28.952 6821-6821/net.mastrgamr.myapp D/GroupListAdpter: onBindViewHolder: 2
01-12 22:36:29.996 6821-6821/net.mastrgamr.myapp D/TripActivity: onCameraChange: 
01-12 22:36:33.397 6821-6821/net.mastrgamr.myapp D/TripActivity: onCameraChange: 
//API also gets called when the map is moved.
01-12 22:36:33.399 6821-6821/net.mastrgamr.myapp D/TripActivity: CAMERA MOVED -- Regenerating: Location[Moved Location 40.880845,-73.837932 acc=??? t=?!? et=?!?]
01-12 22:36:33.749 6821-6821/net.mastrgamr.myapp D/ApiService: recview Child count: 2
01-12 22:36:33.749 6821-6821/net.mastrgamr.myapp D/NearbyGroups: mapping to model
01-12 22:36:33.755 6821-6821/net.mastrgamr.myapp I/System.out: MAPPED!!: [net.mastrgamr.tpulse.models.realtime.NearbyGroups$Stops@90eab4a]
01-12 22:36:33.755 6821-6821/net.mastrgamr.myapp D/NearbyGroups: mapping to model
01-12 22:36:33.762 6821-6821/net.mastrgamr.myapp I/System.out: MAPPED!!: [net.mastrgamr.tpulse.models.realtime.NearbyGroups$Stops@8abaabb]
01-12 22:36:33.763 6821-6821/net.mastrgamr.myapp D/ApiService: onResponse: Swapping
01-12 22:36:33.770 6821-6821/net.mastrgamr.myapp D/GroupListAdpter: onBindViewHolder: 1
01-12 22:36:33.772 6821-6821/net.mastrgamr.myapp D/GroupListAdpter: onBindViewHolder: 1
01-12 22:36:33.778 6821-6821/net.mastrgamr.myapp D/AndroidRuntime: Shutting down VM
01-12 22:36:33.995 6821-6821/net.mastrgamr.myapp E/AndroidRuntime: FATAL EXCEPTION: main
                                                                          Process: net.mastrgamr.myapp, PID: 6821
                                                                          java.lang.IndexOutOfBoundsException: Invalid index 1, size is 1
                                                                              at java.util.ArrayList.throwIndexOutOfBoundsException(ArrayList.java:255)
                                                                              at java.util.ArrayList.get(ArrayList.java:308)
                                                                              at net.mastrgamr.tpulse.adapters.GroupListAdpter.onBindViewHolder(GroupListAdpter.java:63)
                                                                              at net.mastrgamr.tpulse.adapters.GroupListAdpter.onBindViewHolder(GroupListAdpter.java:25)
                                                                              at android.support.v7.widget.RecyclerView$Adapter.onBindViewHolder(RecyclerView.java:5217)
                                                                              at android.support.v7.widget.RecyclerView$Adapter.bindViewHolder(RecyclerView.java:5250)
                                                                              at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4487)
                                                                              at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4363)
                                                                              at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:1961)

2 个答案:

答案 0 :(得分:0)

两个Swap之间的list仅在大小相同的情况下有效,如果list中的一个比其他swap更短或更大,则String one = {"1","2"}将无效预计,你得到一个假定的结果。

示例:String two = {"4","8","10"}two交换。你知道会发生什么会在one数组中丢弃最后一个元素,因为adapter数组没有第3个元素。

虽然swapAdapter();没有显示文档,但您可以假设这是。

处理此问题的最佳和最快捷的方法是制作新的RecyclerView并将其分配给zipWithIndex

答案 1 :(得分:0)

我发现了这个问题,这是一个基本的错误。如果您不了解我的适配器列表结构,可能很难错过。

我的适配器代码

    -----
    @Override
    public void onBindViewHolder(GroupListAdapter.ViewHolder vh, int position) {

        if(NearbyPeople.get(0).getStops() != null) {
            // the line below accesses an inner array to get to the info i really want
            vh.locationTag.setText(NearbyPeople.get(0).getStops().get(position).stopName);
        } else {
            vh.locationTag.setText("Loading...");
        }
    }

    @Override
    public int getItemCount() {
        return NearbyPeople.size(); //Causing bug/runtime error. This only accesses the root 'node' of the array
    }
   -----

问题发生在getItemCount()。我使用此适配器根据“根”数组中的2个内部数组之一显示信息。我只返回getItemCount()中根数组的大小(仅为2)。

这就是为什么当我得到的API结果小于1时,我的应用程序会抛出RuntimeException,如果结果返回的数组大于2,它仍然只会在我的RecyclerView中显示2个元素。