RecyclerView:检测到 IndexOutOfBoundsException 不一致。无效的项目位置

Posted

技术标签:

【中文标题】RecyclerView:检测到 IndexOutOfBoundsException 不一致。无效的项目位置【英文标题】:RecyclerView: IndexOutOfBoundsException Inconsistency detected. Invalid item position 【发布时间】:2017-09-30 20:04:58 【问题描述】:

致命异常:检测到 java.lang.IndexOutOfBoundsException 不一致。无效的项目位置 5(offset:5).state:24

这种崩溃发生的太多了!!!

我在 Fabric Crashlytics 中得到它。

致命异常:java.lang.IndexOutOfBoundsException:不一致 检测到。无效的项目位置 5(offset:5).state:41 在 android.support.v7.widget.RecyclerView$Recycler.clear(未知来源) 在 android.support.v7.widget.GapWorker.add(未知来源) 在 android.support.v7.widget.GapWorker.add(未知来源) 在 android.support.v7.widget.GapWorker.remove(未知来源) 在 android.support.v7.widget.GapWorker.add(未知来源) 在 android.support.v7.widget.GapWorker.run(未知来源) 在 android.os.Handler.handleCallback(Handler.java:739) 在 android.os.Handler.dispatchMessage(Handler.java:95) 在 android.os.Looper.loop(Looper.java:135) 在 android.app.ActivityThread.main(ActivityThread.java:5930) 在 java.lang.reflect.Method.invoke(Method.java) 在 java.lang.reflect.Method.invoke(Method.java:372) 在 com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1405) 在 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1200)

DateList是涉及RecyclerView的Fragment:

public class DateList extends Fragment 

DatesItemAdapter adapter;
List<DateItem2> keyList;
CardView cardView;
double v = 1;
float radius = 1;
ProgressBarCircular mCircular;
SwipeRefreshLayout swipeRefreshLayout;
RecyclerView mRecyclerView;
Animation animSlideUp;
Animation animSlideDown;
Uri.Builder builder;
JsonObjectRequest callToServerRequest;

public DateList() 



@Override
public void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);


@Override
public void onResume() 
    super.onResume();

    swipeRefreshLayout.post(new Runnable() 
                                @Override
                                public void run() 

                                    callToServer(false);

                                
                            
    );


@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) 
    View view = inflater.inflate(R.layout.fragment_date_list, container, false);

    ((MainActivity) getActivity()).setTitle(getString(R.string.third_page));
    ((MainActivity) getActivity()).setToolbarVisibility(false);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) 
        container.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
    

    mCircular = (ProgressBarCircular) view.findViewById(R.id.date_list_progressBar);
    animSlideUp = AnimationUtils.loadAnimation(getActivity(), R.anim.slide_up);
    animSlideDown = AnimationUtils.loadAnimation(getActivity(), R.anim.slide_down);
    swipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.date_list_swipe_refresh_layout);
    mRecyclerView = (RecyclerView) view.findViewById(R.id.date_list_container);
    keyList = new ArrayList<>();

    adapter = new DatesItemAdapter(keyList, new DatesItemAdapter.DateItemClickListener() 
        @Override
        public void onClick(View v, int pos) 
            if (pos < keyList.size()) 

                try 
                    LinearLayoutManager linearLayoutManager = (LinearLayoutManager) mRecyclerView.getLayoutManager();
                    linearLayoutManager.scrollToPositionWithOffset(pos, 0);
                 catch (Exception e) 
                    e.printStackTrace();
                
            
        
    , getActivity(), view);

    ((MainActivity) getActivity()).setTitle("Hello " + getActivity().getResources().getString(R.string.date_list_be) + " " + "World");

    builder = new Uri.Builder();
    builder.scheme("https")
            .authority("mydomain.ir")
            .appendPath("mywebservices")
            .appendPath("myWs");

    cardView = new CardView(getActivity());
    try 
        cardView.setRadius(2);
     catch (Exception e) 
        e.printStackTrace();
    

    cardView.findViewById(R.id.date_list_item_card_view);
    radius = cardView.getRadius();

    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) 
        try 
            v = ((cardView.getMaxCardElevation()) * 1.5) + ((1 - Math.cos(45)) * (radius));
         catch (Exception e) 
            e.printStackTrace();
        
    

    try 
        mRecyclerView.addItemDecoration(new DividerItemDecoration(8, getActivity(), v));
     catch (Exception e) 
        e.printStackTrace();
    

    try 
        final LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
        layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
        mRecyclerView.setLayoutManager(layoutManager);
     catch (Exception e) 
        e.printStackTrace();
    

    mRecyclerView.setItemAnimator(new TchAnimator());

    mRecyclerView.setAdapter(adapter);

    swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() 
        @Override
        public void onRefresh() 

            mRecyclerView.setVisibility(View.GONE);

            callToServer(false);

        
    );

    final ImageView imageView = (ImageView) view.findViewById(R.id.date_list_header_image);
    final String url = "https://pic.mydomain.ir/pics/" + getResources().getString(R.string.url) + "photo.jpg";
    ImageLoader loader = AppController.getInstance().getImageLoader();
    loader.get(url
            , new ImageLoader.ImageListener() 
                @Override
                public void onResponse(ImageLoader.ImageContainer imageContainer, boolean b) 
                    try 
                        imageView.setImageBitmap(imageContainer.getBitmap());
                     catch (Exception e) 
                        e.printStackTrace();
                    
                

                @Override
                public void onErrorResponse(VolleyError volleyError) 
                
            );

    return view;


