taltstidl / Swipeable-RecyclerView

A library that provides an easy and customizable way to implement a swipe to dismiss pattern with RecyclerView.
Apache License 2.0
87 stars 17 forks source link

IndexOutOfBoundsException when adding new item to adapter ArrayList #16

Open ashishoc opened 8 years ago

ashishoc commented 8 years ago

I'm trying to add new Item in list using adapter instance after setting adapter.

E/AndroidRuntime: FATAL EXCEPTION: main
                  Process: app.messaging.bits.bitsmessaging, PID: 15034
                  java.lang.IndexOutOfBoundsException: Invalid index 0, size is 0
                      at java.util.ArrayList.throwIndexOutOfBoundsException(ArrayList.java:255)
                      at java.util.ArrayList.get(ArrayList.java:308)
                      at com.tr4android.recyclerviewslideitem.SwipeAdapter.onBindViewHolder(SwipeAdapter.java:120)
                      at android.support.v7.widget.RecyclerView$Adapter.onBindViewHolder(RecyclerView.java:5825)
                      at android.support.v7.widget.RecyclerView$Adapter.bindViewHolder(RecyclerView.java:5858)
                      at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5094)
                      at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4970)
                      at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2029)
                      at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1414)
                      at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1377)
                      at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:578)
                      at android.support.v7.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:3315)
                      at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:3124)
                      at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:3568)
                      at android.view.View.layout(View.java:15092)
                      at android.view.ViewGroup.layout(ViewGroup.java:4645)
                      at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)
                      at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525)
                      at android.widget.LinearLayout.onLayout(LinearLayout.java:1434)
                      at android.view.View.layout(View.java:15092)
                      at android.view.ViewGroup.layout(ViewGroup.java:4645)
                      at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
                      at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
                      at android.view.View.layout(View.java:15092)
                      at android.view.ViewGroup.layout(ViewGroup.java:4645)
                      at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)
                      at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525)
                      at android.widget.LinearLayout.onLayout(LinearLayout.java:1434)
                      at android.view.View.layout(View.java:15092)
                      at android.view.ViewGroup.layout(ViewGroup.java:4645)
                      at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
                      at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
                      at android.view.View.layout(View.java:15092)
                      at android.view.ViewGroup.layout(ViewGroup.java:4645)
                      at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)
                      at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525)
                      at android.widget.LinearLayout.onLayout(LinearLayout.java:1434)
                      at android.view.View.layout(View.java:15092)
                      at android.view.ViewGroup.layout(ViewGroup.java:4645)
                      at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
                      at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
                      at android.view.View.layout(View.java:15092)
                      at android.view.ViewGroup.layout(ViewGroup.java:4645)
                      at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2007)
                      at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1764)
                      at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1020)
                      at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5762)
                      at android.view.Choreographer$CallbackRecord.run(Choreographer.java:771)
                      at android.view.Choreographer.doCallbacks(Choreographer.java:574)
                      at android.view.Choreographer.doFrame(Choreographer.java:544)
                      at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:757)
                      at android.os.Handler.handleCallback(Handler.java:733)
                      at android.os.Handler.dispatchMessage(Handler.java:95)
                      at android.os.Looper.loop(Looper.java:149)
                      at android.app.ActivityThread.main(ActivityThread.java:5257)
                      at java.lang.reflect.Method.invokeNative(Native Method)
                      at java.lang.reflect.Method.invoke(Method.java:515)
                      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
                    at com.andro
I/Process: Sending signal. PID: 15034 SIG: 9
Application terminated.
taltstidl commented 8 years ago

@ashishoc Please post the adapter code you are using, if possible. Generally, adding an entry to the adapters data should always be accompanied by a call to notifyItemInserted(int position), but I'm not sure whether that solves your case.

ashishoc commented 8 years ago

Activity

  @Override
    public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.contact_main);

    ButterKnife.bind(this);
    this.setMaterialMenu(R.id.toolbar);
    this.disabledTitle();
    pref=new Pref(this);

    ArrayList<ResPartner> dataSet=getData();

    face=Typeface.createFromAsset(getAssets(),"Spinnaker-Regular.ttf");

    //working when I'm passing not empty list
    ContactAdapter adapter=new ContactAdapter(this,recyclerView,dataSet,face);
    recyclerView.setAdapter(adapter);
    //no after http resposne need to add more items
    adapter.mDataset.add(getItemt());
    //IndexOutOfBound exception
  }

