تخفیف تابستانی سال ۱۳۹۹ تاپایان: یکشنبه ۶ مهر ۱۳۹۹
بزن بریم فروشگاه
ساخت عملیات swipe حذف آیتم های recyclerview مثل اپ GMAIL

ساخت عملیات swipe حذف آیتم های recyclerview مثل اپ GMAIL

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

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

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

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

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

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

 

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

 

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

 

در محیط اندروید استودیو خودتان یک پروژه جدید بسازید و یک اکتیویتی 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()  را صدا خواهد زد . 

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

 


پایان 👍
برای استفاده ی دیگران و حمایت از ما در جامعه های زیر به اشتراک بگذارید

مهندس عباس نیک زاد


برای نوشتن نظر وارد شوید ورود
یا به عنوان یک میهمان نظر خود را بنویسید :
    1. اگر سوال شما طولانی است و نیاز به پشتیبانی خوبی دارد در پروفایل خود تیکت باز کنید تیم پشتیبان ما پاسخ میدهد
    2. سعی کنید نظر خود را بیش از چند جمله بنویسید
    3. نظرات شامل توهین و تهمت و نامرتبط تائید نخواهد شد

برای دریافت جدید ترین آموزش ها ما را در فضای مجازی دنبال کنید
دوره ی آموزش پروژه محور ساخت کافه بازار دوره ی آموزش پروژه محور ساخت فروشگاه دیجیکالا آموزش لاراول دوره ی آموزش ویو جی اس
x
تبلیغات از درون سایت خودمون :)
X
دوره های پیشنهادی برای شما