Drag and drop to remove an item from ListFragment

Hi.

I have tried to implement the examples of Drag and Swipe with RecyclerView, like this article:

My code is extension to fragment:

public class CrimeListFragment extends Fragment {

In the meantime the example extends to ItemTouchHelper.Callback:

public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {

How can I join or use both extensions?

Any idea is welcome.

Thanks

You can only have one super class. You can only extends one class but you can implement as many interfaces as you like.

You could define the SimpleItemTouchHelperCallback as an inner class or as a separate class. I believe that is what they do in the tutorial.

Hi.

Thanks for answering.

I think I already made it. There is a bug out there, but I think it’s the least.

Nice video! Feel free to post the stacktrace here from the crash and I can help you debug.

Thanks

public class ListaFragmentoPedido extends Fragment {

private static final int ADD_ACT_PEDIDO = 0;
private static final int EDIT_ACT_PEDIDO = 1;

private RecyclerView mPedidoRecyclerView;
private PedidoAdapter mAdapter;

@Override
public void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    //ActPedido.AbrirBaseDeDatos();
    setHasOptionsMenu(true);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
    View view = inflater.inflate(R.layout.lista_fragmento_pedido,container,false);

    mPedidoRecyclerView = (RecyclerView) view.findViewById(R.id.pedido_recycler_view);
    mPedidoRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));

    //if (null != savedInstanceState) {mSubtitleVisible = savedInstanceState.getBoolean(SAVED_SUBTITULO_VISIBLE);}

    ItemTouchHelper.Callback callback =  new SimpleItemTouchHelperCallback(mAdapter);
    ItemTouchHelper touchHelper = new ItemTouchHelper(callback);
    touchHelper.attachToRecyclerView(mPedidoRecyclerView);

    actualizarUI();

    return  view;
}
@Override
public void onResume() // al dar modificar los datos dentro del objeto de ListaFragmentoCategoria no se actualizan, para eso esta nota
{
    super.onResume();
    actualizarUI();
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{

    switch (item.getItemId())
    {
        case R.id.menu_item_nuevo_pedido:
        {
            List<Pedido> listaPedidos =  Pedido.obtenerListaPedidos(true);
            Intent intent = ActBuscadorPedido.nuevoIntent(getActivity(),listaPedidos.size(),ADD_ACT_PEDIDO);
            startActivity(intent);
            return true;
        }
        default: return super.onOptionsItemSelected(item);
    }
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)
{
    super.onCreateOptionsMenu(menu, inflater);
    inflater.inflate(R.menu.lista_fragmento_pedido, menu);
}





private class PedidoHolder extends RecyclerView.ViewHolder implements View.OnClickListener
{
    private Pedido mPedido;

    private TextView mTitleTextView;
    private TextView mDateTextView;
    private TextView mCostTextView;

    private Button mButtonEditar;
    public PedidoHolder(View itemView)
    {
        super(itemView);
        itemView.setOnClickListener(this);
        //mTitleTextView = (TextView) itemView;
        mTitleTextView = (TextView) itemView.findViewById(R.id.lista_item_pedido_titulo_text_view);
        mDateTextView = (TextView) itemView.findViewById(R.id.lista_item_pedido_fecha_text_view);
        mCostTextView = (TextView) itemView.findViewById(R.id.lista_item_pedido_total_text_view);
        //mSolvedCheckBox = (CheckBox) itemView.findViewById(R.id.list_item_crime_solved_check_box);

        mButtonEditar = (Button) itemView.findViewById(R.id.lista_item_pedido_editar_categoria);
        //mButtonEliminar = (Button)itemView.findViewById(R.id.lista_item_categoria_eliminar_categoria);

        mButtonEditar.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View v)
            {
                Intent intent = ActBuscadorPedido.nuevoIntent(getActivity(),mPedido.getCodigoPedido(),EDIT_ACT_PEDIDO);
                startActivity(intent);
            }
        });

    }
    public void bindPedido(Pedido pedido)
    {
        mPedido = pedido;
        mTitleTextView.setText("Código: " + String.valueOf(mPedido.getCodigoPedido()));
        mDateTextView.setText(mPedido.getFechaRegistro().toString());
        mCostTextView.setText("Total: " + String.valueOf(mPedido.getSubtotal()));
        //mSolvedCheckBox.setChecked(mCrime.isSolved());
    }
    @Override
    public void onClick(View v)
    {
        /*Intent intent = ActBuscadorPedido.nuevoIntent(getActivity(),mPedido.getCodigoPedido(),EDIT_ACT_PEDIDO);
        startActivity(intent);*/
    }



}
private class PedidoAdapter extends RecyclerView.Adapter<ListaFragmentoPedido.PedidoHolder> implements ItemTouchHelperAdapter
{
    private List<Pedido> mPedidos;
    public PedidoAdapter(List<Pedido> pedidos){mPedidos = pedidos;}
    @Override
    public ListaFragmentoPedido.PedidoHolder onCreateViewHolder(ViewGroup parent, int viewType) // al desplazar llama ha este primero
    {
        LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
        View view = layoutInflater.inflate(R.layout.lista_item_pedido,parent,false);
        return new PedidoHolder(view);
    }
    @Override
    public void onBindViewHolder(ListaFragmentoPedido.PedidoHolder holder, int position){ //finalmente a esta de aquí          {
        Pedido pedido = mPedidos.get(position);
        holder.bindPedido(pedido);

    }

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

    @Override
    public void onItemMove(int fromPosition, int toPosition) {

    }

    @Override
    public void onItemDismiss(int position) {
        mPedidos.remove(position);
        notifyItemRemoved(position);
    }

}

