<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های Behnam Nasehi | بهنام ناصحی</title>
        <link>https://virgool.io/feed/@behnamnasehi</link>
        <description>Android Application Developer https://behnamnasehi.ir</description>
        <language>fa</language>
        <pubDate>2026-06-07 08:55:10</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/14934/avatar/dPwn6D.png?height=120&amp;width=120</url>
            <title>Behnam Nasehi | بهنام ناصحی</title>
            <link>https://virgool.io/@behnamnasehi</link>
        </image>

                    <item>
                <title>کاتلین چیست و چجوری کار میکنه ؟ شروع یادگیری کاتلین قسمت اول</title>
                <link>https://virgool.io/@behnamnasehi/%DA%A9%D8%A7%D8%AA%D9%84%DB%8C%D9%86-%DA%86%DB%8C%D8%B3%D8%AA-%D9%88-%DA%86%D8%AC%D9%88%D8%B1%DB%8C-%DA%A9%D8%A7%D8%B1-%D9%85%DB%8C%DA%A9%D9%86%D9%87-%D8%B4%D8%B1%D9%88%D8%B9-%DB%8C%D8%A7%D8%AF%DA%AF%DB%8C%D8%B1%DB%8C-%DA%A9%D8%A7%D8%AA%D9%84%DB%8C%D9%86-%D9%82%D8%B3%D9%85%D8%AA-%D8%A7%D9%88%D9%84-cemreblniqva</link>
                <description>What is Kotlin ? کاتلین چیستاگه برنامه نویس اندروید باشید و هنوز با جاوا کار میکنید دیگه کم کم باید جاوا بزارید روی طاقچه و چمدون رو ببندید کوچ کنید به سمت کاتلین... منم از این قافله مستثنا نیستم و در همین مسیر هستم ولی خب دوست داشتم این مسیری رو که دارم طی میکنم مکتوب داشته باشم تا به کسانی که شرایط من رو دارن بتونم کمکی کرده باشم. من میخوام مرحله به مرحله مسیرم رو توضیح بدم و کمی عمیق تر وارد کاتلین شم.مرحله اول : شناخت زبانشرکت Jet Brains واقع در سن پترزبورگ روسیه که توسعه زبان کاتلین بر عهده اون ها بود اسم این زبان رو از جزیره کاتلین در روسیه الهام گرفت, در 8 نوامبر سال 2010 اولین کامیت در ریپازیتوری زده شد و نسخه اول در سال 2016 منتشر شد.جزیره کاتلین در روسیهاز مزیت های کاتلین میشه به cross-platform , statically typed, general-purpose , High-level programming language و type inference اشاره کرد که تک تک این موارد رو براتون توضیح میدمکاتلین Cross Platform زبان های Cross Platform به شما این اجازه رو میدن که روی پلتفرم های مختلف بتونی با یک زبان کد بزنی و دوباره نیاز نیست که واسه پلتفرم جدید کد های جدید بنویسی , برای مثال در کاتلین شما میتونی یک هسته مشترک طراحی کنی که شامل منطق و عملکرد برنامه باشه و از کد نوشتن دوباره در هر پلتفرم جلوگیری کنی و در نهایت در هر پلتفرم فقط UI مخصوص به خودش رو به کمک هسته پیاده سازی میکنی.علاوه بر خود زبان, کاتلین فریم ورکی به نام KMM رو داره که توسعه پلتفرمی رو تسهیل میده , شما با این فریم ورک میتونی هم برای Android و iOS کد مشترکی بزنید.کاتلین Statically Typedوقتی یک زبانی Statically Typed باشه به این معنی هست که هر متغییری دارای یک نوع خاص هست ( Int , String , Boolean...) و قبل اجرا شدن توسط کامپایلر شناخته میشه و بررسی میشه . در این نوع زبان ها وقتی متغییری تعریف میکنید نوع اون رو باید به صراحت مشخص کنید تا کامپایلر بفهمه که این متغییر چجوری باید استفاده بشه و مطمئن بشه که اون متغییر درست تعریف و استفاده شده.کاتلین General Purpose - GPLبه زبان هایی گفته میشه که طیف وسیعی از حوزه هارو شامل میشن مثل توسعه وب، توسعه اپلیکیشن موبایل، توسعه سمت سرور، برنامه های دسکتاپ و غیرهبه عنوان مثال، Python یک GPL است، در حالی که SQL یک DSL برای جستجو در پایگاه‌های داده رابطه‌ای است.کاتلین High-level programming languageبه صورت خیلی خلاصه بخوام بگم : زبانی که خیلی کد زدن شمارو آسون بکنه بر خلاف زبان برنامه نویسی سطح پایین جوری طراحی شدن که خوندن و نوشتن و درکش برای ما بهتر باشه , بجای دستورات پیچیده ای که فقط کامپیوتر میفهمه , خیلی محاوره ای تر هست و به تفکر ما نزدیک تره ,این به برنامه نویس ها اجازه می ده تا با استفاده از کلمات، نمادها و ساختارهای آشنا کد بنویسن و بیان ایده ها و حل مسائل را آسان تر می کنه. کاتلین Type Inferenceکامپایلر خودش نوع متغییر رو با مقدار دادن اون متغیر تشخیص میده و نیاز نیست که شما نوعش رو مشخص کنید , این باعث میشه شما کد مختصری بنویسید و در عین حال  type safety رو حفظ کنید.کاتلین چجوری کامپایل میشه ؟کاتلین چجوری کامپایل میشه کامپایلر کاتلین به نام kotlinc یک برنامه خاصی هست که توسط JetBrains توسعه داده شده تا کد های کاتلین رو انالیز و کد های کاتلین رو به فرمی تبدیل کنه که کامپیوتر متوجه بشه و اجرا کنه.فایل .kt که کد های کاتلین شما داخل این فایل قرار داره رو به کامپایلر به عنوان ورودی میدید و کامپایلر مثل یک مترجم که به زبان کاتلین تسلط داره اون رو برای ماشین ترجمه میکنه و همچنین چک میکنه که شما از ساختار و قوانین از پیش تعیین شده پیروی میکنید و به دنبال تمامی خطاها و مشکلاتی میگرده که شما در کد انجام دادید .اگر همه چیز درست بود و هیچ مشکلی وجود نداشت کامپایلر کدهارو به فرمت خاصی به اسم بایت کد ( Byte Code ) تبدیل میکنه , بایت کد یک لول سطح پایین تر از کد های شماست که برای کامپیوتر قابل درک هست و میتونه اجرا کنه.زمانی کامپایلر کد های شمارو تبدیل کرد به بایت کد , حالا این بایت کد میتونه روی پلتفرم مورد نظر اجرا بشه , برای مثال اگر شما دارید برنامه اندروید میسازید , بایت کد روی گوشی های اندرویدی قابل اجراست و یا اگر هدفتون ماشین های مجازی جاواست ( JVM ) بایت کد شما روی تمامی دستگاه هایی که JVM رو ساپورت کنند اجرا میشه.درون کامپایلر چه خبره ؟اینجا میخوام یکم جزئی تر وارد کامپایلر بشیم و اینکه کد از اول چه مسیری رو طی میکنه که به بایت کد تبدیل بشه: مرحله اول: Lexical Analysisوقتی کد کاتلین خودتون رو به کامپایلر میدید وارد مرحله تحلیل واژگانی میشه  ( Lexical Analysis ) . یعنی کد شما رو کاراکتر به کاراکتر میخونه و تحلیل میکنه و در اخر به قسمت های کوچیکتری به نام توکن ذخیره میکنه , توکن ها میتونند keywords  ها باشند مثل : val، if، while و یا شناسه ها باشن مثل متغییر ها و نام توابع , یا موارد دیگر مثل عملگرها , نماد ها و ...بیشتر در ویکی پدیامرحله دوم: Syntax Analysis (Parsing)بعد از اینکه کامپایلر توکن هارو شناسایی کرد وارد این مرحله میشه که سینتکس ها و ساختار کد هارو چک میکنه و مطمئن میشه شما از قوانین کاتلین پیروی میکنید و تمامی توکن ها به درستی مرتب شده اند و در اخر یک سلسله مراتبی به اسم parse tree میسازهبیشتر در ویکی‌پدیامرحله سوم: Semantic Analysisدر این مرحله کامپایلر از parse tree استفاده میکنه و درون این tree , نحوه استفاده متغییر ها , فراخوانی تابع ها و دیگر قوانین زبان کاتلین رو بررسی میکنه تا از نظر منطقی درست باشند و به اصطلاح کدی معنا دار داشته باشیدبیشتر در گیگز فور گیگمرحله چهارم: Intermediate Representation (IR)نمایش میانی (IR) یک مرحله میانی بین کد تجزیه شده و بایت کد تولید شده نهایی است. این به عنوان یک نمایش ساده و ساختار یافته از کد شما عمل می کنه که تجزیه و تحلیل، بهینه سازی و تبدیل برای کامپایلر آسان تر است.&amp;lt;br/&amp;gt;بیشتر در ویکی‌پدیامرحله پنجم: Optimizationکامپایلر تکنیک های مختلف بهینه سازی را برای کد IR تولید شده اعمال می کند. هدف بهینه سازی بهبود عملکرد، کارایی و اندازه کد کامپایل شده است. ساختار کد را تجزیه و تحلیل می کند و تغییراتی را برای اجرای سریعتر، استفاده از حافظه کمتر یا بهینه سازی عملیات خاص اعمال می کند.بیشتر در ویکی‌پدیامرحله ششم: Bytecode Generationاگر همه چیز درست بود و هیچ مشکلی وجود نداشت کامپایلر کدهارو به فرمت خاصی به اسم بایت کد ( Byte Code ) تبدیل میکنه , بایت کد یک لول سطح پایین تر از کد های شماست که برای کامپیوتر قابل درک هست و میتونه اجرا کنه.بیشتر در ویکی‌پدیااین داستان ما ادامه داره و سعی میکنم از کوچیک ترین مسائل هم به سادگی نگذرم و همه چیو بررسی کنم جدا از این که به شما کمک کوچیکی میشه , برای یادگیری خودم خیلی موثر تر هست که بخوام بنویسم و آموزش بشدم میتونید در توییتر با من در ارتباط باشید : BinaryBeastt مطمئن باشید هر کمکی از دستم بر بیاد انجام میدم :)</description>
                <category>Behnam Nasehi | بهنام ناصحی</category>
                <author>Behnam Nasehi | بهنام ناصحی</author>
                <pubDate>Sat, 27 May 2023 15:51:34 +0330</pubDate>
            </item>
                    <item>
                <title>امن کردن دیتا های ذخیره شده در SharedPreferences</title>
                <link>https://virgool.io/@behnamnasehi/%D8%A7%D9%85%D9%86-%DA%A9%D8%B1%D8%AF%D9%86-%D8%AF%DB%8C%D8%AA%D8%A7-%D9%87%D8%A7%DB%8C-%D8%B0%D8%AE%DB%8C%D8%B1%D9%87-%D8%B4%D8%AF%D9%87-%D8%AF%D8%B1-sharedpreferences-m0p12qtsjez9</link>
                <description>استفاده از Shared Preferences خیلی راحت و آسونه و برای برنامه نویس های تنبلی مثل من یک تهدید حساب میشه ! چرا ؟ اطلاعات شخصی کاربر ها برای خودشون خیلی مهمه و هرگونه نفوذ به این اطلاعات میتونه فاجعه به بار بیار , هکر ها با ترفند هایی میتونن به این Shared Preferences ما دسترسی پیدا کنن و برای اون فاجعه قدمی بردارن پس تا وقتی Shared Preferences ما امن نباشه و رمزنگاری نشده باشه یجورایی میشه گفت که بی فایدست , بیاید با چند مثال بهتون توضیح بدم :خب اول من میام همونجوری که همیشه از Shared Preferences استفاده میکردم کلاسش رو ساختم و دیتایی که در اپلیکیشن ذخیره شد به صورت زیر کاملا نمایان شد : همونطور که دیدید کاملا من میتونم به اطلاعات ذخیره شده دسترسی پیدا کنم و ببینم چه value با چه key ذخیره شده خب حالا راه حل چیه ؟ خب چطوری از دیتاهامون محافظت کنیم ؟ مشکل ما با استفاده از EncryptedSharedPreferences حل میشه , EncryptedSharedPreferences دقیقا تمامی کارایی که parent خودش SharedPreferences انجام میده رو به همون صورت انجام میده ولی به صورت رمزنگاری شده و هیچ نفر سومی نمیتونه به این اطلاعات دسترسی پیدا کنه و اگه هم پیدا کنه هیچی ازش نمیفهمه چون رمزنگاری شدست.توجه : EncryptedSharedPreferences  برای SDK 23 به بالاست ! و برای اندروید lollipop به پایین باید همون پیشفرض رو استفاده کنیدمرحله اول ( Add dependency ) :به Build.Gradle App Level اضافه کنید//app level build.gradle file
