آموزش حذف آیتم های recyclerview با عمل کشیدن یا swipe کردن در اندروید استودیو با کاتلین

در این مقاله ی آموزشی قصد داریم به شما یاد بدیم چطور روی آیتم های یک recyclerview عمل swipe به چپ یا راست را انجام دهید و بعد از swipe کردن کاربر آنرا حذف کنید . درست مثل اپلیکیشن اندرویدی GMail . 

در این مقاله یاد میگیرید چطور یک آیتم را هنگام swipe پاک کنید و بعد از پاکسازی Undo هم برای لغو حذف نمایش دهید . 

از نظر UX برای کاربران اپلیکیشن ها ، swipe کردن روی آیتم ها برای حذف کردن آنها بسیار راحت و دلچسب تر خواهد بود . 

شما میتوانید از این آموزش برای تغییرات شخصی خودتان نیز به سادگی استفاده کنید . 

ابتدا نتیجه ی نهایی این آموزش را بصورت ویدیویی در زیر مشاهده کنیم :‌

 

خب بیایید شروع کنیم 

 

قدم 1 - پروژه جدید کاتلین بسازید

در محیط اندروید استودیو خودتان یک پروژه جدید با زبان kotlin بسازید و یک اکتیویتی empty جدیدی بسازید . و یادتون نره موقع ساختن پروژه کاتلین رو بعنوان زبان برنامه نویسی انتخاب کنید . 

حالا فایل  build.gradle(Module: app) را باز کنید و کدهای زیر را بهش اضافه کنید 

implementation 'com.android.support:recyclerview-v7:28.0.0'
implementation 'com.android.support:cardview-v7:28.0.0'
implementation 'com.android.support:design:28.0.0'

البته اگر قصد دارید از کتابخانه جدیدتر اینها استفاده کنید از AndroidX استفاده کنید که نحوه ی افزودن کتابخانه ها با androidx را به راحتی میتونید گوگل کنید . 

خب برای اضافه کردن recyclerview و cardview و material desing به پروژمون ما نیاز داریم سه کتابخانه بالا را به گردل اضافه کنیم بعد اتصال به اینترنت با دور زدن ip ایران بخاطر تحریم ها نیاز است و سپس sync کنید تا کتابخانه های بالا دانلود و به پروژتون اضافه بشن . 

 

قدم 2 - اضافه کردن تصویر حذف 

خب یک فایل png تصویری پیدا کنید که تصویر سطل آشغال باشه برای پیدا کردن چنین تصویری مستقیما میتوانید در گوگل عبارت زیر را کپی و سرچ کنید ( البته بخش تصاویر ) :‌

trash icon filetype:png

و بعد از پیدا کردن تصویر آنرا در مسیر app->res->drawable کپی کنید . 

آموزش حذف کردن آیتم های ریسایکلرویو یا recyclerview با کشیدن یا درگ کردن یا swipe کردن - پاک کردن ایتم های recyclerview

قدم 3 - اضافه کردن فایل XML و کلاس مدل

خب در مسیر app->res->layout یک فایل xml جدید بسازید و نام آنرا rv_item.xml قرار دهید . 

فایل rv_item.xml باید محتوای زیر را داشته باشد :‌

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingBottom="5dp"
    android:paddingLeft="5dp"
    android:paddingRight="5dp"
    tools:context=".MainActivity"
    tools:ignore="NamespaceTypo">
 
    <android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
        android:id="@+id/card_view"
        android:layout_width="match_parent"
        android:layout_height="90dp"
        android:layout_gravity="center"
        app:cardBackgroundColor="#ffffff"
        android:layout_marginTop="0dp"
        card_view:cardCornerRadius="1dp">
 
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">
 
            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:id="@+id/tv"
                android:height="90dp"
                android:gravity="center"
                android:paddingLeft="10dp"
                android:text="Image"
                android:textColor="#000"
                android:textStyle="bold"
                android:textSize="18sp" />
 
        </LinearLayout>
 
    </android.support.v7.widget.CardView>
</RelativeLayout>

خب همانطور که در کدهای بالا میبینید این کدها یک textview را داخل یک cardview میسازد پس هر ردیف recyclerview یک عدد cardview خواهد داشت که درونش یک textview وجود دارد . 

حالا یک فایل کاتلین جدید بسازید و اسمش رو SwipeModel.kt بزارید . 

محتویات  SwipeModel.kt باید به شکل زیر باشه : 

class Model {
 
