<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های محمد جهانگیری</title>
        <link>https://virgool.io/feed/@mjahangiry75</link>
        <description></description>
        <language>fa</language>
        <pubDate>2026-04-15 08:57:09</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/45586/avatar/RP7RSE.png?height=120&amp;width=120</url>
            <title>محمد جهانگیری</title>
            <link>https://virgool.io/@mjahangiry75</link>
        </image>

                    <item>
                <title>خدافظی با SharedPreferences - آموزش DataStore در اندروید</title>
                <link>https://virgool.io/MobileLab/%D8%AE%D8%AF%D8%A7%D9%81%D8%B8%DB%8C-%D8%A8%D8%A7-sharedpreferences-%D8%A2%D9%85%D9%88%D8%B2%D8%B4-datastore-%D8%AF%D8%B1-%D8%A7%D9%86%D8%AF%D8%B1%D9%88%DB%8C%D8%AF-b9vapqoekfm7</link>
                <description>DataStore در اندرویددیتا استور چیه؟دیتا استور(DataStore) کامپوننت جدید Jetpack که قرار جایگزین SharedPreferences بشه، طبیعتا نو که بیاد به بازار یه سری ویژگی های جدید داره، بهترین ویژگی که دیتا استور نسبت به شردپرفرنسز داره اینه که خوندن و نوشتن دیتا به صورت Async و با Flow که کامپوننتی از Kotlin Coroutines هست انجام میشه. چندتا نکته که قبل از ادامه دادن باید بدونید اینه که:دیتا استور در این زمان که این پست نوشته میشه تو حالت آلفا قرار داره که برای استفاده در پروداکشن مناسب نیست.قراره ما با کاتلین کد بزنیمما از Kotlin Coroutines استفاده میکنیم(خیلی سطح پایین)همچنین از Kotlin Extension هم استفاده شدهبرای UI برنامه از Jetpack Compose استفاده شدهچطور ازش استفاده کنیممرحله اول - کتابخونه رو به پروژه اضافه میکنیم:implementation &amp;quotandroidx.datastore:datastore-preferences:1.0.0-alpha02&amp;quotمرحله دوم - ساخت یک نمونه از دیتا استور:private val dataStore by lazy { context.createDataStore(name = &amp;quotdata_store&amp;quot) }مرحله سوم - خوندن و نوشتن:دیتا استور هم مثل شردپرفرنسز داده ها رو به صورت key-value سازماندهی میکنه، اما موضوع برای دیتا استور یکم فرق داره اونم اینه که مثل شردپرفرنسز key ها به صورت string نیستن بلکه اونا باید از نوع Preferences.Key باشن(جلوتر مسئله روشن‌تر میشه) و موضوع دیگه ای هم که هست اگه بخوایم دیتایی رو توی دیتا استور ذخیره کنیم حتما باید از کوروتین کاتلین استفاده کینم، که در ساده ترین حالت فرآیند ذخیره کردن به شکل زیر هست:GlobalScope.launch {
    val TEXT_KEY = preferencesKey&lt;String&gt;(&amp;quottext&amp;quot)
    dataStore.edit {
        it[TEXT_KEY] = &amp;quotThis is just a simple text&amp;quot
    }
}درست مثل شردپرفرنسز تو خط دوم یک کلید برای ذخیره کردن دیتامون ساختیم به اسم TEXT_KEY و خط های بعدی دیتا رو توی دیتا استور ذخیره میکنهوقتی بخوایم داده هایی رو که قبلا ذخیره شدن رو از دیتا استور بخونیم، ما اون دیتا رو در قالب Flow دریافت میکنیم مثل کد زیر:val text: Flow&lt;String&gt; = dataStore.data.map { data -&gt;
    data[key] ?: &amp;quotDefault value&amp;quot // if the data is null we&#039;ll get the &amp;quotDefault value&amp;quot text
}

text.collect { data -&gt;
    println(&amp;quotdata is: $data&amp;quot)
}درسته یکم عجیب غریب به نظر میاد برای همین همونطوری که بالاتر قید شد برای اینکه کار با دیتا استور راحت‌تر و خوانایی کدها بالاتر بره یه سری Extension Function به سورس‌کدمون اضافه کردیم@InternalCoroutinesApi
fun &lt;T&gt; DataStore&lt;Preferences&gt;.liveData(key: Preferences.Key&lt;T&gt;, defaultValue: T): LiveData&lt;T&gt; =
    data.map {
        it[key] ?: defaultValue // if the data is null we&#039;ll get the defaultValue
    }.asLiveData(IO)