implementation &amp;quotandroidx.security:security-crypto:1.0.0-alpha02&amp;quotمرحله دوم ( ساخت کلاس ) :خب ما این کلاس رو میسازیم و این کد هارو داخلش مینویسیم : مرحله سوم ( طریقه استفاده ) :طریقه استفاده ازش هیچ فرقی با همون پیشفرض خودش نمیکنه استفاده از این روش فقط برای رمزنگاری دیتا ها برای امن کردن دیتا هست نتیجه : اگه به عکس زیر توجه کنید متوجه میشید که دیتایی که ذخیره کردیم کاملا رمزنگاری شده , شما میتونید از خود Device file explorer اندروید استویو به این فایل دسترسی پیدا کنید : فکر کنم با مقایسه عکس قبل با عکس بالا میتونید به تفاوت های چشمگیری دست پیدا میکنیدخب اینجا یک نکته هست : به سایز های فایل ها توجه کنید ! تقریبا ده برابر شده و این باعث میشه پرفورمنس برنامه یکم بیاد پایین پس سعی کنید از EncryptedSharedPreferences برای دیتا های خاص و حساس استفاده کنید !فایل رو داخل گیت گذاشتم میتونید استفاده کنید :  https://github.com/behnamnasehi/EncryptedSharedPreferences مقاله های قبلی من : https://vrgl.ir/kJSzk  https://vrgl.ir/Mzh2B تلگرام من : BehnamNasehi</description>
                <category>Behnam Nasehi | بهنام ناصحی</category>
                <author>Behnam Nasehi | بهنام ناصحی</author>
                <pubDate>Sat, 19 Sep 2020 12:54:47 +0430</pubDate>
            </item>
                    <item>
                <title>Smooth Corner Transition</title>
                <link>https://virgool.io/MobileLab/smooth-corner-transition-zhj9blksp27i</link>
                <description>یکی از چالش هایی که تو پروژه اخیر داشتم این بود که توی یک صفحه ای ما یک دیزاینی داشتیم که Corner داشت و نیاز بود وقتی اسکرول میشه و به بالا میرسه Corner از بین بره و صاف بشه و tablayout که روز های هفته داخلش ست شده بود اون بالا pin بشه و فیکس بمونه , خب اینکار هم باعث دسترسی بهتر کاربر به روز های هفته میشه و هم تجربه بهتری و حس خوبی از اپ میگیره بنظر من باید با کاربر مثل بچه های کوچیک رفتار کرد , همه چی اماده و جلو دستش باشه و زحمتی نکشه , نباید بهش سخت گرفت و پیچیدش کرد چون بچه ست و زود رنجه البته تا جایی که امکانش هست و واقعا بعضی جاها امکان ساده کردن راه وجود نداره قبل از اینکه وارد دیزاین بشیم یک سری پیش نیاز ها برای دیزاین نیاز داریم !خب ما میخوایم radius رو از مثلا به 25 به 0 تبدیل کنیم من اومدم دوتا shape ساختم که این دو حالت توش تعریف شده باشه یعنی : این shape حالت ساده ما هست که هیچ radius نداره و وقتی کاربر صفحه رو اسکرول میکنه و tablayout ما به بالای صفحه میرسه باید تبدیل به این بشهو برای حالت دیگمون همینه فقط بهش radius اضافه میکنیم میخوام چیکار کنم !؟میخوام با یک حالت انیمیشن این دوتا shape رو transition کنم که این حس القا بشه که داره تبدیل میشه به فلت پس میام یه فایل Drawable میسازم به صورت زیر و از این دوتا shape بالا استفاده میکنمدیزاین اصلی دیزاین اصلی رو بصورت زیر میزنم که یک حالت پارالکس بتونم ایجاد کنمتوجه داشته باشید که به Tablayout باید بکگراند بدیم , حالا کدوم بکگراند ؟ همون tansition که ساختیم با استفاده از اون دوتا shape هابرسیم سر وقت اصل کار !برای به ثمر رسوندن کارمون من کد رو به چند تا قسمت تبدیل کردم از نظر من برای خوانایی کد هاتون هیچوقت بیش از یک کار رو داخل یک تابع انجام ندید و وظیفه هر تابع فقط یک کار باشه اینجوری هم دسترسی اسون تر میشه و هم کداتون تمیز تر میشهبه چند تا تابع نیاز داریم :تابعی برای تغییر رنگ و محتویات Status Bar چون من رنگ سرمه ای به دیزاین بالایی صفحه ام دادم و وقتی اسکرول میشه بگراند پایینی من سفیده پس Status Bar هم باید سفید بشه و اینکه بخوام بصورت ناگهانی و خیلی سریع Status Bar رو تغییر رنگ بدم حس خوبی رو القا نمیکنه و به نظرم اصلا حرفه ای نیست پس اومدم از Value Animator استفاده کردم و تو یه بازه زمانی مشخصی به صورت خیلی نرم تغییر رنگ دادم  :خب حالا اون changeStatusBarUiVisibility داخل تابع بالا چیه ؟ وقتی رنگ status bar رو تغییر میدید اون نوشته ها و یکون هایی که درونش قرار دارن بسته به نوع تم گوشی شما یک رنگی هستن و ممکنه با رنگ که شما دارید به status bar میدید تداخل داشته باشه و دیده نشه پس باید اونا هم بسته به نوع شرایطش تغییر بدید:ولی خب حالا این تابع ها رو کجا استفاده کنیم ؟ اصل اصل کار !وقتی که AppBarLayout ما وضعیتش تغییر کرد باید ما متوجه بشیم که ایا کامل بسته شده یا باز شده ( tab layout ما به صورت کامل چسبیده به بالاترین نقطه صفحه یا نه ؟ )قبل از اینکه listener واسه appbar ایجاد کنیم اول بریم Transition رو درست کنیم با استفاده از فانکشن هایی که بالا ایجاد کردیم یه تابع جدید میسازیم :اینجا چیکار کردم !؟ خب خیلی سادست اومدم اول اون drawable که به Tablayout دادم رو ازش گرفتم و چک کردم که ایا appbar layout بسته ست یا بازه و از طریق اون فهمیدم که باید انیمیشن رو شروع کنم یا بازگردانی و بعدش status bar رو رنگش رو تغییر دادم , از طریق کد listener زیر هم میتونید متوجه بشید که app bar در چه وضعیتی قرار داره :و در نهایت ؟اینم نتیجه کار ما : منتظر کامنت های شما هستم :)میخواستم از gist استفاده کنم ولی انگار ویرگول باگ داره  https://vrgl.ir/4z9l2 Telegram : BehnamNasehii</description>
                <category>Behnam Nasehi | بهنام ناصحی</category>
                <author>Behnam Nasehi | بهنام ناصحی</author>
                <pubDate>Tue, 15 Sep 2020 13:19:54 +0430</pubDate>
            </item>
                    <item>
                <title>چالش پروژه های من | ایجاد انیمیشن برفک</title>
                <link>https://virgool.io/coderlife/%DA%86%D8%A7%D9%84%D8%B4-%D9%BE%D8%B1%D9%88%DA%98%D9%87-%D9%87%D8%A7%DB%8C-%D9%85%D9%86-%D8%A7%DB%8C%D8%AC%D8%A7%D8%AF-%D8%A7%D9%86%DB%8C%D9%85%DB%8C%D8%B4%D9%86-%D8%A8%D8%B1%D9%81%DA%A9-ftrybee4yblu</link>
                <description>تو اخرین پروژه ای که داشتم خیلی متفاوت تر از بقیه پروژه های بود و چالش های متفاوت و جذابی برام پیش اومد و با خودم گفتن که این چالش ها رو براتون به اشتراک بزارم تا شما هم از تجربه هایی که کسب کردم استفاده بکنید ! چالش !!چالش من تو یک قسمتی از اپ این بود که باید یک انیمیشن به حالت برفک تلویزیون روی یک قسمتی از view مینداختم مشکلات پیش روم اینا بودن : مشکل low memoryبهینه ترین روش برای کاهش حجم اپمن باید اول پیدا میکردم که چجوری این رو پیاده سازی کنم که به مشکل memory leak نخورم چون پردازش سر این قسمت مطمئن بودم بالا میرفت پیاده سازی در مرحله اول طراح رابطه کاربری اومد 15 فریم از این انیمیشن تو قالب عکس برام فرستاد پس یعنی من 15 تا عکس کم حجم با بکگراند transparent دارم و فقط باید اینارو پشت سرم هم تو یه بازه زمانی مشخص و به صورت infinite نشون بدن پس میام یک فایل drawable میسازم به اسم anim_snow_flake.xml : و داخل layout یک imageview ساختم که این انیمیشن رو داخل نشون بدم :خب حالا قبل اینکه بیایم این انیمیشن رو start کنیم باید قبلش مموری رو چک کنیم در غیر این صورت اگه مموری low باشه اپ کرش میشه چون دیگه رم نمیمونه واسه پردازش این قسمت  , پس من یه تابع نوشتم و چک میکنم که ایا مموری low هست یا نه :تابع بالا به ما یک مقدار بولین برمیگردونه که از طریق اون میتونیم متوجه بشیم مموری در چه وضعیتی هست و حالا برسیم به قسمت انیمیشن , یک تابع جدا مینویسم و تمامی کد هایی که مربوط به انیمیشن برفک هست رو اینجا قرار میدم تا همه چی تفکیک شده باشه : تو تابع بالا اول اومدم چک کردم مموری در چه وضعیتی قرار داره و بعد از چک اومدم به imageView اون اینیمیشنی که ساختمو دادم و یک thread ایجاد کردم که انیمیشن رو Run کنه و در نهایت این همون چیزی که میخواستمنظرتون رو بهم لطف کنید بگید بنظرتون راه بهتری وجود داشت که من این رو پیاده سازی کنم ؟  https://vrgl.ir/4Culu میتونید از طریق تلگرام با من در ارتباط باشید : Telegram : BehnamNasehiishahrara.net</description>
                <category>Behnam Nasehi | بهنام ناصحی</category>
                <author>Behnam Nasehi | بهنام ناصحی</author>
                <pubDate>Tue, 01 Sep 2020 13:23:31 +0430</pubDate>
            </item>
                    <item>
                <title>بهتره که با Data Binding آشنا بشی !</title>
                <link>https://virgool.io/coderlife/%D8%A8%D9%87%D8%AA%D8%B1%D9%87-%DA%A9%D9%87-%D8%A8%D8%A7-data-binding-%D8%A2%D8%B4%D9%86%D8%A7-%D8%A8%D8%B4%DB%8C-ev2fx4qohytd</link>
                <description>همیشه سعی میکنم که بهترین و بهینه ترین راه رو اجرا کنم تا سهولت بدم به پروژه هام و بعضی وقتا ممکنه بهترین راه ها همیشه اسون نباشه من واقعا ازش جواب گرفتم و خیلی باعث تسریع تو کدام شده ( حداقل از دست findViewById خلاص شدم) بررسیشاید برای شما سوال پیش اومده باشه که Data Binding در برنامه نویسی اندروید چیه و چگونه استفاده می شه و اینکه چرا برای ساخت برنامه اندروید باید در یک فایل XML ظاهر برنامه را طراحی کنیم و بعد ویو ها را در فایل های جاوا قرار بدیم. یکی از روشهای استانداردی که شما میتوانید با کدهای بسیار کمتر و روش جذاب تر ویوهایی که در xml تعریف کردید را مقداردهی کنید استفاده از Data Binding . دیتا بایندینگ به ما کمک میکند تا بتونیم متغیرهای جاوا را به صورت مستقیم در xml وارد کنیم و از طرفی دیتابایندینگ به شما این امکان را میده تا برای همیشه با findViewById و setOnClickListener و ... خداحافظی کنید و کدهای بسیار تمیزتر و اصولی تری را استفاده کنید.ما معمولا به این صورت View هارو Find میکردیم و خیلی وقت گیر بوده و خیلی زمان زیادی از ما میگرفت و باعث میشد که خیلی کدهای شلوغی داشته باشیم ولی با DataBinding خیلی راحت میشه تو View بهش مقدار بدیم و دیگه نیازی زدن کد های بیشتر نباشه شروعبرای اضافه کردن dataBinding به پروژه , App Build.gradle , دیتابایندیگ رو فعال کنید و یک دور پروژه رو Sync کنید و منتظر میمونیم تا پروژه build بشه مثل عکس زیر : نحوه فایل های layout دیتابایندینگ یکم فرق میکنه و با تگ layout شروع میشه و در ادامه با المنت data که داخلش میتونید متغییر هاتون رو تعریف کنید و در ادامش هم خود view مثل زیر : متغیریuser که من در Data تعریف کردم یک مدل رو توصیف میکنه که ممکنه من از اون داخل طرح استفاده کنم :برای مقدار دهی از @{} استفاده میکنیم و از اون متغییر user که بالا استفاده کردیم کمک میگریم و دیتا رو توش میچینیم : برای هر layout یک کلاس binding ساخته میشه , در حالت پیشفرض کلاس ساخته شده اسمش از layou منشا میگیره خب حالا این یعنی چی ؟ مثلا بر فرض activity_main.xml اسم layout باشه پس اسم کلاسمون میشه :این کلاس تمام ویژگی های Binding مربوط به اون layout رو نگه میداره ساخت Bindingبرای تعریف بایندیگ تو اکتیویتی باید از طریق کلاس بایندینگ setContectView کنید به بهش layout رو بدید :ما توی layou یک متغییر داریم به نام user که از اون مدل استفاده میکنیم تا دیتا ست کنیم به view ها , برای دادن مدل user به layout :از این طریق ما مدل user رو به layout میدیم و میتونیم از اطلاعاتی که داخل این کلاس وجود داره استفاده کنیممدیریت کلیک هاما قبلا توی layout از  استفاده میکردیم و یک تابع داخل کلاسمون تعریف میکردیم و بهش میدادیم یا اینکه view رو داخل اکتیوتی find میکردیم و setOnClickListener  میساختیم ولی باید با همه اینا خداحافظی کنیم و بریم سمت تمیز کد زدن چی میشد اگه داخل اکتیویتی یه کلاس داشتی و همه کلیک هارو اونجا هندل میکردی ؟ خوبیش اینه خیلی تمیز تر میشه کد هات و سریع میتونی به تمام کلیک ها و اکشن های داخل طرح دسترسی داشته باشی , خب باید بگم که با بایندینگ میتونی چنین کاری کنی , چجوری ؟ اینجوری : داخل اکتیویتی یک کلاس تعریف میکنی : و داخل layout میای تو النمت data یه متغیر تعریف میکنی به اسم handlers و بهش ادرس اون کلاس رو میدی و از طریق اون هندلر میتونی به اون کلاس MyHandlers دسترسی پیدا کنی  :و داخل اکتیویتی binding رو setHandler میکنی و این کلاس رو بهش میدی :و یک نکته اینکه شما میتونید به وسیله id که به view ها میدید بهشون از طریق bindng بدون find کردن دسترسی داشته باشید : شما چطور فکر میکنید ؟ ایا شما هم از dataBinding استفاده میکنید ؟ خوبه به نظرتون یا بد ؟ تجربیاتتون رو کامنت کنید ! ممنون از توجه شما امیدوارم که تونسته باشه مفید واقع بشه , خواستم یکم ساده توضیح بدم که برای همه قابل درک باشه راستی ی سری به پست های قبلیم هم بندازید اگه نظری داشتید حتما کامنت کنید چون من اعتقاد دارم چیزی رو اموزش بدی خودت بهتر یاد میگیری و فیدبک شما بهم کمک بزرگی میکنه اگه برنامه نویس اندروید هستی و نیاز داری که اپت تو گوگل پلی قرار بگیره یه سر به این پست بزن : https://vrgl.ir/vMKTC  واگهبهکدنویسیتمیزعلاقهداریبهتپیشنهادمیکنمکهاینمیهنگاهبندازی:  https://vrgl.ir/KZWHV Ref : https://developer.android.com/topic/libraries/data-binding</description>
                <category>Behnam Nasehi | بهنام ناصحی</category>
                <author>Behnam Nasehi | بهنام ناصحی</author>
                <pubDate>Thu, 27 Aug 2020 14:43:16 +0430</pubDate>
            </item>
                    <item>
                <title>ساخت اکانت دولوپر گوگل | Google Developer Account</title>
                <link>https://virgool.io/@behnamnasehi/%D8%B3%D8%A7%D8%AE%D8%AA-%D8%A7%DA%A9%D8%A7%D9%86%D8%AA-%D8%AF%D9%88%D9%84%D9%88%D9%BE%D8%B1-%DA%AF%D9%88%DA%AF%D9%84-google-developer-account-wlajzkoqwmoo</link>
                <description>سلام من بهنام ناصحی هستم , چند وقت پیش بود که خواستم اکانت گوگل دولوپر بگیرم و حسابی درگیرش بودم و مقاله ای نتونستم پیدا کنم که کار من رو حل کنه واسه همین گفتم بیام تجربیات خودم رو اینجا قرار بدم نمیخوام کلی مقدمه بچینم و حوصله سر بر باشه پس حاشیه نمیرم و مستقیم میرم سر اصل مطلب :مرحله اول : سایت های واسط !  خرید اکانت دولوپر گوگل یا اپل نیازمند داشتن حساب ارزی و پرداخت ارزی است. امّا به دلیل وجود محدودیت ها، چنین کاری برای توسعه دهندگان مقدور نیست بله درسته از اونجایی که کل دنیا و همچنین برعکس ما با کل دنیا قهریم نمیتونیم به صورت مستقیم پرداخت کنیم و حساب باز کنیم چون تحریمیم ولی خب جای نگرانی نیست سایت هایی وجود داره که واسط بشن من از ایرانی کارت استفاده کردم و پشتیبانیشون واقعا عالی بود و کاملا پیگیر کار من بودن ثبت نام در ایرانی کارت  : ثبت نام بعد از ثبت نام از منو وارد سرویس ها بشید و اکانت دولوپر گوگل رو انتخاب کنید و سفارش خودتون رو ثبت و پرداخت کنید خب دیگه فعلا با ایرانی کارت کاری نداریم برسیم سروقت ساخت جیمیل جدیدمرحله دوم : ساخت جیمیلشاید با خودتون فکر کنید که مثل همیشه یه جیمیل میسازید و میدید به ایرانی کارت براتون دولوپر اکانتش رو فعال میکنن باید بگم نخیربرای ساخت جیمیل برای اکانت دولوپر نیاز دارید که شماره ای به غیر از ایران باشه سرتون رو در نمیارم بعد از کلنجار رفتن با سایت های مختلف فهمیدم بنا به تجربه کاربران شماره موبایل مجازی اوکراین از همه بهتره و گوگل مشکلی باهاش نداره ( توجه داشته باشید که شماره تلفن مجازی با شماره موبایل مجازی فرق داره )برای خرید شماره میتونید از این سایت استفاده کنید : سایت تلوبال و ازشون شماره موبایل مجازی کشور اوکراین رو بخرید : خرید شماره موبایل کشور اوکراینبعد از خرید شماره تلفن اگه اس ام اس داشته باشید به داشبورد تلوبال یا جیمیلی که ثبت نام کردید ارسال میشه خب وقتی شماره رو گرفتید فیلترشکن روشن کنید و به ایپی امریکا وصل شید پیشنهاد میکنم که از incognito کروم استفاده کنید و مطمئن شید که ایپی شما امریکاست میتونید از سایت های مختلفی برای گرفتن ایپی خودتون پیدا کنید : سایت شناسایی ایپی خب بعد از اینکه :ایپی شما امریکا بودشماره مجازی موبایل اوکراین گرفتیدکروم incognito رو باز کردید ( control + shift + n )حالا برید جیمیل بسازید با شماره اوکراین و بعد از اتمام ثبت نام جیمیل رو در اختیار پشتیبانی ایرانی کارت قرار بدیدمرحله سوم : ورود به کنسول گوگل پلی همیشه توجه داشته باشید که با یه ایپی ثابت وصل شید من پیشنهاد میکنم از وی پی  اس استفاده کنید و خب من با فیلترشکن ایپی امریکا کانکت میشم و تاحالا مشکلی پیش نیومده ( مسئولیتش با خودتون)نحوه ورود من به کنسول : فیلتر شکن کانکت میکن با ایپی امریکاتب کروم incognito رو باز میکنم ( control + shift + n ) با سایت تست ایپی مطمئن میشم ایپیم امریکاستورود میکنمسوالات شما !شماره مجازی که معرفی کردین باید ماهیانه 5 دلار داد.آیا لازمه هر ماه 5 دلار واریز کنیم؟بله به نظرم من نیازه چون این شماره به عنوان شماره پشتیبان به حساب جیمیل داده شده و اگه مشکلی پیش بیاد برای بازیابی به اون شماره نیازه و یا بعضی وقتا گوگل احساس خطر میکنه و یهو ازتون احراز هویت میخواد با شمارهبرای پرداخت 25 دلار از کارت فیزیکی استفاده کردین یا مجازی؟چون شنیدم اگه از مجازی استفاده کنیم بعد از مدتی حساب رو میبندن؟من از سایت های واسط استفاده کردم که بالا توضیح دادم iranicardمیتونیم بجای vps آمریکا از vps آلمان استفاده کنیم؟ببین اصلا به vps هم نیاز نیست من از vpn استفاده میکنم و تاحالا مشکلی برام پیش نیومده ولی فقط مهمه که ای پی ثابت باشه و من فقط شنیدم که باید حتما امریکا باشه تست نکردمآیا شما اکانت فیلترشکن خودتون رو در اختیار ایرانی کارت قرار دادین و با اون حساب رو ساختن؟یا اینکه اونها با یه آی پی دیگه ساختن و شما با یه آی پی دیگه وارد کنسول شدین؟نه شما جیمیل رو با رمز فقط در اختیار ایرانی کارت قرار میدیجیمیل رو با اسم واقعی خودمون باید بسازیم یا میشه از اسم فیک هم استفاده کرد؟اصلا مهم نیست هرجور که دلت میخواد میتونی بسازی فقط شماره خارج از ایران باشهوی پی ان رو از کجا بگیریم؟من http://fitnets.net رو پیشنهاد میکنمآیا میشه از این اکانت کسب درآمد کرد؟ منظورم admob و دادن شماره حساب و نامه تاییدیه نیست چون خیلی سخته.منظورم سایت های تبلیغاتی کشورهای دیگه مثل سایت appodeal که مال روسیه است که میشه تبلیغاتش رو توی اپ قرار داد و از طریق سایتش درآمد تبلیغات رو پرداخت میکنه.ببین من نمیتونم توی کاری که تاحالا انجام ندادم بهت راهنمایی اشتباه بدم ولی پیش نیاز خیلی از کارا همین حساب دولوپره باید پیش نیاز های اون سایت رو نگاه بندازی و ببینی که ایا به اکانت دولوپر گوگل نیاز هست یا نه ؟ </description>
                <category>Behnam Nasehi | بهنام ناصحی</category>
                <author>Behnam Nasehi | بهنام ناصحی</author>
                <pubDate>Mon, 13 Jul 2020 16:22:19 +0430</pubDate>
            </item>
                    <item>
                <title>روش صحیح ساختن Splash در اندروید</title>
                <link>https://virgool.io/MobileLab/%D8%B1%D9%88%D8%B4-%D8%B5%D8%AD%DB%8C%D8%AD-%D8%B3%D8%A7%D8%AE%D8%AA%D9%86-splash-%D8%AF%D8%B1-%D8%A7%D9%86%D8%AF%D8%B1%D9%88%DB%8C%D8%AF-pdavrszb8kej</link>
                <description>باید و نباید وجود Splash رو نمیخوام بحث کنم ولی همیشه باعث اعصاب خوردیم میشد که وقتی یک اپلیکیشن رو باز میکنم و هی صفحه Splash رو به روخ میکشه ! من میدونم چه اپی رو باز کردم فقط بزار ازش استفاده کنم !!!!!توسعه دهنده اندروید هستم این باعث شد که متوجه بشم که وقتی تو پروژه هام از Splash استفاده میکنم شاید همینقدر برای کاربر رو مخ باشه...گوگل چی میگه ؟ گوگل قبلا از Splash  حمایت میکرده ولی همیشه اینطور نبوده و حتی اون رو انتی پترن صدا زده. Splash به روش صحیح !از صفحه های Splash استفاده نکنید وقت کاربر رو هدر میده , خب پس چیکار کنیم ؟ دیدید وقتی اپلیکیشن رو باز میکنید یه چند ثانیه اول تاخیر داره و بعضی اوقات صفحه خالی نشون میده ؟  https://media.giphy.com/media/mks5DcSGjhQ1a/giphy.gif بووووم همینه !باید از موقعیت ها استفاده کنیم , چرا کاربر رو معطل کنیم ؟ وقتی چند ثانیه وقت داریم تا اپ باز بشه چرا همونجا کار رو یکسره نکنیم و 3 ثانیه دیگه به انتظارش اضافه نکنیم ؟ اگه به اپدیت های جدید اپ های گوگل نگاه کنید میبینید که همین کارو کردن مثلا YouTube :Splash YouTubeمقدار زمانی که شما صرف دیدن این صفحه میکنید دقیقا همون زمانیه که اپلیکیشن نیاز داره تا قشنگ لود بشه و در صورت لود شدن اپلیکیشن تقریبا بلافاصله از بین میره خب بریم سر اجرا اجرا ممکنه با اون چیزی که فک میکنید یکم متفاوت باشه چون صفحه باید سریع بالا بیاد حتی وقت این رو هم ندارید که توی SplashActivity صفحه رو inflate کنین !ما از layout استفاده نمیکنیم , بجاش بکگراند رو از Theme تنظیم میکنیم برای این کار یه فایل XML drawable در res/drawable ایجاد میکنیم :&lt;?xml version=&amp;quot1.0&amp;quot encoding=&amp;quotutf-8&amp;quot?&gt; 