Adapter

 public class ContactAdapter extends SwipeAdapter implements View.OnClickListener {
ArrayList<ResPartner> mDataset;
int[] colorsRnd = new int[]{
        R.color.color_red,
        R.color.color_pink,
        R.color.color_purple,
        R.color.color_deep_purple,
        R.color.color_indigo,
        R.color.color_blue,
        R.color.color_light_blue,
        R.color.color_cyan,
        R.color.color_teal,
        R.color.color_green,
        R.color.color_light_green,
        R.color.color_lime,
        R.color.color_yellow,
        R.color.color_amber,
        R.color.color_orange,
        R.color.color_deep_orange,
        R.color.color_brown,
        R.color.color_grey,
        R.color.color_blue_grey};

      private Activity mContext;
     private RecyclerView mRecyclerView;
    private Typeface face;

public ContactAdapter(Activity context, RecyclerView recyclerView, ArrayList<ResPartner> mDataset, Typeface face) {
    mContext = context;
    mRecyclerView = recyclerView;
    this.mDataset = mDataset;
    this.face = face;
    recyclerView.setHasFixedSize(true);
    recyclerView.setLayoutManager(new LinearLayoutManager(context));

}

public class ContactViewHolder extends RecyclerView.ViewHolder {
    LinearLayout contentView;
    ImageView avatarView;
    TextView textView;
    TextView description;

    public ContactViewHolder(View view) {
        super(view);
        contentView = (LinearLayout) view.findViewById(R.id.contentView);
        avatarView = (ImageView) view.findViewById(R.id.avatarView);
        textView = (TextView) view.findViewById(R.id.textView);
        description = (TextView) view.findViewById(R.id.textView_other);
        contentView.setOnClickListener(ContactAdapter.this);
    }
}

@Override
public RecyclerView.ViewHolder onCreateSwipeViewHolder(ViewGroup parent, int i) {
    View v = LayoutInflater.from(parent.getContext())
            .inflate(R.layout.contact_child, parent, true);
    return new ContactViewHolder(v);
}

private int getColor() {
    return colorsRnd[new Random().nextInt(colorsRnd.length)];
}

@Override
public void onBindSwipeViewHolder(RecyclerView.ViewHolder swipeViewHolder, int i) {
    ContactViewHolder sampleViewHolder = (ContactViewHolder) swipeViewHolder;
    ResPartner partner = mDataset.get(i);

    TextDrawable drawable = TextDrawable.builder().buildRound(String.valueOf(partner.name.charAt(0)), getColor());

    sampleViewHolder.avatarView.setImageDrawable(drawable);
    String fStr = partner.function.equals("false") ?
            (partner.phone.equals("false") ?
                    (partner.mobile.equals("false") ?
                            (partner.street.equals("false") ?
                                    (partner.customer ? "Customer" : "Individual") : partner.street) : partner.mobile) : partner.phone) : partner.function;

    String descriptionText = "<b> " + fStr + " </b> " + "<br />" + (partner.email.equals("false") ? "email not available" : partner.email);
    sampleViewHolder.textView.setText(partner.name);
    sampleViewHolder.textView.setTypeface(face);
    sampleViewHolder.description.setTypeface(face);
    sampleViewHolder.description.setText(Html.fromHtml(descriptionText));
}

@Override
public SwipeConfiguration onCreateSwipeConfiguration(Context context, int position) {
    return new SwipeConfiguration.Builder(context)
            .setLeftBackgroundColorResource(R.color.color_delete)
            .setRightBackgroundColorResource(R.color.color_mark)
            .setDrawableResource(R.drawable.ic_action_add_contact)
            .setRightDrawableResource(R.drawable.ic_communication_call)
            .setLeftUndoable(true)
            .setLeftUndoDescription(R.string.add_contact)
            .setDescriptionTextColorResource(android.R.color.white)
            .setLeftSwipeBehaviour(SwipeConfiguration.SwipeBehaviour.NORMAL_SWIPE)
            .setRightSwipeBehaviour(SwipeConfiguration.SwipeBehaviour.RESTRICTED_SWIPE)
            .build();
}

@Override
public void onSwipe(int position, int direction) {
    if (direction == SWIPE_LEFT) {
       mDataset.remove(position);
        notifyItemRemoved(position);

        Toast toast = Toast.makeText(mContext, "Deleted item at position " + position, Toast.LENGTH_SHORT);
        toast.show();
    } else {

        callIntent( position);
    }
}

private void callIntent(int position){
    String phone = mDataset.get(position).phone;
    if (phone.equals("false")) {
        Snackbar.make(mContext.findViewById(R.id.toolbar), "Oops! "+mDataset.get(position).name+" not shared any phone or mobile contact yet.", Snackbar.LENGTH_LONG)
                .show();
    } else {
        Intent intent = new Intent(Intent.ACTION_DIAL, Uri.fromParts("tel", phone, null));
        mContext.startActivity(intent);
    }
}

@Override
public void onClick(View view) {
    // We need to get the parent of the parent to actually have the proper view
    int position = mRecyclerView.getChildAdapterPosition((View) view.getParent().getParent());
    Toast toast = Toast.makeText(mContext, "Clicked item at position " + position, Toast.LENGTH_SHORT);
    toast.show();
}

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

}

ashishoc commented 8 years ago

alternatively is there way to restore normal state while swiping left? Some time need to restore item rather then deletion.

taltstidl commented 8 years ago

@ashishoc Try adding the following after inserting the item, so the code looks like this:

adapter.mDataset.add(getItem());
adapter.notifyItemInserted(adapter.mDataset.size() - 1);

Currently there is no way of programmatically undoing a swipe, and I probably won't add this in the near future due to limited time. Pull Requests are welcome though 😃!

ashishoc commented 8 years ago

Thanks, Btw I came to this solution for now..

 if (direction == SWIPE_LEFT) {
     //restore state 
     notifyItemChanged(position);
}