public void callToServer(final boolean rTime) 

    String url = builder.toString();

    if (rTime) 
        url = url + "&rTime=1";
     else 
        url = url + "&rTime=0";
    

    callToServerRequest = new JsonObjectRequest(Request.Method.GET
            , url
            , null
            , new Response.Listener<JSONObject>() 
        @Override
        public void onResponse(JSONObject response) 

            try 
                JSONArray array = new JSONArray(response.getString("Ticks"));

                // Clears list to replace new list items
                keyList.clear();

                adapter.notifyDataSetChanged();

                for (int i = 0; i < array.length(); i++) 
                    JSONObject obj = new JSONObject(array.get(i).toString());
                    DateItem2 item = new DateItem2();

                    item.setAvailable(obj.getString("available"));
                    item.setType(obj.getString("type"));
                    item.setUrl(obj.getString("url"));
                    item.setTime(obj.getString("time"));
                    item.setDescription(obj.getString("description"));

                    keyList.add(item);
                

                if (rTime) 
                    mCircular.setVisibility(View.GONE);
                

                adapter.notifyDataSetChanged();
                mRecyclerView.setVisibility(View.VISIBLE);
                swipeRefreshLayout.setRefreshing(false);

             catch (JSONException e) 
                e.printStackTrace();
            

            if (!rTime) 
                callToServer(true);
            
        
    , new Response.ErrorListener() 
        @Override
        public void onErrorResponse(VolleyError volleyError) 

            if (rTime) 
                mCircular.setVisibility(View.GONE);
            

            try 
                if (ConnectionDetector.IS_CONNECTED(getActivity())) 
                    if (volleyError.toString().contains("[]") && !rTime) 

                        callToServer(true);

                    
                
             catch (Exception e) 
                e.printStackTrace();
            

            swipeRefreshLayout.setRefreshing(false);
        
    );

    AppController.getInstance().addToRequestQueue(callToServerRequest, "DateList" + "callToServerRequest");


@Override
public void onActivityCreated(Bundle savedInstanceState) 
    super.onActivityCreated(savedInstanceState);

    try 
        if (getView() != null) 
            getView().setFocusableInTouchMode(true);
            getView().requestFocus();
        
     catch (Exception e) 
        e.printStackTrace();
    

    getView().setOnKeyListener(new View.OnKeyListener() 

        @Override
        public boolean onKey(View v, int keyCode, KeyEvent event) 
            if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) 
                try 
                    callToServerRequest.cancel();
                 catch (Exception e) 
                    e.printStackTrace();
                
            
            return false;
        
    );