    var name: String? = null
 
 
    fun getNames(): String {
        return name.toString()
    }
 
    fun setNames(name: String) {
        this.name = name
    }
}

در کدهای بالا یک متغیر از نوع رشته ای یا string تعریف کردیم که قراره نام ماشین را نگهداری بکنه و روی recyclerview به نمایش بذاره 

برای این متغیر دو متد کمک کننده ی Setter و Getter نوشتیم که دریافت و ارسال اطلاعات را راحت میکنه . 

 

قدم 4 - آداپتر برای swipe 

یک فایل کلاس از نوع کاتلین بسازید و اسم انرا SwipeAdapter.kt انتخاب کنید . 

محتویات  SwipeAdapter.kt  باید به شکل زیر باشد :‌

import android.content.Context
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import java.util.ArrayList
 
/**
 * Created by avasam.ir in 2019
 */
class SwipeAdapter(ctx: Context, private val imageModelArrayList: ArrayList<Model>) :
    RecyclerView.Adapter<SwipeAdapter.MyViewHolder>() {
 
    private val inflater: LayoutInflater
 
 
    init {
 
        inflater = LayoutInflater.from(ctx)
    }
 
    fun removeItem(position: Int) {
        imageModelArrayList.removeAt(position)
        notifyItemRemoved(position)
        notifyItemRangeChanged(position, imageModelArrayList.size)
    }
 
    fun restoreItem(model: Model, position: Int) {
        imageModelArrayList.add(position, model)
        // notify item added by position
        notifyItemInserted(position)
    }
 
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
 
        val view = inflater.inflate(R.layout.rv_item, parent, false)
 
        return MyViewHolder(view)
    }
 
    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
 
        holder.time.setText(imageModelArrayList[position].getNames())
    }
 
    override fun getItemCount(): Int {
        return imageModelArrayList.size
    }
 
     inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
 
        var time: TextView
 
 
        init {
 
            time = itemView.findViewById(R.id.tv) as TextView
 
        }
 
    }
}

 

در تابع onCreateViewHolder() ما rv_item.xml را پر میکنیم تا ویو مربوط به هر ردیف ساخته شود . 

سپس در تابع onBindViewHolder() نام ماشین را در textview ست میکنیم ، برای این از imageModelArrayList بعنوان منبع دیتا استفاده کرده ایم . 

imageModelArrayList یک arraylist است که درونش از آبجکت های SwipeModel پر میشود که آداپتر آنرا از پارامترها میگیرد . 

خب حالا به کد زیر نگاه کنید :‌

 fun removeItem(position: Int) {
        imageModelArrayList.removeAt(position)
        notifyItemRemoved(position)
        notifyItemRangeChanged(position, imageModelArrayList.size)
    }

این تابع زمانی اجرا میشود که کاربر به سمت راست یا چپ عمل swipe را انجام دهد و سپس دکمه ی undo را نزند . 

کامپایلر آیتمی که روی آن عمل swipe اتفاق افتاده است را پاکسازی خواهد کرد . 

سپس متد notifyItemRemoved()  فراخوانی میشود و سپس متد notifyItemRangeChanged() ریسایکلر ویو را ریفرش میکند . 

حالا به تابع زیر نگاه کنید : 

 fun restoreItem(model: Model, position: Int) {
        imageModelArrayList.add(position, model)
        // notify item added by position
        notifyItemInserted(position)
    }

 

اگر کاربر دکمه ی undo را کلیک کند قبل از اینکه آیتم حذف شود کامپایلر متد زیر را فراخوانی میکند . 

ابتدا آیتم حذف شده را دوباره به سرجایش برمیگرداند و سپس متد notifyItemInserted() را فراخوانی میکند تا ریسایکلر ویو ریفرش شود . 

 

قدم 5 - تغییرات اکتیویتی اصلی برنامه 

خب ما دو فایل در اکتیویتی اصلی داریم یکی فایل کاتلین و یکی فایل لیوت آن بنام activity_main.xml  که کدش در زیر است :‌

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/linear"
        android:background="#e4e0e0"
        tools:context=".MainActivity">
 
    <android.support.v7.widget.RecyclerView
            android:id="@+id/recycler"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginTop="10dp"/>
 
</android.support.design.widget.CoordinatorLayout>

خب همانطور که میبینید چیز خاصی ندارد و فقط یک recyclerview به آن اضافه شده است 

کد های زیر هم برای MainActivity.kt است :‌