fun &lt;T&gt; DataStore&lt;Preferences&gt;.saveData(data: Pair&lt;Preferences.Key&lt;T&gt;, T&gt;) = GlobalScope.launch {
    edit {
        it[data.first] = data.second
    }
}فانکشن اول دیتای ذخیره شده تو دیتا استور رو به صورت LiveData به ما میده که نهایتا نحوه خوندن از دیتا استور رو آسونتر میکنه:val textLiveData: LiveData&lt;String&gt; = dataStore.liveData(TEXT_KEY, &amp;quotnot specified yet&amp;quot) // &amp;quotnot specified yet&amp;quot is the default valueهمینطور نحوه استفاده از فانکشن دوم:dataStore.saveData(TEXT_KEY to &amp;quotThis text is gonna be saved in data store&amp;quot)در نهایت کد کامل اکتیویتی:class MainActivity : AppCompatActivity() {

    private val dataStore by lazy { createDataStore(name = &amp;quotdata_store&amp;quot) }

    @InternalCoroutinesApi
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val textLiveData: LiveData&lt;String&gt; = dataStore.liveData(TEXT_KEY, &amp;quotnull&amp;quot)

        setContent {
            AppScreen(
                textLiveData = textLiveData,
                onSaveClicked = { saveText(it) }
            )
        }
    }

    private fun saveText(value: String) = dataStore.saveData(TEXT_KEY to value)

    companion object {
        private val TEXT_KEY = preferencesKey&lt;String&gt;(&amp;quottext&amp;quot)
    }میتونید کد کامل رو از گیت دریافت کنید</description>
                <category>محمد جهانگیری</category>
                <author>محمد جهانگیری</author>
                <pubDate>Wed, 28 Oct 2020 10:35:19 +0330</pubDate>
            </item>
                    <item>
                <title>استفاده از دیتا بایندینگ در ریسایکلرویو (اندروید)</title>
                <link>https://virgool.io/MobileLab/%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-%D8%AF%DB%8C%D8%AA%D8%A7-%D8%A8%D8%A7%DB%8C%D9%86%D8%AF%DB%8C%D9%86%DA%AF-%D8%AF%D8%B1-%D8%B1%DB%8C%D8%B3%D8%A7%DB%8C%DA%A9%D9%84%D8%B1%D9%88%DB%8C%D9%88-%D8%A7%D9%86%D8%AF%D8%B1%D9%88%DB%8C%D8%AF-t4443bvzrjvc</link>
                <description>سلام این اولین پست من هست امیدوارم خوشتون بیاد و بتونید ازش استفاده کنید.خب دیتابایندینگ چه مشکلی رو برای ما حل کرده؟ قبل از به وجود اومدن دیتابایندینگ ما مجبور بودیم تک تک ویو هایی که داشتیم(مثل تکست‌ویو و ادیت‌تکست) با استفاده از findViewById به کد جاوا متصل کنیم(البته اگه از زبان کاتلین برای توسعه اپ استفاده کنید دیگه مجبور نیستید از findViewById استفاده کنید) و بعد از اون دیتای هر ویو رو داخلش بریزیم برای نمونه مثال زیر رو در نظر بگیرید:TextView tvFirstName = findViewById(R.id.first_name);
TextView tvLastName = findViewById(R.id.last_name);
TextView tvAge = findViewById(R.id.age);
TextView tvDescription = findViewById(R.id.description);

