Desative Swipe para posição em RecyclerView usando ItemTouchHelper.SimpleCallback

93

Estou usando o recyclerview 22.2.0 e a classe auxiliar ItemTouchHelper.SimpleCallback para habilitar a opção deslizar para dispensar em minha lista. Mas como tenho um tipo de cabeçalho, preciso desabilitar o comportamento de deslizar para a primeira posição do adaptador. Como RecyclerView.Adapter não possui o método isEnabled () , tentei desabilitar a interação da view através dos métodos isEnabled () e isFocusable () na própria criação do ViewHolder, mas não tive sucesso. Tentei ajustar o limite de furto para um valor total, como 0f ot 1f no método getSwipeThreshold () do SimpleCallback , mas também não tive sucesso.

Alguns fragmentos do meu código para ajudá-lo a me ajudar.

Minha atividade:

@Override
protected void onCreate(Bundle bundle) {
    //... initialization
    ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0,
            ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {

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

        @Override
        public float getSwipeThreshold(RecyclerView.ViewHolder viewHolder) {
            if (viewHolder instanceof CartAdapter.MyViewHolder) return 1f;
            return super.getSwipeThreshold(viewHolder);
        }

        @Override
        public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {

        }
    };

    ItemTouchHelper itemTouchHelper = new ItemTouchHelper(simpleItemTouchCallback);
    itemTouchHelper.attachToRecyclerView(recyclerView);
}

E eu tenho um adaptador comum com dois tipos de visualização. No ViewHolder que desejo desabilitar o deslizamento, fiz:

public static class MyViewHolder extends RecyclerView.ViewHolder {
    public ViewGroup mContainer;

    public MyViewHolder(View v) {
        super(v);
        v.setFocusable(false);
        v.setEnabled(false);
        mContainer = (ViewGroup) v.findViewById(R.id.container);      
    }
}
Rafael Toledo
fonte

Respostas:

204

Depois de brincar um pouco, consegui dizer que SimpleCallback tem um método chamado getSwipeDirs () . Como tenho um ViewHolder específico para a posição não deslizável , posso fazer uso de instanceof para evitar o deslizamento. Se não for o seu caso, você pode realizar este controle usando a posição do ViewHolder no adaptador.

Java

@Override
public int getSwipeDirs(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
    if (viewHolder instanceof CartAdapter.MyViewHolder) return 0;
    return super.getSwipeDirs(recyclerView, viewHolder);
}

Kotlin

override fun getSwipeDirs (recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {
    if (viewHolder is CartAdapter.MyViewHolder) return 0
    return super.getSwipeDirs(recyclerView, viewHolder)
}
Rafael Toledo
fonte
1
Exatamente o que eu estava procurando! Especialmente quando este método não aparece na documentação oficial ...
ptitvinou
1
@ptitvinou está presente em SimpleCallback developer.android.com/intl/pt-br/reference/android/support/v7/…
Rafael Toledo
2
Vale a pena mencionar: se você quiser permitir apenas uma direção em certos itens, use return position == 0 ? ItemTouchHelper.LEFT : super.getSwipeDirs(recyclerView, viewHolder);- este, isto é, permite apenas deslizar para a esquerda.
Jean d'arme
Perfeito. Trabalhando como um encanto
Rei das Missas,
Como é que isso funciona? O que devo fazer se quiser desativar o deslizar para a direita, por exemplo?
Ali Bdeir
66

Se alguém estiver usando ItemTouchHelper.Callback . Em seguida, você pode remover quaisquer sinalizadores relacionados na função getMovementFlags (..) .

@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);
}

Aqui, em vez de dragFlags e swipeFlags, você pode passar 0 para desativar o recurso correspondente.

ItemTouchHelper.START significa deslizar da esquerda para a direita no caso de localidade da esquerda para a direita (suporte de aplicativo LTR), mas o contrário em localidade direita para esquerda (suporte de aplicativo RTL). ItemTouchHelper.END significa deslizar na direção oposta de START.

então você pode remover qualquer sinalizador de acordo com seus requisitos.

Muhammad Adil
fonte
Obrigado!, Eu uso o código: @Override public int getMovementFlags (RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN; // movimentos arrastar return makeFlag (ItemTouchHelper.ACTION_STATE_DRAG, dragFlags); // como parâmetro, ação de arrastar e sinalizadores de arrastar}
Faça Xuan Nguyen
Esta é a resposta preferida se (ao contrário do OP) você estiver estendendo ItemTouchHelper.Callbackdiretamente.
tir38
24

Esta é uma maneira simples de fazer isso que depende apenas da posição do item que está sendo deslizado:

@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder holder) {
    int position = holder.getAdapterPosition();
    int dragFlags = 0; // whatever your dragFlags need to be
    int swipeFlags = createSwipeFlags(position)

    return makeMovementFlags(dragFlags, swipeFlags);
}

private int createSwipeFlags(int position) {
  return position == 0 ? 0 : ItemTouchHelper.START | ItemTouchHelper.END;
}

Isso também deve funcionar se você estiver usando SimpleCallback:

@Override
public int getSwipeDirs(RecyclerView recyclerView, RecyclerView.ViewHolder holder) {
    int position = holder.getAdapterPosition();
    return createSwipeFlags(position);
}

