Nonaktifkan Gesek untuk posisi di RecyclerView menggunakan ItemTouchHelper.SimpleCallback

92

Saya menggunakan recyclerview 22.2.0 dan kelas pembantu ItemTouchHelper.SimpleCallback untuk mengaktifkan opsi geser-untuk-menutup ke daftar saya. Tetapi karena saya memiliki jenis tajuk di atasnya, saya perlu menonaktifkan perilaku gesek untuk posisi pertama adaptor. Karena RecyclerView.Adapter tidak memiliki metode isEnabled () , saya mencoba menonaktifkan interaksi tampilan melalui metode isEnabled () dan isFocusable () dalam pembuatan ViewHolder itu sendiri, tetapi tidak berhasil. Saya mencoba menyesuaikan ambang geser ke nilai penuh, seperti 0f ot 1f dalam metode getSwipeThreshold () pada metode SimpleCallback , tetapi tidak berhasil juga.

Beberapa potongan kode saya untuk membantu Anda membantu saya.

Aktivitas Saya:

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

Dan saya memiliki adaptor umum dengan dua jenis tampilan. Di ViewHolder yang ingin saya nonaktifkan swiping, saya lakukan:

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
sumber

Jawaban:

204

Setelah bermain sedikit, saya berhasil mengatur bahwa SimpleCallback memiliki metode yang disebut getSwipeDirs () . Karena saya memiliki ViewHolder khusus untuk posisi yang tidak dapat digesek , saya dapat menggunakan instanceof untuk menghindari gesekan. Jika bukan itu masalahnya, Anda dapat melakukan kontrol ini menggunakan posisi ViewHolder di Adaptor.

Jawa

@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
sumber
1
Persis apa yang saya cari! Terutama ketika metode ini tidak muncul di dokumentasi resmi ...
ptitvinou
1
@ptitvinou hadir di SimpleCallback developer.android.com/intl/pt-br/reference/android/support/v7/…
Rafael Toledo
2
Perlu disebutkan: jika Anda hanya ingin mengizinkan satu arah pada item tertentu, gunakan return position == 0 ? ItemTouchHelper.LEFT : super.getSwipeDirs(recyclerView, viewHolder);- yang satu ini hanya mengizinkan untuk gesekan ke kiri.
jean d'arme
Sempurna. Bekerja seperti pesona
King of Masses
Bagaimana cara kerjanya? Apa yang harus saya lakukan jika saya ingin menonaktifkan swiping right misalnya?
Ab
66

Jika seseorang menggunakan ItemTouchHelper.Callback . Kemudian Anda dapat menghapus semua flag terkait di fungsi 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);
}

Di sini alih-alih dragFlags dan swipeFlags Anda dapat meneruskan 0 untuk menonaktifkan fitur yang sesuai.

ItemTouchHelper.START berarti menggesek dari kiri ke kanan dalam kasus lokal kiri ke kanan (Dukungan aplikasi LTR), tetapi sebaliknya dalam lokal kanan ke kiri (Dukungan aplikasi RTL). ItemTouchHelper.END berarti menggesek ke arah yang berlawanan START.

sehingga Anda dapat menghapus bendera apa pun sesuai dengan kebutuhan Anda.

Muhammad Adil
sumber
Terima!, Saya menggunakan kode: @Override public int getMovementFlags (RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN; // gerakan tarik kembali makeFlag (ItemTouchHelper.ACTION_STATE_DRAG, dragFlags); // sebagai parameter, seret aksi dan seret bendera}
Lakukan Xuan Nguyen
Ini adalah jawaban yang lebih disukai jika (tidak seperti OP) Anda memperluas ItemTouchHelper.Callbacksecara langsung.
tir38
23

Berikut cara sederhana untuk melakukan ini yang hanya bergantung pada posisi item yang digesek:

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

Ini juga harus berfungsi jika Anda menggunakan 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);
}

Jika Anda ingin menonaktifkan swiping conditional pada data dalam item, gunakan positionnilai untuk mendapatkan data dari adaptor untuk item yang sedang digesek dan menonaktifkan yang sesuai.

Jika Anda sudah memiliki tipe pemegang tertentu yang tidak perlu digeser, jawaban yang diterima akan berfungsi. Namun, membuat tipe pemegang sebagai proxy untuk posisi adalah sebuah kekeliruan dan harus dihindari.

Robert Liberatore
sumber
6

Ada beberapa cara untuk melakukannya, tetapi jika Anda hanya memiliki satu ViewHolder, tetapi lebih dari satu tata letak, Anda dapat menggunakan pendekatan ini.

Ganti getItemViewType dan berikan beberapa logika untuk menentukan jenis tampilan berdasarkan posisi atau jenis data dalam objek (Saya memiliki fungsi getType di objek saya)

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

Kembalikan tata letak yang tepat di onCreateView berdasarkan ViewType (Pastikan untuk meneruskan tipe tampilan ke kelas 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);
    }
}

Dapatkan tampilan konten dari tata letak yang berbeda berdasarkan jenis tampilan kelas statis publik AppListItemHolder extends RecyclerView.ViewHolder {

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

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

Dan kemudian di ItemTouchHelper Anda mengubah tindakan berdasarkan ViewType. Bagi saya ini menonaktifkan menggesek header bagian RecyclerView

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

sumber
Ide bagus tapi getItemViewType sudah final
tim
3

Pertama di recyclerView at metode onCreateViewHolder, setel tag untuk setiap jenis viewHolder, seperti kode di bawah ini:

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

kemudian dalam implementasi ItemTouchHelper.Callback, perbarui metode getMovementFlags, seperti di bawah ini:

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

pada akhirnya lampirkan ke recyclerView:

final SwipeController swipeController = new SwipeController();

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

Selain jawaban @Rafael Toledo, Jika Anda ingin menghapus gerakan gesek tertentu seperti gesek KIRI atau Geser KANAN berdasarkan posisi atau jenis data dalam objek di Adaptor, maka Anda dapat melakukan seperti ini.

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

Di sini, dalam kasus saya, Dalam Recyclerview saya, saya memuat percakapan obrolan dan memiliki gerakan geser KANAN (untuk ARCHIVE) & KIRI (untuk KELUAR) pada setiap item. Tetapi jika percakapan tidak aktif, maka saya perlu menonaktifkan gesekan KANAN untuk item tertentu di tampilan Recycler.

Jadi saya memeriksa activeChatdan yang sesuai dan menonaktifkan gesekan KANAN.

King of Masses
sumber
2

Itu dapat dinonaktifkan menggunakan 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
sumber