&lt;layer-list xmlns:android=&amp;quothttp://schemas.android.com/apk/res/android&amp;quot&gt; 
    &lt;item
         android:drawable=&amp;quot@color/gray&amp;quot/&gt;
     &lt;item&gt; 
        &lt;bitmap
             android:gravity=&amp;quotcenter&amp;quot
             android:src=&amp;quot@mipmap/ic_launcher&amp;quot/&gt;
     &lt;/item&gt;
 &lt;/layer-list&gt;در اینجا ، من یک رنگ پس زمینه و یک تصویر تنظیم کرده ام.خب حالا برید داخل فایل Style.xml  و یک Theme جدید برای SplashActivity اضافه کنید :&lt;resources&gt;
    &lt;!-- Base application theme. --&gt;
    &lt;style name=&amp;quotAppTheme&amp;quot parent=&amp;quotTheme.AppCompat.Light.DarkActionBar&amp;quot&gt;
        &lt;!-- Customize your theme here. --&gt;
    &lt;/style&gt;
    &lt;style name=&amp;quotSplashTheme&amp;quot parent=&amp;quotTheme.AppCompat.NoActionBar&amp;quot&gt;
        &lt;item name=&amp;quotandroid:windowBackground&amp;quot&gt;@drawable/background_splash&lt;/item&gt;
    &lt;/style&gt;