callToServer (with false input) 首先清除 keyList 然后填充它。几毫秒后 callToServer (带有真实输入)将被调用以再次清除 keyList 并填充新项目。这两个事件将在每次刷新中重复。

这是适配器:

public class DatesItemAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> 

private List<DateItem2> items;
private Context mContext;
private DateItemClickListener listener;
private Animation animRotate;
private LogAndReg logAndReg;
private View mainView;

public DatesItemAdapter(List<DateItem2> items, DateItemClickListener listener, Context mContext, View v) 
    this.items = items;
    this.listener = listener;
    this.mContext = mContext;
    animRotate = AnimationUtils.loadAnimation(mContext, R.anim.rotation);
    this.mainView = v;


public interface DateItemClickListener 
    void onClick(View v, int pos);


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

    try 
        View view = LayoutInflater.from(mContext).inflate(R.layout.date_list_list_item, parent, false);

        switch (viewType) 
            case 0:
                return new GuarantyOpen(view);

            case 1:
                return new GuarantyClose(view);

            case 2:
                return new FreeOpenRefresh(view);

            case 3:
                return new FreeCloseRefresh(view);

            case 4:
                return new FreeOpen(view);

            case 5:
                return new FreeClose(view);
        
     catch (Exception e) 
        e.printStackTrace();
    
    return null;


@Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) 

    if (holder instanceof GuarantyOpen) 
        final GuarantyOpen h0 = (GuarantyOpen) holder;

        h0.time.setText(items.get(h0.getAdapterPosition()).getTime());
        h0.type.setText(getModelType(h0.getAdapterPosition()));

        if (items.get(h0.getAdapterPosition()).getAvailable().contentEquals("500")) 
            h0.available.setText(R.string.is_available);
         else 
            h0.available.setText(items.get(h0.getAdapterPosition()).getAvailable());
        

        if (items.get(h0.getAdapterPosition()).getDescription().contentEquals("")) 
            h0.description.setVisibility(View.GONE);
         else 
            h0.description.setText(items.get(h0.getAdapterPosition()).getDescription());
        

        h0.url.setOnClickListener(new View.OnClickListener() 

            @Override
            public void onClick(View view) 
                h0.url.setEnabled(true);

                Intent intent = new Intent(mContext, Reserve.class);
                intent.putExtra("type", items.get(h0.getAdapterPosition()).getType());
                intent.putExtra("time", items.get(h0.getAdapterPosition()).getTime());

                mContext.startActivity(intent);
            
        );

        h0.url.setBackgroundResource(R.drawable.button_bg_green_selector);

     else if (holder instanceof GuarantyClose) 
        final GuarantyClose h1 = (GuarantyClose) holder;

        try 
            h1.time.setText(items.get(h1.getAdapterPosition()).getTime());
         catch (Exception e) 
            e.printStackTrace();
        

        if (items.get(h1.getAdapterPosition()).getAvailable().contentEquals("500")) 
            h1.available.setText(R.string.is_available);
         else 
            h1.available.setText(items.get(h1.getAdapterPosition()).getAvailable());
        

     else if (holder instanceof FreeOpenRefresh) 
        final FreeOpenRefresh h2 = (FreeOpenRefresh) holder;

        h2.time.setText(items.get(h2.getAdapterPosition()).getTime());
        h2.type.setText(getModelType(h2.getAdapterPosition()));


        if (items.get(h2.getAdapterPosition()).getAvailable().contentEquals("500")) 
            h2.available.setText(R.string.is_available);
         else 
            h2.available.setText(items.get(h2.getAdapterPosition()).getAvailable());
        

        if (!((MainActivity) mContext).isLoggedIn()) 
            h2.url.setText(R.string.date_adapter_login);
         else 
            h2.url.setText(R.string.date_adapter_trans);
        

        if (items.get(h2.getAdapterPosition()).getDescription().contentEquals("")) 
            h2.description.setVisibility(View.GONE);
         else 
            h2.description.setText(items.get(h2.getAdapterPosition()).getDescription());
        

        h2.url.setOnClickListener(new View.OnClickListener() 

            @Override
            public void onClick(View view) 
                h2.url.setEnabled(false);

                if (!((MainActivity) mContext).isLoggedIn()) 
                    h2.url.setEnabled(false);
                    logAndReg = LogAndReg.newInstance();
                    logAndReg.show(((MainActivity) mContext).getSupportFragmentManager(), "tag");
                    h2.childLayout.setVisibility(View.GONE);
                 else 
                    h2.url.setEnabled(true);

                    Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse("http://" + items.get(h2.getAdapterPosition()).getUrl()));
                    mContext.startActivity(i);
                
            
        );

     else if (holder instanceof FreeCloseRefresh) 
        final FreeCloseRefresh h3 = (FreeCloseRefresh) holder;

        h3.time.setText(items.get(h3.getAdapterPosition()).getTime());

        if (items.get(h3.getAdapterPosition()).getAvailable().contentEquals("500")) 
            h3.available.setText(R.string.is_available);
         else 
          h3.available.setText(items.get(h3.getAdapterPosition()).getAvailable());
        

        if (!((MainActivity) mContext).isLoggedIn()) 
            h3.url.setText(R.string.date_adapter_login);
         else 
            h3.url.setText(R.string.date_adapter_trans);
        

     else if (holder instanceof FreeOpen) 
        final FreeOpen h4 = (FreeOpen) holder;

        h4.time.setText(items.get(h4.getAdapterPosition()).getTime());
        h4.available.setText(items.get(h4.getAdapterPosition()).getAvailable());
        h4.type.setText(getModelType(h4.getAdapterPosition()));

        if (items.get(h4.getAdapterPosition()).getAvailable().contentEquals("500")) 
            h4.available.setText(R.string.is_available);
         else 
            h4.available.setText(items.get(h4.getAdapterPosition()).getAvailable());
        

        if (!((MainActivity) mContext).isLoggedIn()) 
            h4.url.setText(R.string.date_adapter_login);
         else 
            h4.url.setText(R.string.date_adapter_trans);
        

        if (items.get(h4.getAdapterPosition()).getDescription().contentEquals("")) 
            h4.description.setVisibility(View.GONE);
         else 
            h4.description.setText(items.get(h4.getAdapterPosition()).getDescription());
        

        h4.url.setOnClickListener(new View.OnClickListener() 

            @Override
            public void onClick(View view) 
                h4.url.setEnabled(false);

                if (!((MainActivity) mContext).isLoggedIn()) 
                    h4.url.setEnabled(false);
                    logAndReg = LogAndReg.newInstance();
                    logAndReg.show(((MainActivity) mContext).getSupportFragmentManager(), "tag");
                    h4.childLayout.setVisibility(View.GONE);
                 else 
                    h4.url.setEnabled(true);
                    Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse("http://" + items.get(h4.getAdapterPosition()).getUrl()));
                    mContext.startActivity(i);
                
            
        );

     else if (holder instanceof FreeClose) 
        final FreeClose h5 = (FreeClose) holder;

        h5.time.setText(items.get(h5.getAdapterPosition()).getTime());

        if (items.get(h5.getAdapterPosition()).getAvailable().contentEquals("500")) 
            h5.available.setText(R.string.is_available);
         else 
            h5.available.setText(items.get(h5.getAdapterPosition()).getAvailable());
        

        if (!((MainActivity) mContext).isLoggedIn()) 
            h5.url.setText(R.string.date_adapter_login);
         else 
            h5.url.setText(R.string.date_adapter_trans);
        
    



@Override
public int getItemCount() 
    return items.size();


@Override
public int getItemViewType(int position) 
    return items.get(position).getexpandState().getValue();