import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Color
import android.graphics.Paint
import android.graphics.RectF
import android.support.design.widget.Snackbar
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.graphics.Canvas
import java.util.ArrayList
import android.support.v7.widget.helper.ItemTouchHelper
 
class MainActivity : AppCompatActivity() {
 
    private var recyclerView: RecyclerView? = null
    private var imageModelArrayList: ArrayList<Model>? = null
    private var adapter: SwipeAdapter? = null
    private val p = Paint()
    private val myImageNameList = arrayOf("Benz", "Bike", "Car", "Carrera", "Ferrari", "Harly", "Lamborghini", "Silver")
 
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        recyclerView = findViewById(R.id.recycler) as RecyclerView
 
        imageModelArrayList = populateList()
 
        adapter = SwipeAdapter(this, imageModelArrayList!!)
        recyclerView!!.adapter = adapter
        recyclerView!!.layoutManager = LinearLayoutManager(applicationContext, LinearLayoutManager.VERTICAL, false)
 
        enableSwipe()
    }
 
    private fun enableSwipe() {
        val simpleItemTouchCallback =
            object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) {
 
                override fun onMove(
                    recyclerView: RecyclerView,
                    viewHolder: RecyclerView.ViewHolder,
                    target: RecyclerView.ViewHolder
                ): Boolean {
                    return false
                }
 
                override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
                    val position = viewHolder.adapterPosition
 
                    if (direction == ItemTouchHelper.LEFT) {
                        val deletedModel = imageModelArrayList!![position]
                        adapter!!.removeItem(position)
                        // showing snack bar with Undo option
                        val snackbar = Snackbar.make(
                            window.decorView.rootView,
                            " removed from Recyclerview!",
                            Snackbar.LENGTH_LONG
                        )
                        snackbar.setAction("UNDO") {
                            // undo is selected, restore the deleted item
                            adapter!!.restoreItem(deletedModel, position)
                        }
                        snackbar.setActionTextColor(Color.YELLOW)
                        snackbar.show()
                    } else {
                        val deletedModel = imageModelArrayList!![position]
                        adapter!!.removeItem(position)
                        // showing snack bar with Undo option
                        val snackbar = Snackbar.make(
                            window.decorView.rootView,
                            " removed from Recyclerview!",
                            Snackbar.LENGTH_LONG
                        )
                        snackbar.setAction("UNDO") {
                            // undo is selected, restore the deleted item
                            adapter!!.restoreItem(deletedModel, position)
                        }
                        snackbar.setActionTextColor(Color.YELLOW)
                        snackbar.show()
                    }
                }
 
                override fun onChildDraw(
                    c: Canvas,
                    recyclerView: RecyclerView,
                    viewHolder: RecyclerView.ViewHolder,
                    dX: Float,
                    dY: Float,
                    actionState: Int,
                    isCurrentlyActive: Boolean
                ) {
 
                    val icon: Bitmap
                    if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
 
                        val itemView = viewHolder.itemView
                        val height = itemView.bottom.toFloat() - itemView.top.toFloat()
                        val width = height / 3
 
                        if (dX > 0) {
                            p.color = Color.parseColor("#388E3C")
                            val background =
                                RectF(itemView.left.toFloat(), itemView.top.toFloat(), dX, itemView.bottom.toFloat())
                            c.drawRect(background, p)
                            icon = BitmapFactory.decodeResource(resources, R.drawable.delete)
                            val icon_dest = RectF(
                                itemView.left.toFloat() + width,
                                itemView.top.toFloat() + width,
                                itemView.left.toFloat() + 2 * width,
                                itemView.bottom.toFloat() - width
                            )
                            c.drawBitmap(icon, null, icon_dest, p)
                        } else {
                            p.color = Color.parseColor("#D32F2F")
                            val background = RectF(
                                itemView.right.toFloat() + dX,
                                itemView.top.toFloat(),
                                itemView.right.toFloat(),
                                itemView.bottom.toFloat()
                            )
                            c.drawRect(background, p)
                            icon = BitmapFactory.decodeResource(resources, R.drawable.delete)
                            val icon_dest = RectF(
                                itemView.right.toFloat() - 2 * width,
                                itemView.top.toFloat() + width,
                                itemView.right.toFloat() - width,
                                itemView.bottom.toFloat() - width
                            )
                            c.drawBitmap(icon, null, icon_dest, p)
                        }
                    }
                    super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive)
                }
            }
        val itemTouchHelper = ItemTouchHelper(simpleItemTouchCallback)
        itemTouchHelper.attachToRecyclerView(recyclerView)
    }
 
    private fun populateList(): ArrayList<Model> {
 
        val list = ArrayList<Model>()
 
        for (i in 0..7) {
            val imageModel = Model()
            imageModel.setNames(myImageNameList[i])
            list.add(imageModel)
        }
 
        return list
    }
}

 