public interface ItemTouchHelperAdapter {

    void onItemMove(int fromPosition, int toPosition);

    void onItemDismiss(int position);
}

private class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {

    private final ItemTouchHelperAdapter mAdapter;

    public SimpleItemTouchHelperCallback(PedidoAdapter adapter) {
        mAdapter = adapter;
    }

    @Override
    public boolean isLongPressDragEnabled() {
        return true;
    }

    @Override
    public boolean isItemViewSwipeEnabled() {
        return true;
    }

    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
        mAdapter.onItemDismiss(viewHolder.getAdapterPosition());
    }

    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
        int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
        return makeMovementFlags(dragFlags, swipeFlags);
    }

    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder,
                          RecyclerView.ViewHolder target) {
        mAdapter.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition());
        return true;
    }



}

private void actualizarUI()
{
    List<Pedido> listaPedidos =  Pedido.obtenerListaPedidos(true);//crimeLab.getCrimes();

    mAdapter = new PedidoAdapter(listaPedidos);
    mPedidoRecyclerView.setAdapter(mAdapter);
    String subtitle = getString(R.string.lblFormatoSubtituloPedidos,listaPedidos.size());

    AppCompatActivity activity = (AppCompatActivity) getActivity();
    activity.getSupportActionBar().setSubtitle(subtitle);

}

}

E/AndroidRuntime: FATAL EXCEPTION: main
Process: net.sicoy.sicoyboxandroid, PID: 23624
java.lang.NullPointerException
at net.sicoy.sicoyboxandroid.visual.pedidos.ListaFragmentoPedido$SimpleItemTouchHelperCallback.onSwiped(ListaFragmentoPedido.java:213)

Line 213 is:

public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
mAdapter.onItemDismiss(viewHolder.getAdapterPosition()); // line 213
}

When you create the SimpleItemTouchHelperCallback, you pass in mAdapter but you haven’t actually created the adapter yet. mAdapter is null at that point.

    actualizarUI();

    ItemTouchHelper.Callback callback =  new SimpleItemTouchHelperCallback(mAdapter);
    ItemTouchHelper touchHelper = new ItemTouchHelper(callback);
    touchHelper.attachToRecyclerView(mPedidoRecyclerView);

Try moving your actualizarUI() method above the ItemTouchHelper code.

Hi.

Thank you very much for the help.

It works correctly, only when a second element is elapsed, an IndexOutOfBoundsException error occurs.

Also I don’t know if there is an event that causes the element that was at the bottom of the deleted element, to rise to occupy the new post.

Because when I deleted an item from the list, there is an empty ready to be occupied by the lower element.

It is probably because when I delete an item, I should again update the list.

Thanks again

I have modified the code as follows:

  @Override
    public void onItemDismiss(int position) {
        mPedidos.remove(position);
        //notifyItemRemoved(position);
        mAdapter.notifyItemRemoved(position);
    }

In this way, when an element is eliminated, the lower element rises to occupy the new post, but remains a ‘phantom’ of itself.

That is, for example, suppose we have the following:

Item 1
Item 2
Item 3

If you delete Item 2, the list is as follows:

Item 1
Item 3
Item 3

It looks like you might have to tell the RecyclerView to refresh all of the items after the one that you removed. I’m surprised by this as I would expect for the RecyclerView to be smart about doing the right thing after you remove an item.

Try using notifyItemRangeChanged after you use notifyItemRemoved:

When changing the code this way:

 @Override
    public void onItemDismiss(int position) {
        mPedidos.remove(position);
        //notifyItemRemoved(position);

        mAdapter.notifyItemRangeChanged(position, getItemCount());
        mAdapter.notifyItemRemoved(position);
    }

When deleting the selected item, it seems that the lower element up, but again returns the deleted item, ie if I delete item 2, the list remains the same:

Item 1
Item 2
Item 3

Looks like the solution in stackoverflow recommends the other order:

notifyItemRemoved(position);
notifyItemRangeChanged(position, getItemCount());

In any position the same thing happens, that’s why I tried changing the order.

Hi.

I see the following line:

  mPedidos.remove (position);

Correctly removes the selected item:

https://scontent.feoh2-1.fna.fbcdn.net/v/t1.0-9/17862307_10155315021112342_2990037806908211326_n.jpg?oh=17698580bd54f696830ea6ffa3d0141f&oe=594E480F

next:

https://scontent.feoh2-1.fna.fbcdn.net/v/t1.0-0/s480x480/17862603_10155315025262342_994640945092906041_n.jpg?oh=f1826aa27f0ed8b9d19386f476ddd6d4&oe=59587ABF

After this is executed onCreateViewHolder, and mPedidos has the amount of 5 elements again:

I think that would be logical if the next portion of the code was run again:

private List<Pedido> mPedidos;
    public PedidoAdapter(List<Pedido> pedidos){
        mPedidos = pedidos;
    }

I have a breakpoint there, but only that portion of code is executed at the beginning of everything, before attempting to eleminate an element.

Could that be the cause?

Thanks

Hi.

I solved it as follows:

 @Override
    public void onItemDismiss(int position) {
       ...
      mPedidos.remove(position);
      mAdapter.notifyItemRemoved(position);

     UpDateListPedidos();
     mPedidoRecyclerView.setAdapter(mAdapter);


}

Where:

 private int UpDateListPedidos() {
    List<Pedido> listaPedidos = Pedido.obtenerListaPedidos(true);
    mAdapter = new PedidoAdapter(listaPedidos);
    return listaPedidos.size();
}