private class GuarantyOpen extends RecyclerView.ViewHolder implements View.OnClickListener 

    TextView time, available, description, type;
    Button url;
    LinearLayout parentLayout;

    GuarantyOpen(View v) 
        super(v);

        time = (TextView) v.findViewById(R.id.date_list_item_text_time);
        available = (TextView) v.findViewById(R.id.date_list_item_text_available);
        description = (TextView) v.findViewById(R.id.date_list_item_text_type);
        type = (TextView) v.findViewById(R.id.date_list_item_ticket_type);
        url = (Button) v.findViewById(R.id.date_list_item_btn_open_url);

        url.setText(R.string.xml_reserve_reservation_btn);
        parentLayout.setOnClickListener(this);
    

    @Override
    public void onClick(View view) 
        try 
            listener.onClick(view, getAdapterPosition());
            items.get(getAdapterPosition()).setexpandState(DateItemStatus.GUARANTY_CLOSE);
            onBindViewHolder(this, getAdapterPosition());
         catch (Exception e) 
            e.printStackTrace();
        
    


private class GuarantyClose extends RecyclerView.ViewHolder implements View.OnClickListener 

    TextView time;
    TextView available;
    Button url;
    LinearLayout parentLayout;
    RelativeLayout childLayout;

    GuarantyClose(View v) 
        super(v);

        time = (TextView) v.findViewById(R.id.date_list_item_text_time);
        available = (TextView) v.findViewById(R.id.date_list_item_text_available);
        url = (Button) v.findViewById(R.id.date_list_item_btn_open_url);
        parentLayout = (LinearLayout) v.findViewById(R.id.date_list_item_parent_layout);
        childLayout = (RelativeLayout) v.findViewById(R.id.date_list_item_child_layout);
        childLayout.setVisibility(View.GONE);
        parentLayout.setBackgroundColor(Color.parseColor("#f3faf4"));
        childLayout.setBackgroundColor(Color.parseColor("#c8e6c9"));

        parentLayout.setOnClickListener(this);
    

    @Override
    public void onClick(View view) 
        try 
            listener.onClick(view, getAdapterPosition());
            items.get(getAdapterPosition()).setexpandState(DateItemStatus.GUARANTY_OPEN);
            onBindViewHolder(this, getAdapterPosition());
         catch (Exception e) 
            e.printStackTrace();
        
    


private class FreeOpenRefresh extends RecyclerView.ViewHolder implements View.OnClickListener 

    TextView time;
    TextView available;
    TextView description;
    TextView type;
    Button url;
    LinearLayout parentLayout;
    RelativeLayout childLayout;

    FreeOpenRefresh(View v) 
        super(v);
        time = (TextView) v.findViewById(R.id.date_list_item_text_time);
        available = (TextView) v.findViewById(R.id.date_list_item_text_available);
        description = (TextView) v.findViewById(R.id.date_list_item_text_type);
        type = (TextView) v.findViewById(R.id.date_list_item_ticket_type);
        url = (Button) v.findViewById(R.id.date_list_item_btn_open_url);

        parentLayout = (LinearLayout) v.findViewById(R.id.date_list_item_parent_layout);
        childLayout = (RelativeLayout) v.findViewById(R.id.date_list_item_child_layout);
        parentLayout.setOnClickListener(this);
    

    @Override
    public void onClick(View view) 
        if (view.getId() == R.id.date_list_item_btn_refresh) 
            try 
                items.get(getAdapterPosition()).setexpandState(DateItemStatus.FREE_REFRESH_OPEN);
             catch (Exception e) 
                e.printStackTrace();
            
         else 
            try 
                listener.onClick(view, getAdapterPosition());
                items.get(getAdapterPosition()).setexpandState(DateItemStatus.FREE_REFRESH_CLOSE);
                onBindViewHolder(this, getAdapterPosition());
             catch (Exception e) 
                e.printStackTrace();
            
        
    