private int createSwipeFlags(int position) {
  return position == 0 ? 0 : super.getSwipeDirs(recyclerView, viewHolder);
}

Se você deseja desabilitar a passagem condicional aos dados no item, use o positionvalor para obter dados do adaptador para o item que está sendo deslizado e desabilite de acordo.

Se você já tem tipos de suporte específicos que não precisam ser deslizados, a resposta aceita funcionará. No entanto, criar tipos de titular como um proxy para a posição é uma confusão e deve ser evitado.

Robert Liberatore
fonte
6

Existem algumas maneiras de fazer isso, mas se você tiver apenas um ViewHolder, mas mais de um layout, você pode usar essa abordagem.

Substitua getItemViewType e dê a ele alguma lógica para determinar o tipo de visualização com base na posição ou tipo de dados no objeto (eu tenho uma função getType em meu objeto)

@Override
public int getItemViewType(int position) {
    return data.get(position).getType;
}

Retorne o layout adequado em onCreateView com base em ViewType (certifique-se de passar o tipo de visualização para a classe ViewHolder.

@Override
public AppListItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    mContext = parent.getContext();

    if (viewType == 0){
        return new AppListItemHolder(LayoutInflater.from(mContext).inflate(R.layout.layout, parent, false), viewType);
    else
        return new AppListItemHolder(LayoutInflater.from(mContext).inflate(R.layout.header, parent, false), viewType);
    }
}

Obtenha as visualizações de conteúdo dos diferentes layouts com base no tipo de visualização public static class AppListItemHolder extends RecyclerView.ViewHolder {

    public AppListItemHolder (View v, int viewType) {
        super(v);

        if (viewType == 0)
            ... get your views contents
        else
            ... get other views contents
        }
    }
}

E então, em seu ItemTouchHelper, altere as ações com base em ViewType. Para mim, isso desativa a passagem de um cabeçalho de seção RecyclerView

@Override
public int getSwipeDirs(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
    if (viewHolder.getItemViewType() == 1) return 0;
        return super.getSwipeDirs(recyclerView, viewHolder);
    }
}

fonte
Boa ideia, mas getItemViewType é final
tim
3

Primeiro, em recyclerView no método onCreateViewHolder, defina a tag para cada tipo de viewHolder, como o código abaixo:

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
    if(ITEM_TYPE_NORMAL == viewType) {
        View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.deposite_card_view, viewGroup, false);
        ItemViewHolder holder = new ItemViewHolder(context, v);
        holder.itemView.setTag("normal");
        return holder;
    } else {
        View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.card_header, viewGroup, false);
        HeaderViewHolder holder = new HeaderViewHolder(context, v);
        holder.itemView.setTag("header");
        return holder;
    }
} 

em seguida, na implementação ItemTouchHelper.Callback, atualize o método getMovementFlags, como abaixo:

public class SwipeController extends ItemTouchHelper.Callback {

@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
    if("normal".equalsIgnoreCase((String) viewHolder.itemView.getTag())) {
        return makeMovementFlags(0, LEFT | RIGHT);
    } else {
        return 0;
    }
}

no final, anexe a recyclerView:

final SwipeController swipeController = new SwipeController();

    ItemTouchHelper itemTouchHelper = new ItemTouchHelper(swipeController);
    itemTouchHelper.attachToRecyclerView(recyclerView);
M.Kouchi
fonte
2

Além da resposta do @Rafael Toledo, se você deseja remover o gesto específico de deslizar como deslizar para a ESQUERDA ou para a DIREITA com base na posição ou tipo de dados no objeto no Adaptador, você pode fazer assim.

@Override
  public int getSwipeDirs(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder)
       {
          int position = viewHolder.getAdapterPosition();
          ConversationDataModel conversationModel = null;
          conversationModel = mHomeAdapter.getItems().get(position);
          boolean activeChat = true;
          if(conversationModel!=null && !conversationModel.isActive())
           {
             activeChat = false;
           }
  return activeChat ? super.getSwipeDirs(recyclerView, viewHolder): ItemTouchHelper.RIGHT;
        }

Aqui no meu caso, no meu Recyclerview estou carregando as conversas do chat e tem gestos de deslizar RIGHT (para ARCHIVE) e LEFT (para SAIR) em cada item. Mas, caso a conversa não esteja ativa, preciso desabilitar o deslizar para a DIREITA para o item específico na visualização do Reciclador.

Portanto, estou verificando o activeChate, portanto, desativei o deslizar para a DIREITA.

Rei das missas
fonte
2

Pode ser desativado usando ItemTouchHelper.ACTION_STATE_IDLE

ItemTouchHelper.SimpleCallback(
    ItemTouchHelper.UP or ItemTouchHelper.DOWN, //drag directions
    ItemTouchHelper.ACTION_STATE_IDLE //swipe directions
)
val touchHelperCallback = object : ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP or ItemTouchHelper.DOWN, ItemTouchHelper.ACTION_STATE_IDLE) {
    override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
        (recyclerView.adapter as MyAdapter).onItemMove(viewHolder.adapterPosition, target.adapterPosition)
        return true
    }

    override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
        return
    }

}
val touchHelper = ItemTouchHelper(touchHelperCallback)
touchHelper.attachToRecyclerView(recyclerViewX)
Pedro Romão
fonte