خب بیایید اکتیویتی اصلی را بررسی کنیم .

ابتدا کدهای زیر را میبینید :‌

 private var recyclerView: RecyclerView? = null
    private var imageModelArrayList: ArrayList<Model>? = null
    private var adapter: SwipeAdapter? = null
    private val p = Paint()
    private val myImageNameList = arrayOf("Benz", "Bike", "Car", "Carre

خب در ابتدا یک ابجکت از Recyclerview ساختیم و سپس یک SwipeAdapter و بعد هم کلاس های مورد نیازمان 

یک آرایه با ابجکت های مدلی که بالاتر ساختیم و اسمش imageModelArrayList است 

و یک آرایه ی از نوع string دیگر که نام وسایل نقلیه را نگهداری میکند مانند Benz Bike Car و ...

حالا کدهای زیر را ببینید :‌

imageModelArrayList = populateList()
 
        adapter = SwipeAdapter(this, imageModelArrayList!!)
        recyclerView!!.adapter = adapter
        recyclerView!!.layoutManager = LinearLayoutManager(applicationContext, LinearLayoutManager.VERTICAL, false)
         enableSwipe()

کامپایلر populateList() را استفاده خواهد کرد که دیتا را داخل  imageModelArrayList قرار دهد . 

سپس adapter را به recyclerview معرفی و bind خواهد کرد . 

کامپایلر متد enableSwipe()  را صدا میزند . 

در زیر کدهای متد enableSwipe()  میبینید :‌

override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
                    val position = viewHolder.adapterPosition
 
                    if (direction == ItemTouchHelper.LEFT) {
                        val deletedModel = imageModelArrayList!![position]
                        adapter!!.removeItem(position)
                        // showing snack bar with Undo option
                        val snackbar = Snackbar.make(
                            window.decorView.rootView,
                            " removed from Recyclerview!",
                            Snackbar.LENGTH_LONG
                        )
                        snackbar.setAction("UNDO") {
                            // undo is selected, restore the deleted item
                            adapter!!.restoreItem(deletedModel, position)
                        }
                        snackbar.setActionTextColor(Color.YELLOW)
                        snackbar.show()
                    } else {
                        val deletedModel = imageModelArrayList!![position]
                        adapter!!.removeItem(position)
                        // showing snack bar with Undo option
                        val snackbar = Snackbar.make(
                            window.decorView.rootView,
                            " removed from Recyclerview!",
                            Snackbar.LENGTH_LONG
                        )
                        snackbar.setAction("UNDO") {
                            // undo is selected, restore the deleted item
                            adapter!!.restoreItem(deletedModel, position)
                        }
                        snackbar.setActionTextColor(Color.YELLOW)
                        snackbar.show()
                    }
                }

 

کامپایلر متد onSwiped() را زمانی صدا میزند که کاربر به سمت راست یا چپ swipe بکند . 

سپس یک شرط if گذاشتیم تا بررسی کند عمل swipe به کدام سمت بوده است . 

سپس کامپایلر آبجکتی که میخواهد پاک شود را انتخاب میکند . 

سپس متد removeItem() را صدا میزند که درون کلاس آداپتر است 

سپس یک snack bar نمایش داده میشود . 

زمانی که کاربر دکمه ی UNDO را روی snack bar  کلیک میکند کامپایلر متد restoreItem()  را صدا خواهد زد . 

و کار ما با پیاده سازی این سیستم تمام شد . 

 

لینک کوتاه این مقاله : https://avasam.ir/post/124
این سیستم برپایه ی علاقه مندی شما یک دوره ی مناسب به شما پیشنهاد میدهد
مرا بسوی بهترین دوره ی آموزشی که برای من مناسب است هدایت کن 🤖
برای استفاده ی دیگران و حمایت از ما در جامعه های زیر به اشتراک بگذارید

.:: نظرهای کاربران ::.
شهرام قاسمی / 5 ماه پیش

دستتون درد نکنه آموزش بسیاری عالی بود زبان کاتلین عالیه 

دوره ی آموزش پروژه محور ساخت کافه بازار دوره ی آموزش پروژه محور ساخت فروشگاه دیجیکالا آموزش لاراول دوره ی آموزش ویو جی اس