Como implementar ‘Swipe for Options’ no RecyclerView

Primeiro, vamos criar uma lista normal do RecyclerView

RecyclerView é uma versão avançada e flexível do ListView e GridView. Ele é capaz de armazenar grandes quantidades de dados da lista e tem melhor desempenho que seus antecessores.

Como o nome sugere, o RecyclerView ‘recicla’ os itens da nossa lista quando fica fora de vista na rolagem e os preenche novamente quando eles retornam à exibição. Portanto, o contêiner da lista deve manter apenas um número limitado de visualizações e não a lista inteira.

É tão flexível que o novo ViewPager2 A classe, usada para criar guias passíveis de furto, é escrita sobre o RecyclerView.

Crie um POJO (Objeto Java Antigo Simples) para armazenar os dados da lista

public class RecyclerEntity {
    private String title;
    private boolean showMenu = false;
    private int image;

    public RecyclerEntity() {
    }

    public RecyclerEntity(String title, int image, boolean showMenu) {
        this.title = title;
        this.showMenu = showMenu;
        this.image = image;
    }

    public int getImage() {
        return image;
    }

    public void setImage(int image) {
        this.image = image;
    }
    
    //... all the getters and setters
}

Observe que temos um membro showMenu aqui que tratará da visibilidade do menu para esse item da lista em nosso RecyclerView.

Criar um adaptador RecyclerView

public class RecyclerAdapter extends RecyclerView.Adapter {
    List list;
    Context context;

    public RecyclerAdapter(Context context, List articlesList) {
        this.list = articlesList;
        this.context = context;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v;
            v= LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_list, parent, false);
            return new MyViewHolder(v);
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
        RecyclerEntity entity = list.get(position);
        if(holder instanceof MyViewHolder){
            ((MyViewHolder)holder).title.setText(entity.getTitle());
            ((MyViewHolder)holder).imageView.setImageDrawable(context.getResources().getDrawable(entity.getImage()));   
        }
    }

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

    public class MyViewHolder extends RecyclerView.ViewHolder {
        TextView title;
        ImageView imageView;
        ConstraintLayout container;

        public MyViewHolder(View itemView) {
            super(itemView);
            title = itemView.findViewById(R.id.title);
            imageView = itemView.findViewById(R.id.imageView);
            container = itemView.findViewById(R.id.container);
        }
    }
}

Normalmente, colocamos nossa subclasse ViewHolder (MyViewHolder) no modelo de superclasse. Isso nos permite retornar diretamente nosso objeto de subclasse ViewHolder definido a partir do método onCreateViewHolder (). Então não precisamos convertê-lo repetidamente no método onBindViewHolder ().

Mas aqui não podemos fazer isso, e aprenderemos o porquê em um minuto.

Inicialize o RecyclerView na atividade

public class MainActivity extends AppCompatActivity {
    RecyclerView recyclerView;
    List list;
    RecyclerAdapter adapter;


    @RequiresApi(api = Build.VERSION_CODES.M)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        recyclerView = findViewById(R.id.recyclerview);
        list = new ArrayList<>();

        list.add(new RecyclerEntity("This is the best title", R.drawable.one, false));
        list.add(new RecyclerEntity("This is the second-best title", R.drawable.two, false));
		//... rest of the list items
        
        adapter = new RecyclerAdapter(this, list);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.setAdapter(adapter);
    }
}

Agora vamos começar a tornar as coisas um pouco mais interessantes.

E inicialize-o no Recycler Adapter:

public class RecyclerAdapter extends RecyclerView.Adapter {
    List list;
    Context context;
    private final int SHOW_MENU = 1;
    private final int HIDE_MENU = 2;

    public RecyclerAdapter(Context context, List articlesList) {
        this.list = articlesList;
        this.context = context;
    }

    @Override
    public int getItemViewType(int position) {
        if(list.get(position).isShowMenu()){
            return SHOW_MENU;
        }else{
            return HIDE_MENU;
        }
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v;
        if(viewType==SHOW_MENU){
            v= LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_menu, parent, false);
            return new MenuViewHolder(v);
        }else{
            v= LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_list, parent, false);
            return new MyViewHolder(v);
        }

    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
        RecyclerEntity entity = list.get(position);
        if(holder instanceof MyViewHolder){
        	//... same as above
        }
        
        if(holder instanceof MenuViewHolder){
            //Menu Actions
        }

    }

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

    public class MyViewHolder extends RecyclerView.ViewHolder {
        //... same as above
    }
    //Our menu view
    public class MenuViewHolder extends RecyclerView.ViewHolder{
        public MenuViewHolder(View view){
            super(view);
        }
    }
}

Agora, temos duas subclasses do ViewHolder em nosso adaptador, MyViewHolder (o item da lista real) e MenuViewHolder. Ambos herdam a mesma classe, então retornamos a classe pai RecyclerView.ViewHolder de onCreateViewHolder ().