tvFirstName.setText(&amp;quotMohammad&amp;quot);
tvLastName.setText(&amp;quotJahangiri&amp;quot);
tvAge.setText(&amp;quot23&amp;quot);
tvDescription.setText(&amp;quotDescription&amp;quot);همونطور که بالاتر توضیح دادیم اول ویوبایندینگ(findViewById) کردیم بعد دیتابایندینگ(setText)شاید این کار برای چهار ویو خیلی آسون باشه اما وقتی تعداد ویو ها بیشتر بشه باید این کار تکراری رو برای هر ویو انجام بدید، اینجاست که دیتابایندینگ به کمک ما میاد و ما رو از شر boilerplate های کسالت آور رها میکنه.اگر بای دیتابایندینگ آشنایی ندارید تو مقاله زیر میتونید مراحل کامل نحوه پیاده سازی دیتابایندینگ رو به زبان ساده مطالعه کنید. https://virgool.io/AndroidStudio/%D9%BE%DB%8C%D8%A7%D8%AF%D9%87-%D8%B3%D8%A7%D8%B2%DB%8C-data-binding-%D8%AF%D8%B1-%D8%A7%D9%86%D8%AF%D8%B1%D9%88%DB%8C%D8%AF-xjfjo3xvmvn3 تو مقاله بالا یاد گرفتید که چطور میشه دیتابایندینگ رو فعال و از اون استفاده کنید، اما موضوع نوشته ما پیاده سازی دیتا بایندینگ تو ریسایکلرویو هست که آموزشش توی مقاله مذکور نبود.اول برای آیتم های ریسایکلرویو نیاز به یک دیتا مدل داریم، دیتا مدل زیر شامل سه خصوصیت (name, description, isFavorite) میشه:data class Person(
    var name: String,
    var description: String,
    var isFavorite: Boolean
)همونطور که میدونید به یک لایه برای آیتم ها نیاز داریم، طراحی لایه هم به صورت زیر هست:&lt;?xml version=&amp;quot1.0&amp;quot encoding=&amp;quotutf-8&amp;quot?&gt;
&lt;layout xmlns:android=&amp;quothttp://schemas.android.com/apk/res/android&amp;quot
    xmlns:tools=&amp;quothttp://schemas.android.com/tools&amp;quot&gt;

    &lt;data&gt;

        &lt;import type=&amp;quotandroid.view.View&amp;quot /&gt;

        &lt;variable
            name=&amp;quotperson&amp;quot
            type=&amp;quotcom.github.masterj3y.Person&amp;quot /&gt;

    &lt;/data&gt;

    &lt;RelativeLayout
        android:layout_width=&amp;quotmatch_parent&amp;quot
        android:layout_height=&amp;quotwrap_content&amp;quot
        android:background=&amp;quot#FFFFFF&amp;quot
        android:paddingStart=&amp;quot16dp&amp;quot
        android:paddingTop=&amp;quot8dp&amp;quot
        android:paddingEnd=&amp;quot16dp&amp;quot
        android:paddingBottom=&amp;quot8dp&amp;quot&gt;

        &lt;TextView
            android:id=&amp;quot@+id/nameTextView&amp;quot
            android:layout_width=&amp;quotmatch_parent&amp;quot
            android:layout_height=&amp;quotwrap_content&amp;quot
            android:layout_alignParentStart=&amp;quottrue&amp;quot
            android:layout_toStartOf=&amp;quot@id/favorite&amp;quot
            android:text=&amp;quot@{person.name}&amp;quot
            android:textColor=&amp;quot#000&amp;quot
            android:textSize=&amp;quot20sp&amp;quot
            tools:text=&amp;quotTitle&amp;quot /&gt;

        &lt;TextView
            android:layout_width=&amp;quotwrap_content&amp;quot
            android:layout_height=&amp;quotwrap_content&amp;quot
            android:layout_below=&amp;quot@id/nameTextView&amp;quot
            android:layout_alignStart=&amp;quot@id/nameTextView&amp;quot
            android:text=&amp;quot@{person.description}&amp;quot
            tools:text=&amp;quotDescription&amp;quot /&gt;

        &lt;ImageView
            android:id=&amp;quot@+id/favorite&amp;quot
            android:layout_width=&amp;quotwrap_content&amp;quot
            android:layout_height=&amp;quotwrap_content&amp;quot
            android:layout_alignParentEnd=&amp;quottrue&amp;quot
            android:src=&amp;quot@drawable/ic_turned_in_black_24dp&amp;quot
            android:visibility=&amp;quot@{person.isFavorite?View.VISIBLE:View.INVISIBLE}&amp;quot /&gt;

    &lt;/RelativeLayout&gt;