&lt;/resources&gt;و حالا باید داخل AndroidManifest.xml به SplashActivity اتربیوت Theme رو اضافه کنید و اون Theme که ساختید رو بهش بدید :  &lt;activity
    android:name=&amp;quot.SplashActivity&amp;quot
    android:theme=&amp;quot@style/SplashTheme&amp;quot&gt;
    &lt;intent-filter&gt;
        &lt;action android:name=&amp;quotandroid.intent.action.MAIN&amp;quot /&gt;

        &lt;category android:name=&amp;quotandroid.intent.category.LAUNCHER&amp;quot /&gt;
    &lt;/intent-filter&gt;
&lt;/activity&gt;و در اخر باید SplashActivity باید به MainActivity فروارد بشه :public class SplashActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Intent intent = new Intent(this, MainActivity.class);
        startActivity(intent);
        finish();
    }
}توجه داشته باید که شما هیچ View برای SplashActivity نساختید و View از Theme میاد درست انجام دادیم :)فرقش با روش قدیمی اینه که وقتی نشون داده میشه که اپ داره کانفیگ میشه و اگر از روش قدیمی استفاده کنید بعد از کانفینگ اپ بالا میاد برفرض اینکه کانفیگ اپ شما 2 ثانیه باشه :روش قدیمی : شما 2 ثانیه هم به Splash میدید و وقتی اپ رو باز میکنید دو ثانیه کانفیگ میشه و بعد 2 ثانیه Splash شمارو نشون میده.روش جدید : در همون 2 ثانیه کانفیگ Splash شما نشون داده میشه و هیچ وقتی از کاربر نمیگیره انقدر کاربرا رو اذیت نکنید ! امیدوارم که تونسته باشم کمکتون کنم اگه جایی اشتباه کردم اول معذرت میخوام دوم لطفا بهم بگید تا ویرایش کنم !راستی پست قبلی منو خوندید ؟ یه سری بهش بزنید  http://vrgl.ir/KZWHV  https://vrgl.ir/vMKTC Ref :- https://www.bignerdranch.com/blog/splash-screens-the-right-way/- https://medium.com/@shishirthedev/the-right-way-to-implement-a-splash-screen-in-android-acae0e52949a</description>
                <category>Behnam Nasehi | بهنام ناصحی</category>
                <author>Behnam Nasehi | بهنام ناصحی</author>
                <pubDate>Mon, 06 Apr 2020 14:45:33 +0430</pubDate>
            </item>
                    <item>
                <title>یک قرارداد نام گذاری XML موفق | ایجاد نظم در پروژه</title>
                <link>https://virgool.io/@behnamnasehi/%DB%8C%DA%A9-%D9%82%D8%B1%D8%A7%D8%B1%D8%AF%D8%A7%D8%AF-%D9%86%D8%A7%D9%85-%DA%AF%D8%B0%D8%A7%D8%B1%DB%8C-xml-%D9%85%D9%88%D9%81%D9%82-%D8%A7%DB%8C%D8%AC%D8%A7%D8%AF-%D9%86%D8%B8%D9%85-%D8%AF%D8%B1-%D9%BE%D8%B1%D9%88%DA%98%D9%87-ku9dv0nf7jjw</link>
                <description>آخرین باری که مجبور شدید فایل string.xml باز کنید و به سختی و غرزنان دنبال ایدی String مورد نظر خودتون بگردید کی بوده ؟ یا اینکه تک تک همه drawable باز کنید و اونی که مدنظرتونه رو پیدا کنید ؟ هروقت که پروژه جدید شروع کردید خیلی مواظب architecture, CI, build flavors و و و ... بودید ولی آیا استراتژی مناسبی برای نام گذاری resources داشتید ؟ باید داشته باشید ! نبود نام مناسب باعث میشه که در مدیریت پروژتون خسته بشید با نام گذاری درست میتونید راحت پروژه کنترل کنید مخصوصا در پروژه های بزرگ ! اصل اساسیهمه resources  از یه قرداد ساده پیروی میکنن : &lt;WHAT&gt;   _   &lt;WHERE&gt;   _   &lt;DESCRIPTION&gt;   _   &lt;SIZE&gt;بیایم اول در مورد تک تک اینا صحبت کنیم&lt;WHAT&gt;هموطوری که از اسمش پیداست نشان میده که دقیقا resources چه چیزی هستن ؟ معمولا Android View Class برای مثال : e.g. MainActivity -&gt; activiy&lt;WHERE&gt;توضیحی درباره اینکه از لحاظ منطقی در کجای اپ شما قرار دارد اگر در جاهای مختلفی استفاده میکنید از all و در غیر این صورت از اسم کلاسی که داخلش ازش استفاده میشه استفاده کنیدبرای مثال : e.g. MainActivity -&gt; mainArticleDetailFragment -&gt; articledetail&lt;DESCRIPTION&gt;یه توضیحی تقریبا یک کلمه برای تفکیک عناصر در یک صفحه مثال : e.g. title&lt;SIZE&gt; (optional)این قسمت اختیاری و معمولا برای سایز دهی استفاده میشه مثال : 24dpsmallچندتا مثال Layouts (&lt;WHAT&gt;&lt;WHERE&gt;.XML): activity_main.xml Strings (&lt;WHERE&gt;&lt;DESCRIPTION&gt;): main_introall_doneDrawables (&lt;WHRE&gt;&lt;DESCRIPTION&gt;&lt;SIZE&gt;):main_backgroundall_infoicon_smallID (&lt;WHAT&gt;&lt;WHERE&gt;&lt;DESCRIPTION&gt;):linearlayout_main_fragmentcontaineمزایاقسمت WHERE توصیف می کند که کدام صفحه از یک منبع متعلق به آن است. از این رو ، به راحتی میتونید همه IDs, drawables, dimensions را برای یک صفحه خاص دریافت کنید.قسمت WHAT در ایدی منابع ها نام کلاسی که Xml متعلق به ان هست را توصیف میکند پس در findViewById() برای شما راحت تر که چه چیزی را صدا بزنید فایل هایی که در اندروید استودیو لیست میشوند معمولا بر اساس الفبا ترتیب بندی میشوند و با رعایت این نوع قرار داد نامگذاری یک لیست منظم از فایل هایتان دارید و برایتان پیدا کردن فایل مدنظرتون بسیار اسان میشود نام های منابع بسیار قابل پیشبینی شده و استفاده از autocomplete اندروید استودیو بسیار اسان میشودبه طور کلی تمام منابع منطقی نام گذاری میشوند و این امر باعث میشود پروژه تمیز تری داشته باشید چندتا مثال میزنم براتون از چند قسمت پروژه :1. Layoutsمعمولا در پروژه ها ما برای هر اسکرینی یک Layout دارم پس در نتیجه خیلی ساده ست نحوه نام گذاریش :WHAT_WHERE.XMLWhat معمولا یکی از موارد زیر است :activity = content view for activityfragment = view for a fragmentview = inflated by a custom viewitem = layout used in list/recycler/gridviewlayout = layout reused using the include tagمثال : activity_main: content view of the MainActivityfragment_articledetail: view for the ArticleDetailFragmentview_menu: layout inflated by custom view class MenuViewitem_article: list item in ArticleRecyclerViewlayout_actionbar_backbutton: layout for an actionbar with a backbutton (too simple to be a customview)2. Stringsدر String ها کلمه what نامربوطه پس بجاش از Where برای نشان دادن محل استفاده , استفاده میکنیم : &lt;WHERE&gt;_&lt;DESCRIPTION&gt;و یا برای استفاده از String در کل برنامه : all_&lt;DESCRIPTION&gt;مثال : articledetail_title: title of ArticleDetailFragmentfeedback_explanation: feedback explanation in FeedbackFragmentfeedback_namehint: hint of name field in FeedbackFragmentall_done: generic “done” string3. Drawables این هم مانند String ها از Where استفاده میکنیم :&lt;WHERE&gt;_&lt;DESCRIPTION&gt;_&lt;SIZE&gt;و یا برای استفاده در کل برنامه :all_&lt;DESCRIPTION&gt;_&lt;SIZE&gt;به صورت اختیاری میتوانید یک آرگومان Size اضافه کنید که یا میتواند به صورت 26 باشد یا largeمثال : articledetail_placeholder: placeholder in ArticleDetailFragmentall_infoicon: generic info iconall_infoicon_large: large version of generic info iconall_infoicon_24dp: 24dp version of generic info icon4. IDsبرای ID کلمه What نام کلاسی است که به آن تعلق دارد و Where اسکرینی است که در ان قرار دارد و میتوانید برای ID هایی که مشابه هستند میتوانید DESCRIPTION هم به صورت اختیاری اضافه کنید : &lt;WHAT&gt;_&lt;WHERE&gt;_&lt;DESCRIPTION&gt;مثال : ablayout_main -&gt; TabLayout in MainActivityimageview_menu_profile -&gt; profile image in custom MenuViewtextview_articledetail_title -&gt; title TextView in ArticleDetailFragment5. Dimensionsپروژه باید مجموعه محدودی از dimensions را تعریف کنید که مرتبا مورد استفاده قرار میگیرد که این باعث میشه کلمه all به طور پیشفرض در نامگذاری باشد : &lt;WHAT&gt;_all_&lt;DESCRIPTION&gt;_&lt;SIZE&gt;برای جاهای خاص میتوانید از Where هم استفاده کنیدکلمه WHAT در dimensions  معمولا یکی از موارد زیر است البته توجه داشته باشید که لیست پایین شامل بیشترین موارد استفاده شده از کلمه What است : height_toolbar: height of all toolbarskeyline_listtext: listitem text is aligned at this keylinetextsize_medium: medium size of all textsize_menu_icon: size of icons in menuheight_menu_profileimage: height of profile image in menuنکات مهم صفحه ها باید اسامی منحصر به فرد داشته باشندبرای جلوگیری از مشکل در آرگومان Where  کلاس های شما باید اسم های منحصر به فرد داشته باشند پس در نتیجه شما نمیتوانید MainActivity و MainFragment داشته باشید چون پیشوند main قبلا مورد استفاده قرار گرفته ساپورت نکردن Refactoringبا عوض کردن اسم کلاس اسامی که در resource ها قرار تغییر نمیکنند پس اگر شما کلاس MainActivity را به SecondActivity تغییر دادید layout activity_main به activity_second تغییر نخواهد کرد امیدواریم که یه روزی اندروید استودیو چنین ویژگی را اضافه کنه :)همه نوع resource ساپورت نمیشناین قرار داد برای همه resource نیست و بعضی ها جایی توی این قرار داد ندارند مانند: Raw Assetsممنون که وقتتون رو در اختیار من گذاشتید :)بنظرتون خوب نیست که همه روی این قرار داد تمرکز کنیم و در پروژه هامون استفاده کنیم که تقریبا یه زبان مشترک بین ما بشه تا بتونیم راحت تر کد هارو ری ویو کنیم ؟ ؟ نظرتون برام خیلی مهمهاگه هم جایی اشتباه کردم لطفا بهم بگید تا ویرایش کنم...Ref :https://jeroenmols.com/blog/2016/03/07/resourcenaming/مقاله ی دیگه هم نوشتم برای اینه بتونی متوجه شی چه موقعی کیبوردت باز شده ! http://vrgl.ir/bA5Ti </description>
                <category>Behnam Nasehi | بهنام ناصحی</category>
                <author>Behnam Nasehi | بهنام ناصحی</author>
                <pubDate>Sun, 05 Apr 2020 21:21:15 +0430</pubDate>
            </item>
                    <item>
                <title>چجوری بفهمیم کیبورد باز شده ؟ | Keyboard Listener</title>
                <link>https://virgool.io/@behnamnasehi/%DA%86%D8%AC%D9%88%D8%B1%DB%8C-%D8%A8%D9%81%D9%87%D9%85%DB%8C%D9%85-%DA%A9%DB%8C%D8%A8%D9%88%D8%B1%D8%AF-%D8%A8%D8%A7%D8%B2-%D8%B4%D8%AF%D9%87-keyboard-listener-cbz4k6kceky2</link>
                <description>سلام دوستان تو این مطلب قصد دارم یه KeyboardListener اموزش بدم که وقتی کیبورد باز شد همیشه گوش به زنگ باشه و بهتون اعلام کنه ViewTreeObserverA view tree observer is used to register listeners that can be notified of global changes in the view treeبرای اعلام هرگونه تغییر بر روی View استفاده میشودو زیر مجموعه های خیلی زیادی دارد که ما از اینترفیس OnGlobalLayoutListener  استفاده خواهیم کرد به این دلیل که اگر شما از ViewTreeObserver استفاده نکنید ، و از mainLayout.getRootView (). getHeight()استفاده کنید به سادگی 0px را برمی گرداند ، زیرا هنوز تنظیم نشده است. پس شما منتظر میمانید View اندازه گیری شود , نمایش داده شود و بعد عرض و ارتفاع ان را میگیرید.برای شروع تابع زیر را مینویسیم  و Activity را به عنوان پارامتر به تابع میدهیم تا از طریق اون بتوانیم بدون نیاز به دانستن ایدی به  Root آن Activity دسترسی داشته باشیم :public static void OnkeyBoardShowListener(final Activity activity) {
        final View activityRootView = activity.findViewById(android.R.id.content);
}بعد از گرفتن Root اکتیویتی مورد نظر حال با استفاده از آن میتوانیم ViewTreeObserver را صدا زده :public static void OnkeyBoardShowListener(final Activity activity) {
   final View activityRootView = activity.findViewById(android.R.id.content);
   activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new 
   ViewTreeObserver.OnGlobalLayoutListener() {
    @Override
     public void onGlobalLayout() {

     }
    });
 }در تابع onGlobalLayout میتوانیم ارتفاع و عرض Root را بگیریم و از آن استفاده بکنیم ولی مشکل اینجاست که چطور بفهمیم کیبورد باز شده است ؟ از طریق Rect میتوانیم یک مستطیل بسازیم و با استفاده از root.getWindowVisibleDisplayFrame(Rect rect);میتوانیم فضای خالی View را در اختیار داشته باشیمپس : public static void OnkeyBoardShowListener(final Activity activity) {
    final View activityRootView = activity.findViewById(android.R.id.content);    
    activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new     
    ViewTreeObserver.OnGlobalLayoutListener() {
     @Override
      public void onGlobalLayout() {
         Rect rect = new Rect();
         activityRootView.getWindowVisibleDisplayFrame(rect);
       }
     });
  } باید ارتفاع Root را بگیریم و با استفاده از Rect اختلاف فضای خالی Root وقتی کیبورد باز میشود را اندازه گیری کنیم و با در اختیار داشتن اختلاف ارتفاع میشود تقریبا متوجه باز شدن کیبورد شد .public static void OnkeyBoardShowListener(final Activity activity) {
    final View activityRootView = activity.findViewById(android.R.id.content);    
    activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new     
    ViewTreeObserver.OnGlobalLayoutListener() {
     @Override
      public void onGlobalLayout() {
         Rect rect = new Rect();
         activityRootView.getWindowVisibleDisplayFrame(rect);
         int heightRoot = activityRootView.getRootView().getHeight();
         nt heightDiff = heightRoot - rect.bottom;
       }
     });
  }heightRoot  : ارتفاع کلheightDiff : اختلاف ارتفاعاز این به بعد دیگه بستگی به استفاده خودتان داره که به چه نحوی میخواهید استفاده کنید توجه داشته باشید که heightRoot  و heightDiff  خروجی px به شما میدهند پس : public static void OnkeyBoardShowListener(final Activity activity) {
    final View activityRootView = activity.findViewById(android.R.id.content);    
    activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new     
    ViewTreeObserver.OnGlobalLayoutListener() {
     @Override
      public void onGlobalLayout() {
         Rect rect = new Rect();
         activityRootView.getWindowVisibleDisplayFrame(rect);
         int heightRoot = activityRootView.getRootView().getHeight();
         nt heightDiff = heightRoot - rect.bottom;
         if (heightDiff &gt; dpToPx(activity, 200)) {
             // ادامه کد بستگی به نوع استفاده شما داره
         } else if (heightDiff &lt; dpToPx(activity, 200)) {
            // ادامه کد بستگی به نوع استفاده شما داره
         }
       }
     });
  }تابع برای تبدیل Dp به Px private static float dpToPx(Context context, int dp) {
        DisplayMetrics metrics = context.getResources().getDisplayMetrics();
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, metrics);
 }امیداورم کمک کوچیکی کرده باشم به شما دوستای عزیزم 3&gt; IG : www.instagram.com/behnamnasehiGmail : behnammnasehi@gmail.comما برنامه نمی نویسیم , ما آینده مینویسیم :) https://virgool.io/@behnamnasehi/custom-snack-bar-%D8%B3%D9%81%D8%A7%D8%B1%D8%B4%DB%8C-%D8%B3%D8%A7%D8%B2%DB%8C-%D8%A7%D8%B3%D9%86%DA%A9-%D8%A8%D8%A7%D8%B1-rtm98vrjegmt </description>
                <category>Behnam Nasehi | بهنام ناصحی</category>
                <author>Behnam Nasehi | بهنام ناصحی</author>
                <pubDate>Wed, 15 Jan 2020 15:06:08 +0330</pubDate>
            </item>
                    <item>
                <title>Custom Snack Bar | سفارشی سازی اسنک بار</title>
                <link>https://virgool.io/@behnamnasehi/custom-snack-bar-%D8%B3%D9%81%D8%A7%D8%B1%D8%B4%DB%8C-%D8%B3%D8%A7%D8%B2%DB%8C-%D8%A7%D8%B3%D9%86%DA%A9-%D8%A8%D8%A7%D8%B1-rtm98vrjegmt</link>
                <description>سلام دوستان امیدوارم با این همه اتفاقات حال دل همتون خوب و غمتون کم باشه !21/11/1398نمیخوام زیاد وقتتون رو با مقدمه و این حرفا بگیرم چون هم وقت طلاست هم اینکه شما نمیخونید :)اسنک بار چیست ؟ | What is SnackBar اسنک بار میشه گفت همون Toast با امکانات بیشتر و اجرای روان‌تر که از اندروید ۵ به بعد معرفی شد و توی طراحی های متریال فوق العاده پرکاربردهاسنک‌بار در پایین صفحه نمایش (پیشفرض) داده می‌شه و شامل یک متن و یک دکمه عملیات (اختیاری) هست. کاربر می‌تونه روی دکمه عملیات  کلیک کنه یا اون رو به طرفین بکشه تا از روی صفحه محو بشه. اگه کاربر هیچکدوم از این کارها را نکنه، بعد از مدتی اسنک‌بار خود به خود محو می‌شه.اسنک بار ساده !قدم اول : فایل build.gradle پروژتون رو باز کنید و کد زیر رو بهش اضافه کنید .dependencies {
compile fileTree(dir: &#039;libs&#039;, include: [&#039;*.jar&#039;])
compile &#039;com.android.support:appcompat-v7:23.0.1&#039;
compile &#039;com.android.support:design:23.0.1&#039;
}قدم دوم : کد زیر Snackbar ای میسازه که فقط یک متن رو نمایش میده .Snackbar snackbar = Snackbar.make(rootView, &amp;quotThis is our Snackbar Sample !!&amp;quot, Snackbar.LENGTH_LONG);
snackbar.show();تابع make سه پارامتر به عنوان ورودی میگیره : پارامتر اول : view مادر (لایوت نمایش دهنده اسنک بار) که قراره توی اون Snackbar رو نمایش بدیم پارامتر دوم : متنی هست که میخوایم نمایش بدیمسومین پارامتر : مدت زمانی هست که قراره snackbar به کاربرانمون نشون داده بشه : LENGTH_LONG : مدت زمان زیاد LENGTH_SHORT : مدت زمان کوتاه LENGTH_INDEFINITE : تا زمانی که به وسیله کشیدن از صفحه خارج نشه ، نشون داده خواهد شد .نکته : در مستندات گوگل توصیه شده که برای view اصلی (مادر) که قراره snackbar در اون نمایش داده بشه  و به عنوان پارامتر اول به تابع make ارسال بشه از CoordinatorLayoutاستفاده کنید ، تا تمام قابلیت‌های پیش فرض snackbar مثل کشیدن برای از بین بردن ( swipe dissmiss ) و خارج شدن اتوماتیک از کنار صفحه قابل استفاده باشهAction Button : خب حالا فرض کنید به وسیله snackbarمیخوایم از کاربرامون یک تاییدیه بگیریم.Snackbar snackbar = Snackbar.make(coordinatorLayout, &amp;quotMessage is deleted&amp;quot, Snackbar.LENGTH_LONG).setAction(&amp;quotUNDO&amp;quot, new View.Listener() {
@Override
public void (View view) {
Snackbar snackbar1 = Snackbar.make(coordinatorLayout, &amp;quotMessage is restored!&amp;quot, Snackbar.LENGTH_SHORT);
snackbar1.show();
}
});

snackbar.show();توی بالا با استفاده از تابع setAction یک عملیات رو برای snackbar مون تعریف میکنیم تابع setAction دوتا ورودی میگیره ، اولی اسمی هست که میخوایم برای عملیاتمون نمایش بدیم و دومی یک Listener .Simple Snack Bar سفارشی سازی خب وقتی مراحل بالا رو برید متوجه میشید که اسنک بارتون خیلی سادست و اصلا شبیه اون چیزی که توی توییتر و یا جیمیل ( یا جاهای دیگه ) نیست Custom Snack Bar ( Twitter )مثلا رنگ دلخواهتون نیست و یا گوشه هاش خمیده نیست ( corner radius ) یا کاملا چسبیده به اطراف...برای این کار یه کلاس میسازم به اسم SnackBarHelper :public class SnackBarHelper {

      public static void configSnackbar(Context context, Snackbar snack) {
      
      }
     
}برای این کلاس یک کانستراکتور میسازم که بتونم Context و  SnackBar رو بگیرم و روشون تغییرات رو اعمال کنم قدم اول برای اضافه کردن Elevation به اسنک بارم میتونم توی کانستراکتورم این خط کد رو اعمال کنم :public class SnackBarHelper {

       public static void configSnackbar(Context context, Snackbar snack) {
      ViewCompat.setElevation(snack.getView(), 6f);
      }
      
}که دوتا پارامتر میگیره : اول : ویو اسنک بار مادوم : مقدار Elevationدر ادامه میخوام چند تابع ( به نظرم من پرکاربرد ترینشون ) رو براتون بگم که تو این کلاس شما میتونید بعدا هرچی خواستین اضافه کنید :تابع اول ( راست چین کردن | Set To Rtl ) این تابع برای فارسی زبان ها بیشتر کاربرد داره وقتی اسنک بار میسازید مسیج داخلش چپ چینه و برای فیکس کردن این موضوع از تابع زیر استفاده میکنیم:private static void setToRtl(Snackbar snackbar) {
         ViewCompat.setLayoutDirection(snackbar.getView(), 
        ViewCompat.LAYOUT_DIRECTION_RTL);
 }تابع دوم ( فاصله دادن از اطراف | Set Margin ) وقتی اسنک بار میسازید کلا چسبیده به اطراف و هیچ فاصله ای ( margin ) نداره برای فاصله دادن از اطراف از تابع زیر استفاده میکنیم :private static void addMargins(Snackbar snack) {
         ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) 
         snack.getView().getLayoutParams();
         params.setMargins(12, 12, 12, Function.snackMargin);  // میتونید مقدار مارجین ها رو به مقدار دلخواه تغییر بدید
         snack.getView().setLayoutParams(params);
} اون پارامتری که من نوشتم Function.snackMargin یه متغییریه که با تابعی که نوشتم و در اخر توضیح میدم متوجه میشه که کیبورد باز شده یا نه چون اسنک پشت کیبورد میره و معلوم نمیشه و ارتفاع کیبورد رو میگیره و به عنوان مارجین پایین بهش ست میشه .تابع سوم ( Set Drawable ) توی این تابع شما میتونید هر drawable بهش بدید تا کاستومایز شما بشه این Drawable منه : &lt;?xml version=&amp;quot1.0&amp;quot encoding=&amp;quotutf-8&amp;quot?&gt;
&lt;shape xmlns:android=&amp;quothttp://schemas.android.com/apk/res/android&amp;quot
android:shape=&amp;quotrectangle&amp;quot&gt;
     &lt;solid android:color=&amp;quot#323232&amp;quot /&gt;
     &lt;corners android:radius=&amp;quot4dp&amp;quot /&gt;