Nosso método getItemViewType () retorna a variável int (viewType), que informa o tipo de exibição que queremos mostrar em nosso RecyclerView para uma posição específica: ou seja, MyViewHolder ou MenuViewHolder.

Essa variável viewType é usada por onCreateViewHolder () que realmente retorna o respectivo objeto ViewHolder.

public void showMenu(int position) {
        for(int i=0; i

Observe que existem muitas maneiras de lidar com isso. Mas, por uma questão de simplicidade, mantemos um valor booleano em nosso POJO para manter a visibilidade do menu.

Após alterar nossa lista de dados, chamamos o método notifyDataSetChanged () para redesenhar a lista.

@Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
        RecyclerEntity entity = list.get(position);
        if(holder instanceof MyViewHolder){
            ((MyViewHolder)holder).title.setText(entity.getTitle());
            ((MyViewHolder)holder).imageView.setImageDrawable(context.getResources().getDrawable(entity.getImage()));

            ((MyViewHolder)holder).container.setOnLongClickListener(new View.OnLongClickListener() {
                @Override
                public boolean onLongClick(View v) {
                    showMenu(position);
                    return true;
                }
            });
        }
        if(holder instanceof MenuViewHolder){
            //Set Menu Actions like:
            //((MenuViewHolder)holder).edit.setOnClickListener(null);
        }

    }

Novamente, definir eventos em nossas visões também pode ser feito de várias maneiras.

No nosso exemplo, temos três ações em nosso menu. Você pode escrever sua lógica para lidar com essas ações na segunda instrução if, como mostrado nos comentários.

Para fazer isso, adicionamos um auxiliar de toque em nosso MainActivity.java:

public class MainActivity extends AppCompatActivity {
    RecyclerView recyclerView;
    List list;
    RecyclerAdapter adapter;


    @RequiresApi(api = Build.VERSION_CODES.M)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        //... same as above 
         
        adapter = new RecyclerAdapter(this, list);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.setAdapter(adapter);

        ItemTouchHelper.SimpleCallback touchHelperCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) {
            private final ColorDrawable background = new ColorDrawable(getResources().getColor(R.color.background));

            @Override
            public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
                return false;
            }

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

            @Override
            public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
                super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);

                View itemView = viewHolder.itemView;

                if (dX > 0) {
                    background.setBounds(itemView.getLeft(), itemView.getTop(), itemView.getLeft() + ((int) dX), itemView.getBottom());
                } else if (dX < 0) {
                    background.setBounds(itemView.getRight() + ((int) dX), itemView.getTop(), itemView.getRight(), itemView.getBottom());
                } else {
                    background.setBounds(0, 0, 0, 0);
                }

                background.draw(c);
            }
        };
        ItemTouchHelper itemTouchHelper = new ItemTouchHelper(touchHelperCallback);
        itemTouchHelper.attachToRecyclerView(recyclerView);

    }

Chamamos a função showMenu () dentro do nosso adaptador quando um item da lista é passado.

A função onChildDraw () desenha o plano de fundo enquanto deslizamos. Caso contrário, haverá um fundo branco ao deslizar e o layout do menu será exibido com um pop.

Existem três maneiras de ocultar o nosso menu.

  1. Ocultando o menu quando outra linha é passada:

Este caso já é tratado no método showMenu () em nosso adaptador. Antes de mostrar o menu para qualquer linha, ligamos primeiro setShowMenu (false) para todas as linhas ocultarem o menu.

2. Ocultando o menu quando o botão Voltar é pressionado (em nossa Atividade):

@Override
    public void onBackPressed() {
        if (adapter.isMenuShown()) {
            adapter.closeMenu();
        } else {
            super.onBackPressed();
        }
    }

3. Ocultando o menu quando um usuário rola a lista:

recyclerView.setOnScrollChangeListener(new View.OnScrollChangeListener() {
            @Override
            public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
                adapter.closeMenu();
            }
        });

Embora o pocket tenha apenas uma ação de pressão longa para mostrar o menu, neste exemplo, adicionamos deslize o dedo para mostrar o menu para obter funcionalidades adicionais. Você pode ocultar o item de menu ao deslizar para a direita / esquerda novamente, mas acho que isso pode confundir o usuário.

Empacotando

Se seu aplicativo tiver um conjunto de dados muito grande para mostrar em um RecyclerView, esse tipo de UX pode não ser o caminho a seguir. Nesse caso, você deve ter um tipo de funcionalidade de edição em massa.

Ademais, se suas opções de edição forem mais do que as que você pode ajustar em uma linha do RecyclerView, mas você ainda desejar mostrar algumas ações rápidas, poderá exibir uma caixa de diálogo Bottomsheet pressionando longamente o item e ele pode ter todas as suas opções de edição. o Google Drive O aplicativo Android faz exatamente a mesma coisa.

Se você deseja implementar um furto simples para excluir a função, o código para isso pode ser encontrado aqui no Github.

Você também pode verificar o código fonte para este projeto no Github.

Visita 22Boxes.com para obter mais recursos de desenvolvimento para dispositivos móveis e Web.