&lt;/layout&gt;بعد از ایجاد لایه بالا یک بار پروژه رو Rebuild کنید تا کدهای بایندینگ برای ما Generate بشن، برای این کار از منوی Build گزینه Rebuild Project رو انتخاب کنید.وقتی که پروژه بیلد شد کلاسی تحت عنوان PersonsAdapter ایجاد کنیداگر آداپتر های ریسایکلرویو رو بدون استفاده از دیتابایندینگ پیاده‌میکردیم باید یک پارامتر رو به عنوان itemView به کلاس پدر یا همون RecyclerView.ViewHolder پاس میدادیم دقیقا مثل کد زیر:inner class PersonViewHolder(itemView: View) :
    RecyclerView.ViewHolder(itemView) {
    
    }طبق معمول باید توی ViewHolder هم تک تک ویو ها رو با استفاده از findViewById باندشون کنیم، ولی به لطف DataBinding دیگه نیازی به این بویلرپلیت ها نداریم کافیه به جای itemView تو کانستراکتور PersonViewHolder یه نمونه از نوع کلاس بایندینگ ایجاد شده بهش بدیم، خب کلاس بایندینگ ایجاد شده چی هست؟ همون کدهایی که بعد از بیلد کردن برامون Generate شدن، این کد ها برای هر لایه تو یک کلاس هم اسم همون لایه ایجاد میشه، برای مثال اگر اسم لایه مربوط به آیتم های ریسایکلرویویی که کد xml ش رو بالاتر گذاشتم list_person_item_layout.xml باشه کلاسی که اندروید استودیو برای DataBinding این لایه برای ما تولید میکنه ListPersonItemLayoutBinding خواهد بود (دقت کنید همه _ ها حذف شد و اسم به صورت CamelCase نوشته شد)حالا کافیه به جای itemView ما یک نمونه از ListPersonItemLayoutBinding تو کانستراکتور PersonViewHolder داشته باشیم:inner class PersonViewHolder(privateval binder: ListPersonItemLayoutBinding) :
    RecyclerView.ViewHolder(binder.root) {

}کلاس RecyclerView.ViewHolder یک ویو رو به عنوان پارامتر کانستراکتور میگیره ما هم مقدار binder.root بهش پاس میدیمو حالا برای آخرین مرحله کافیه یک شی از کلاس Person به binder بدیم تا DataBinding ویو ها رو به صورت اتوماتیک بایند و دیتای هر ویو رو ست کنه، کد زیر همین کار رو برای ما انجام میده:inner class PersonViewHolder(privateval binder: ListPersonItemLayoutBinding) :
    RecyclerView.ViewHolder(binder.root) {

    fun render(person: Person) {
        binder.person = person
    }

}بله دیگه نیازی به findViewById و بقیه بویلرپلیت ها نیست، کد ویوهولدر ما فقط شامل همین چند خط میشه و تمام، کد کامل PersonsAdapter هم به صورت زیر هست:class PersonsAdapter(privateval personsList: List&lt;Person&gt;) :
    RecyclerView.Adapter&lt;PersonsAdapter.PersonViewHolder&gt;() {

    private lateinit var context: Context

    override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
        super.onAttachedToRecyclerView(recyclerView)
        context = recyclerView.context
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = PersonViewHolder(
        DataBindingUtil.inflate(
            LayoutInflater.from(context),
            R.layout.list_person_item_layout,
            parent,
            false
        )
    )

    override fun getItemCount() = personsList.size

    override fun onBindViewHolder(holder: PersonViewHolder, position: Int) =
        holder.render(personsList[position])

    inner class PersonViewHolder(privateval binder: ListPersonItemLayoutBinding) :
        RecyclerView.ViewHolder(binder.root) {

        fun render(person: Person) {
            binder.person = person
        }

    }

}کد کامل پروژه رو هم تو گیت هاب گذاشتم: https://github.com/masterj3y/RecyclerViewDataBinding برای اولین نوشته من میتونه خیلی اشکالات داشته باشه، اگر خیلی سخت بود یا متوجه مورد اشتباهی یا غلط املایی شدین ممنون میشم توی کامنت ها مطلعم کنید‌:) ممنونم که وقتتون رو در اختیار من گذاشتین.</description>
                <category>محمد جهانگیری</category>
                <author>محمد جهانگیری</author>
                <pubDate>Mon, 02 Sep 2019 13:21:52 +0430</pubDate>
            </item>
            </channel>
</rss>