private class FreeCloseRefresh extends RecyclerView.ViewHolder implements View.OnClickListener 
    TextView time;
    TextView available;
    Button url;
    LinearLayout parentLayout;
    RelativeLayout childLayout;

    FreeCloseRefresh(View v) 
        super(v);
        time = (TextView) v.findViewById(R.id.date_list_item_text_time);
        available = (TextView) v.findViewById(R.id.date_list_item_text_available);
        url = (Button) v.findViewById(R.id.date_list_item_btn_open_url);
        parentLayout = (LinearLayout) v.findViewById(R.id.date_list_item_parent_layout);
        childLayout = (RelativeLayout) v.findViewById(R.id.date_list_item_child_layout);

        childLayout.setVisibility(View.GONE);
        parentLayout.setOnClickListener(this);
    

    @Override
    public void onClick(View view) 

        if (view.getId() == R.id.date_list_item_btn_refresh) 
            try 
                items.get(getAdapterPosition()).setexpandState(DateItemStatus.FREE_REFRESH_CLOSE);
             catch (Exception e) 
                e.printStackTrace();
            
         else 
            try 
                listener.onClick(view, getAdapterPosition());
                items.get(getAdapterPosition()).setexpandState(DateItemStatus.FREE_REFRESH_OPEN);
                onBindViewHolder(this, getAdapterPosition());
             catch (Exception e) 
                e.printStackTrace();
            
        
    


private class FreeOpen extends RecyclerView.ViewHolder implements View.OnClickListener 

    TextView time;
    TextView available;
    TextView description;
    TextView type;
    Button url;
    LinearLayout parentLayout;
    RelativeLayout childLayout;

    FreeOpen(View v) 
        super(v);
        time = (TextView) v.findViewById(R.id.date_list_item_text_time);
        available = (TextView) v.findViewById(R.id.date_list_item_text_available);
        description = (TextView) v.findViewById(R.id.date_list_item_text_type);
        type = (TextView) v.findViewById(R.id.date_list_item_ticket_type);
        url = (Button) v.findViewById(R.id.date_list_item_btn_open_url);
        parentLayout = (LinearLayout) v.findViewById(R.id.date_list_item_parent_layout);
        childLayout = (RelativeLayout) v.findViewById(R.id.date_list_item_child_layout);

        parentLayout.setOnClickListener(this);
    

    @Override
    public void onClick(View view) 
        try 
            listener.onClick(view, getAdapterPosition());
            items.get(getAdapterPosition()).setexpandState(DateItemStatus.FREE_CLOSER);
            onBindViewHolder(this, getAdapterPosition());
         catch (Exception e) 
            e.printStackTrace();
        
    


private class FreeClose extends RecyclerView.ViewHolder implements View.OnClickListener 

    TextView time;
    TextView available;
    Button url;
    LinearLayout parentLayout;
    RelativeLayout childLayout;

    FreeClose(View v) 
        super(v);
        time = (TextView) v.findViewById(R.id.date_list_item_text_time);
        available = (TextView) v.findViewById(R.id.date_list_item_text_available);
        url = (Button) v.findViewById(R.id.date_list_item_btn_open_url);
        parentLayout = (LinearLayout) v.findViewById(R.id.date_list_item_parent_layout);
        childLayout = (RelativeLayout) v.findViewById(R.id.date_list_item_child_layout);

        childLayout.setVisibility(View.GONE);
        parentLayout.setOnClickListener(this);

    

    @Override
    public void onClick(View view) 
        try 
            listener.onClick(view, getAdapterPosition());
            items.get(getAdapterPosition()).setexpandState(DateItemStatus.FREE_OPEN);
            onBindViewHolder(this, getAdapterPosition());
         catch (Exception e) 
            e.printStackTrace();
        
    


private String getModelType(int position) 

    String tType;
    String type = items.get(position).getType();

    switch (type) 
        case "bclass":
            ...
            break;

        case "normal":
            ...
            break;

        case "phone":
            ...
            break;

        case "phone-bclass":
            ...
            break;
    

    return tType;


我尝试了其他一些解决方案,例如 THIS 和 THIS 和 THIS,但它们并没有解决我的问题。我认为我的问题与其他人不同,因为我在每次通话中都会清除我的列表。

我根本找不到问题所在!!!

【问题讨论】:

请问您是如何确定哪个 RecyclerView 是问题的根源?我和你在同一条船上,但我不知道应该归咎于哪个班级。 @JasonRobinson :我有同样的问题,但我猜这个 RecyclerView 会导致崩溃!因为所有用户在每次使用时都会查看这个Fragment,而其他RecyclerViews在一些不太常见的Activity和Fragment中。 您的问题是重复的。检查以下问题:***.com/questions/26827222/… @dowookim 是的,但前一个是由于 RecyclerView 中的错误导致的,并且接受的解决方案不再有效。这个问题现在真的需要一个不同的答案 由于这个问题已经快3年了,甚至不清楚使用的是哪个版本。代码很乱,并且没有任何适当的堆栈跟踪;不会碰那个。 GitHub 上的minimal 示例应用程序可能有助于重现问题 - 并获得可靠的答案。 【参考方案1】:

这可能是一个并发问题。我不知道 JsonObjectRequest 是如何工作的,但它的回调可能是在后台线程中调用的。因此,当您在更新数据时滚动时,回收器视图会尝试创建其信息不再存在的视图。

为了避免这种情况,一种可能的解决方案是将回调例程包装在 Runnable 中,以便由主线程中的 Handler 调用。像这样的:

public void callToServer(final boolean rTime) 

    String url = builder.toString();

    if (rTime) 
        url = url + "&rTime=1";
     else 
        url = url + "&rTime=0";
    

    final Handler handler = new Handler(getMainLooper());

    callToServerRequest = new JsonObjectRequest(Request.Method.GET
            , url
            , null
            , new Response.Listener<JSONObject>() 
        @Override
        public void onResponse(JSONObject response) 
            handler.post(new Runnable() 
                @Override
                public void run() 
                    try 
                        JSONArray array = new JSONArray(response.getString("Ticks"));

                        // Clears list to replace new list items
                        keyList.clear();

                        adapter.notifyDataSetChanged();

                        for (int i = 0; i < array.length(); i++) 
                            JSONObject obj = new JSONObject(array.get(i).toString());
                            DateItem2 item = new DateItem2();

                            item.setAvailable(obj.getString("available"));
                            item.setType(obj.getString("type"));
                            item.setUrl(obj.getString("url"));
                            item.setTime(obj.getString("time"));
                            item.setDescription(obj.getString("description"));

                            keyList.add(item);
                        

                        if (rTime) 
                            mCircular.setVisibility(View.GONE);
                        

                        adapter.notifyDataSetChanged();
                        mRecyclerView.setVisibility(View.VISIBLE);
                        swipeRefreshLayout.setRefreshing(false);

                     catch (JSONException e) 
                        e.printStackTrace();
                    

                    if (!rTime) 
                        callToServer(true);
                    
                
            );

        
    , new Response.ErrorListener() 
        @Override
        public void onErrorResponse(VolleyError volleyError) 
            handler.post(new Runnable() 
                @Override
                public void run() 
                    if (rTime) 
                        mCircular.setVisibility(View.GONE);
                    

                    try 
                        if (ConnectionDetector.IS_CONNECTED(getActivity())) 
                            if (volleyError.toString().contains("[]") && !rTime) 

                                callToServer(true);

                            
                        
                     catch (Exception e) 
                        e.printStackTrace();
                    

                    swipeRefreshLayout.setRefreshing(false);
                
            );
        
    );

    AppController.getInstance().addToRequestQueue(callToServerRequest, "DateList" + "callToServerRequest");

PS:清除列表后无需调用 notifyDataSetChanged。就在你填充它之后。

【讨论】:

感谢您的回答。我会试试看。但需要一段时间才能确定它是否有效。 @Kevin 我仍然无法拒绝或接受此解决方案,因为我从未在自己的设备上遇到过这种崩溃。我只在 Firebase Crashlytics 控制台收到此崩溃报告。所以我不得不发布一个新版本,期待它的反馈。 一个好的测试用例是使用一台旧设备并在后台运行许多应用程序。而且,更重要的是,列表必须充满项目,越多越好。尝试在更新列表时滚动列表。 没用!我仍然收到java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positiona2fd6b8b position=9 id=-1, oldPos=-1, pLpos:-1 no parent【参考方案2】:

为我工作!

这是一个常见的问题,当您尝试在列表中添加数据时。

尝试按照以下步骤操作:

1 - 清除您的列表

2 - 通知您的适配器

3 - 将数据添加到您的列表中

4 - 通知适配器

【讨论】:

【参考方案3】:

将您的布局管理器更改为 WrapContentLinearLayoutManager,这将解决问题。

public class WrapContentLinearLayoutManager extends LinearLayoutManager 
        public WrapContentLinearLayoutManager(Context context) 
            super(context);
        

        public WrapContentLinearLayoutManager(Context context, int orientation, boolean reverseLayout) 
            super(context, orientation, reverseLayout);
        

        public WrapContentLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) 
            super(context, attrs, defStyleAttr, defStyleRes);
        

        //... constructor
        @Override
        public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) 
            try 
                super.onLayoutChildren(recycler, state);
             catch (IndexOutOfBoundsException e) 
                LogUtils.debugMsg("probe" + "meet a IOOBE in RecyclerView");
            
        

        @Override
        public boolean supportsPredictiveItemAnimations() 
            return false;
        
    

【讨论】:

你能解释一下你提供的解决方案吗? 我不知道这个解决方案是如何工作的,但它对我有用 你得到的正是IndexOutOfBoundsException 吗? 是的,我遇到了同样的错误,也尝试了 Diff utils,但最后我用这个布局管理器修复了 这个问题表明这个解决方案有助于减少崩溃的数量,但并没有完全消除它:***.com/q/41300626/1101730【参考方案4】:

我只是通过从LinearLayoutManager 中删除对预测项目动画的支持来解决此问题:

import android.content.Context
import android.util.AttributeSet
import androidx.recyclerview.widget.LinearLayoutManager

class CustomLinearLayoutManager @JvmOverloads constructor(
    context: Context?,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : LinearLayoutManager(context, attrs, defStyleAttr, defStyleAttr) 

    override fun supportsPredictiveItemAnimations() = false

如果您需要水平方向,请使用

init 
    orientation = HORIZONTAL

【讨论】:

我不确定我要说什么,因为您在 Kotlin 中实现了它,但看起来您的代码与 Kashyap 的代码相似,但我看到您在他/她的帖子下发表了评论该解决方案将在一定程度上减少崩溃,但不会归零。我有一些疑问。你能用Java写吗?接下来的问题是,你的代码和 Kashyap 的代码有什么区别?你的代码能完全解决问题吗?您是否在许多用户使用的应用程序中尝试过此解决方案? @AlirezaNoorali 不同之处在于不需要在onLayoutChildren 中添加try-catch,因为永远不会抛出异常(至少在我的情况下)。自从我关闭了预测项目动画后,我没有遇到任何崩溃,但根据***.com/q/41300626/1101730,仍然可能有一些崩溃。我将在下周将其推送给成千上万的用户,让我们拭目以待。 这样我们会有一个很好的测试。请在此处与我们分享此实验的结果。 @AlirezaNoorali 看起来这确实修复了崩溃,一个月后我可以说它不再发生了。 如果是这样,那就太好了

以上是关于RecyclerView:检测到 IndexOutOfBoundsException 不一致。无效的项目位置的主要内容,如果未能解决你的问题,请参考以下文章

RecyclerView:检测到不一致。无效的项目位置。原因 - 使用计时器删除项目

RecyclerView 和 java.lang.IndexOutOfBoundsException:检测到不一致。三星设备中的无效视图支架适配器 positionViewHolder

RecyclerView java.lang.IndexOutOfBoundsException:检测到不一致。视图支架适配器位置无效。由于更改 getItemCount()

hive脚本出现Error: java.lang.RuntimeException: Error in configuring object和Caused by: java.lang.IndexOut

检测 RecyclerView 滚动时何时到达最底部位置

如何创建一个循环(无尽的)RecyclerView?