&lt;/shape&gt;و این هم تابع : private static void setDrawable (Context context, Snackbar snackbar) {
    
    if (Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.LOLLIPOP) {
    snackbar.getView().setBackground(context.getDrawable(R.drawable.contianer_snackbar));
    }
    
}تابع چهارم ( تغییر فونت  | Set Type Face) معمولا ما از فونت های خاص خودمون توی اپلیکیشن ها استفاده میکنیم خب اسنک بارمون هم که نمیشه فونت پیشفرض داشته باشه , برای اعمال فونت خاص خودمون از تابع زیر استفاده کنید : private static void setTypeFace(Context context, Snackbar snackbar) {
    TextView tv = 
    (snackbar.getView()).findViewById(com.google.android.material.R.id.snackbar_text);
    TextView snackbarActionTextView = 
    snackbar.getView().findViewById(com.google.android.material.R.id.snackbar_action );
    Typeface font = 
    Typeface.createFromAsset(context.getAssets(),&amp;quotfonts/IranSansRegular.ttf&amp;quot);
    Typeface bold =Typeface.createFromAsset(context.getAssets(), 
    &amp;quotfonts/IranSansMedium.ttf&amp;quot);
    tv.setTypeface(font);
   snackbarActionTextView.setTypeface(bold);
   }تابع پنجم (  تغییر رنگ دکمه اسنک بار )وقتی برای اسنک بار دکمه میزارید بهتره که رنگشو عوض کنید تا کاربر متوجه بشه این فقط تکست نیست و یک عملی انجام میده :private static void changeActionTextColor(Snackbar snackbar , Context context) {
     snackbar.setActionTextColor(context.getResources().getColor(R.color.colorWhite))
}کد نهایی :  و همه تابع ها رو توی کانستراکتور صدا میزنیم :public class SnackBarHelper {

public static void configSnackbar(Context context, Snackbar snack) {
        addMargins(snack);
        setRoundBordersBg(context, snack);
        setTypeFace(context, snack);
        setToRtl(snack);
        ViewCompat.setElevation(snack.getView(), 6f);
        changeActionTextColor(snack , context);
}
private static void setToRtl(Snackbar snackbar) {
       ViewCompat.setLayoutDirection(snackbar.getView(), 
        ViewCompat.LAYOUT_DIRECTION_RTL);
}

private static void changeActionTextColor(Snackbar snackbar , Context context) {      snackbar.setActionTextColor(context.getResources().getColor(R.color.colorWhite))
 }
 
 private static void addMargins(Snackbar snack) {
        ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) 
        snack.getView().getLayoutParams();
        params.setMargins(12, 12, 12, Funcations.SnackMargin);
        snack.getView().setLayoutParams(params);
}

private static void setRoundBordersBg(Context context, Snackbar snackbar) {
       if (Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.LOLLIPOP) {     
 snackbar.getView().setBackground(context.getDrawable(R.drawable.contianer_snackbar));
        }
  }
  
  private static void setTypeFace(Context context, Snackbar snackbar) {
        TextView tv =(snackbar.getView()).findViewById(com.google.android.material.R.id.snackbar_text);
        TextView snackbarActionTextView = snackbar.getView().findViewById(com.google.android.material.R.id.snackbar_action );
        Typeface font = Typeface.createFromAsset(context.getAssets(), &amp;quotfonts/IranSansRegular.ttf&amp;quot);
        Typeface bold = Typeface.createFromAsset(context.getAssets(), &amp;quotfonts/IranSansMedium.ttf&amp;quot);
         tv.setTypeface(font);
         snackbarActionTextView.setTypeface(bold);
         }
 }My Final Snack Barتابع Keyboard Listenerاز این تابع که یکمی بالاتر ازش حرف زدم برای باز بودن یا نبودن کیبورد استفاده میکنم که بتونم مقدار ارتفاعشو به عنوان مارجین پایین ست کنم به اسنک بارم : public static boolean isKeyboardHidden = false;
public static int SnackMargin = 12;

public static void keyBoardListener(final Activity activity) {
        final View activityRootView = activity.findViewById(android.R.id.content);
        activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new 
        ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
                Rect rect = new Rect();
                activityRootView.getWindowVisibleDisplayFrame(rect);
                int heightRoot = activityRootView.getRootView().getHeight();
                int heightDiff = heightRoot - rect.bottom;
                
                if (heightDiff &gt; dpToPx(activity, 200)) {
                
                     if (!isKeyboardHidden) {
                          isKeyboardHidden = true;
                          SnackMargin = heightDiff + 15;
                        }
                        
                 } else if (heightDiff &lt; dpToPx(activity, 200)) {
                         if (isKeyboardHidden) {
                          isKeyboardHidden = false;
                          SnackMargin = 12;
                       }
                 }
           }
            });
    }این تابع رو تو  onCreate هر اکیتیوتی که قرار توش اسنک بار رو استفاده کنم صدا میزنم و بعد از متغییر snackMargin استفاده میکنم تا مارجین پایین رو ست کنم و اسنک بیاد بالای کیبورد و در نهایت برای اعمال تغییرات روی اسنک بارم از کد زیر استفاده میکنم : Snackbar snack = Snackbar.make(
       activity.findViewById(android.R.id.content),
       message,
       Snackbar.LENGTH_LONG
 );
 SnackBarHelper.configSnackbar(activity, snack);
 snack.show();امیداورم که تونسته باشم دانش کوچیکم رو به شما دوستان انتقال داده باشم 3&gt;اگه جایی اشتباهی کردم یا گنگ بوده هم معذرت میخوام و هم من در خدمت شما هستم !</description>
                <category>Behnam Nasehi | بهنام ناصحی</category>
                <author>Behnam Nasehi | بهنام ناصحی</author>
                <pubDate>Sat, 11 Jan 2020 11:24:15 +0330</pubDate>
            </item>
                    <item>
                <title>یکی از روش های تغییر فونت در اندروید</title>
                <link>https://virgool.io/@behnamnasehi/%D8%AA%D8%BA%DB%8C%DB%8C%D8%B1-%D9%81%D9%88%D9%86%D8%AA-%D8%AF%D8%B1-%D8%A7%D9%86%D8%AF%D8%B1%D9%88%DB%8C%D8%AF-mqpudjheqvys</link>
                <description>سلام...راه های زیادی برای تغییر فونت دلخواه (Custom Font) در اندروید وجود داره؛ در اینجا میخوام یکی از روش رو بهتون بگم امیدوارم مفید باشه براتون : یه دایرکتوری به نام assets  در مسیر App &gt; Src &gt; Main ایجاد کنید و داخل assets یک دایرکتوری دیگه با نام font ایجاد کنید ، فونت هایی که میخواید استفاده کنید رو داخلش کپی کنید.( ایجاد دایرکتوری font داخل پوشه assets برای نظم دهی بهتر پروژه هست و پیشنهاد میشه حتما این کارو انجام بدید و اجبار به ساختن این پوشه نیست چون اگه داخل پوشه assets هم فونت ها رو کپی کنید قابل استفاده هستن).من خودم از این روش استفاده میکنم و باعث میشه که بعد از یک بار Run گرفتن در Preview که در قسمت XML قرار داره فونت قابل رویت باشه .اول از همه یه کلاس با نام دلخواه ایجاد میکنیم ، من از اسم View بعلاوه نوع فونت استفاده کردم  مثلا :EditText + Yekanbakh_bold = EditTextBold در مرحله بعد این کلاس رو extend میکنیم به AppCompat + View ؛ این View بستگی به نوع View داره که دارین استفاده میکنید میتونه Button باشه EditText , TextView  و و و ... public class EditTextMedium extends AppCompatEditText {وقتی که extend کردین یک اروری میده برای ساختن کانستراکتور روی کلاس کلیک کنید و alt +Enter بزنید کانستراکتورهایی که میخواد رو اور راید کنید:سه تا کانستراکتور ساخته میشه که به این صورت هستن :public class EditTextMedium extends AppCompatEditText {
    public EditTextMedium(Context context) {
        super(context);
    }

    public EditTextMedium(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public EditTextMedium(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
}توی هر 3 تا کاسنتراکتور یک typeFace ایجاد کنید از فونتی دلخواهتون :Typeface face = Typeface.createFromAsset(context.getAssets(), &quot;fonts/YekanBakh_Medium.ttf&quot;);کد نهایی :public class EditTextMedium extends AppCompatEditText {
    public EditTextMedium(Context context) {
        super(context);
        Typeface face = Typeface.createFromAsset(context.getAssets(), 
        &quot;fonts/YekanBakh_Medium.ttf&quot;);
        this.setTypeface(face);
    }

    public EditTextMedium(Context context, AttributeSet attrs) {
        super(context, attrs);
        Typeface face = Typeface.createFromAsset(context.getAssets(), 
       &quot;fonts/YekanBakh_Medium.ttf&quot;);
        this.setTypeface(face);
    }

    public EditTextMedium(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        Typeface face = Typeface.createFromAsset(context.getAssets(), 
        &quot;fonts/YekanBakh_Medium.ttf&quot;);
        this.setTypeface(face);
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
    }
}خب حالا باید تو قسمت XML  از این تگ کاستوم شده استفاده کرد :Package Name + Directory + Class Nameور در نهایت : خواستم یکم عمومی تر صحبت کنم کنم که کسایی که تازه وارد هستن هم متوجه بشن امیدوارم تونسته باشم کمکتون کنم.</description>
                <category>Behnam Nasehi | بهنام ناصحی</category>
                <author>Behnam Nasehi | بهنام ناصحی</author>
                <pubDate>Sat, 17 Aug 2019 11:23:28 +0430</pubDate>
            </item>
            </channel>
</rss>