استایل (Style) و تم (Theme) متریال دیزاین در اندروید

به نام خدا. در جلسه قبل با مفهوم کلی متریال دیزاین آشنا شدیم. یکی از موارد کلیدی در ساخت رابط کاربری، قابلیت تعریف Style (استایل) است.

استایل (Style) چیست؟

اگر با مفاهیم طراحی وب آشنایی دارید حتما با دیدن واژه استایل به یاد CSS می افتید. بله! در اندروید هم ما با همین ویژگی سروکار داریم. با این تفاوت که اینجا در قالب xml تعریف شده است.
فرض کنید در اپلیکیشن ما ۴ اکتیویتی وجود دارد که در هرکدام یک دکمه قرار داده ایم. می خواهم رنگ پس زمینه، رنگ و اندازه متن، فاصله متن دکمه از حاشیه و… در هر ۴ دکمه یکسان باشد. خب احتمالا اولین راهی که به ذهنتان می رسد این است که برای تک تک دکمه ها، این خواص را به صورت جداگانه درون هر اکتیویتی و داخل تگ مربوط به دکمه تعریف کنم. مشکلی نیست! اما وقتی به دردسر می افتم که تصمیم بگیرم در این دکمه ها تغییراتی ایجاد کنم. مثلا رنگ پس زمینه فعلی دکمه ها سبز است. حالا نظرم عوض شده و می خواهم آنرا به بنفش تغییر دهم. باید به سراغ تک تک اکتیویتی ها رفته و رنگ جدید را برای هر ۴ دکمه جایگزین مقدار قبلی کنم. علاوه بر اینکه وقت زیادی می گیرد ممکن است یک مورد را از قلم بیندازم و آنرا اصلاح نکنم. شاید برای ۴ دکمه این اتلاف زمان محسوس نباشد یا احتمال خطا و فراموشی در حد پایینی باشد. اما در یک پروژه پیچیده و دارای صفحات و عناصر مختلف، باید آماده بروز این خطاها باشیم. پس لازم است از استایل بهره ببریم. در استایل یکبار ویژگی ها و خواص مدنظر را تعریف کرده و بی نهایت مرتبه در سراسر پروژه و اپلیکیشن خود آنرا بکار می بریم. با هر ایجاد تغییر در استایل، این تغییر در سراسر پروژه و در تمامی عناصر/ ویجت/ کنترل هایی که به آن ربط داده شده، اعمال خواهد شد.

تم (Theme) چیست؟

تم (قالب) همان استایل است که در کل اپلیکیشن بکار می رود. مثلا در تم تعریف می کنیم رنگ منوی اپلیکیشن (تولبار) خاکستری باشد. با افزودن این ویژگی به تم، رنگ موردنظر در تمام اکتیویتی ها اعمال شده و نیاز نیست استایل مدنظر را در هر اکتیویتی تعریف کنیم.
احتمالا تعاریف ذکر شده برای شما تا حدودی نامفهوم است. جای نگرانی نیست. یک پروژه ایجاد می کنم و به صورت عملی و در قالب مثال توضیح می دهم.
من یک پروژه با نام Style و MinSDK 16 ساختم. اگر بخاطر داشته باشید در بخش قبل اشاره شد که متریال دیزاین از اندروید ۵٫۰ (Lollipop) اضافه شده و برای سازگاری نسخه های قبل از API 21 لازم است از کتابخانه appcompat-v7 استفاده کنیم. با توجه به اینکه پروژه ما حداقل API 16 را پشتیبانی می کند بنابراین به این کتابخانه نیاز داریم.
Build.gradle (app) را باز می کنم. بلاک مربوط به dependencies به اینصورت است:

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:26.+'
}

بنابراین کتابخانه appcompat به صوت پیش فرض به پروژه من اضافه شده و لازم نیست کاری انجام دهم. اما اگر این کتابخانه در پروژه شما وجود نداشت باید به صورت دستی آن را اضافه کنید. راه ساده اضافه کردن خط

compile 'com.android.support:appcompat-v7:x.x'

به پروژه است. اما ممکن است از ورژن appcompat موجود در SDK خود اطلاعی نداشته باشید (جایی که با x.x مشخص شده). بنابراین بهتر است از اندروید استودیو کمک بگیرید.
مسیر File > Project Structure سپس در پنجره باز شده در قسمت Modules گزینه app را انتخاب می کنم:

اضافه کردن کتابخانه appcompat-v7 به پروژه اندروید

با انتخاب تب Dependencies لیست دپندنسی های موجود نمایش داده می شود. در سمت راست گزینه اضافه کردن (+) قرار دارد که با کلیک روی آن و انتخاب Library dependency پنجره جدیدی باز می شود. در اینجا لیست کتابخانه هایی که در SDK ما وجود دارد نمایش داده می شود. Appcompat را از لیست پیدا و یا در کادر جستجو وارد کنید:

اضافه کردن کتابخانه appcompat-v7 به پروژه اندروید

اضافه کردن کتابخانه appcompat-v7 به پروژه اندروید

نکته : برای استفاده از کتابخانه appcompat لازم است Android Support Library را نصب کرده باشید.

با تایید پنجره فوق، کتابخانه به لیست Dependencies اضافه و در نهایت با تایید آن، به پروژه و فایل build.gradle اضافه می شود.
به سراغ اکتیویتی می روم. سه Button به activity_main.xml اضافه می کنم و خواص (attribute) یکسانی برای هرسه تعیین می کنم:

اضافه کردن سه Button به پروژه اندرویدی

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="ir.android_studio.style.MainActivity">


    <Button
        android:id="@+id/button_one"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Button One"
        android:textSize="18sp"
        android:textColor="#ffffff"
        android:background="#9365ca"
        android:layout_margin="5dp" />

    <Button
        android:id="@+id/button_two"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Button Two"
        android:textSize="18sp"
        android:textColor="#ffffff"
        android:background="#9365ca"
        android:layout_margin="5dp"/>

    <Button
        android:id="@+id/button_three"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Button Three"
        android:textSize="18sp"
        android:textColor="#ffffff"
        android:background="#9365ca"
        android:layout_margin="5dp"/>

</LinearLayout>

کد مربوط به دکمه ها را بررسی کنید. به جز id و text مابقی خواص یکسان است. این تکرار خواص علاوه بر افزایش حجم نهایی اپلیکیشن، روند اصلاح و تغییر را با مشکل مواجه می کند. الان اگر بخواهم مقدار مربوط به خاصیت background را تغییر دهم باید رنگ جدید را در هر سه مورد جایگزین کنم. حالا درنظر بگیرید در چند اکتیویتی دیگر هم ۱۰ دکمه دیگر داشته باشیم!
در مسیر res/values فایلی با نام styles.xml قرار دارد که مختص تعریف استایل هاست:

فایل styles.xml در اندروید

استایل ها داخل تگ

<style>
</style>

تعریف می شوند که برای هر استایل باید یک نام منحصر بفرد تعیین کنیم. هر یک از خواص داخل استایل نیز درون تگ

<item>
</item>

قرار می گیرند.
حالا می خواهم یک استایل جدید ایجاد کنم که خواص مشترک در دکمه ها را شامل شود. (استایلی که به صورت پیش فرض با نام AppTheme تعریف شده را فعلا نادیده بگیرید.)

<style name="MyButton">

</style>

یک استایل با نام دلخواه MyButton ایجاد کردم. در قدم بعد، خواص را در قالب item هایی به استایل اضافه می کنم.
فایل styles.xml:

<resources>

    <style name="MyButton">
        <item name="android:layout_width">match_parent</item>
        <item name="android:layout_height">wrap_content</item>
        <item name="android:textSize">18sp</item>
        <item name="android:textColor">#ffffff</item>
        <item name="android:background">#9365ca</item>
        <item name="android:layout_margin">5dp</item>
    </style>
    
    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

</resources>

به اکتیویتی برمی گردم. ابتدا خواص مشترک در دکمه اول را حذف می کنم:

<Button
    android:id="@+id/button_one"
    android:text="Button One" />

حالا کافیست استایل MyButton را به این دکمه اضافه کنم:

<Button
    style="@style/MyButton"
    android:id="@+id/button_one"
    android:text="Button One" />

خاصیت style با مقدار @style/StyleName خواصی که در استایل تعریف کرده ام را روی این دکمه اعمال می کند.

<Button
    style="@style/MyButton"
    android:id="@+id/button_one"
    android:text="Button One" />

<Button
    android:id="@+id/button_two"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="Button Two"
    android:textSize="18sp"
    android:textColor="#ffffff"
    android:background="#9365ca"
    android:layout_margin="5dp"/>

<Button
    android:id="@+id/button_three"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="Button Three"
    android:textSize="18sp"
    android:textColor="#ffffff"
    android:background="#9365ca"
    android:layout_margin="5dp"/>

در حال حاضر در قسمت Preview هرسه Button ظاهر یکسانی دارند. حالا دو دکمه دیگر را هم به استایل مرتبط می کنم:

<Button
    style="@style/MyButton"
    android:id="@+id/button_one"
    android:text="Button One" />

<Button
    style="@style/MyButton"
    android:id="@+id/button_two"
    android:text="Button Two" />

<Button
    style="@style/MyButton"
    android:id="@+id/button_three"
    android:text="Button Three" />

در ابتدا Layout اکتیویتی ۴۰ خط کد داشت که با استفاده از استایل دهی به Button ها، این عدد به ۲۵ خط رسید. علاوه بر تمیزی و کوتاهی کد نهایی، هرگاه نیاز به تغییری در خواص این دکمه ها داشته باشم، با یک بار تغییر آن در styles.xml، در تمامی سطح پروژه لحاظ خواهد شد و نگرانی از این بابت که موردی از قلم بیفتد ندارم.
در صورتی که مانند قسمت بالا قصد انتقال خواص موجود در یک کنترل به استایل را داشته باشیم، اندروید استودیو راه ساده تری را در اختیار ما قرار داده. من دو TextView با خواص مشترک به اکتیویتی اضافه می کنم تا دو قابلیت کاربردی را به شما نشان دهم.

<TextView
    android:id="@+id/txt_1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center"
    android:text="SampleText 1"
    android:textColor="#c20d37"
    android:textSize="23sp"
    android:layout_marginTop="10dp"/>

<TextView
    android:id="@+id/txt_2"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center"
    android:text="SampleText 2"
    android:textColor="#c20d37"
    android:textSize="23sp"
    android:layout_marginTop="10dp"/>

در قسمت Preview روی یکی از TextView ها راست کلیک کرده و Refactor > Extract Style را انتخاب می کنم:

extract کردن استایل در اندروید

extract کردن استایل در اندروید

در پنجره جدید ابتدا یک نام برای استایل مدنظر انتخاب می کنم. در کادر Attributes خواصی که قصد دارم به استایل تبدیل شوند را باید انتخاب کنم. گزینه ای با عنوان

Launch ‘Use Style Where Possible’

وجود دارد که با انتخاب آن، تمام کنترل های موجود در پروژه (یا اکتیویتی) که خواص مشترک با مقادیر یکسان با این استایل داشته باشند، به صورت خودکار به آن مرتبط می شوند و نیاز به تبدیل دستی و تک به تک موارد نیست:

extract کردن استایل در اندروید

در این مرحله انتخاب می کنم که تغییرات برای سایر کنترل ها فقط در سطح همین اکتیویتی انجام شود یا کل پروژه که من فقط به همین اکتیویتی نیاز دارم.

extract کردن استایل در اندروید

extract کردن استایل در اندروید

در نهایت مجدد گزینه Do Refactor را تایید می کنم. استایل با موفقیت اضافه و روی هر دو TextView اعمال شد.

styles.xml:

<style name="MyText">
    <item name="android:layout_width">match_parent</item>
    <item name="android:layout_height">wrap_content</item>
    <item name="android:gravity">center</item>
    <item name="android:textColor">#c20d37</item>
    <item name="android:textSize">23sp</item>
    <item name="android:layout_marginTop">10dp</item>
</style>

main_activity.xml:

<TextView
    android:id="@+id/txt_1"
    android:text="SampleText 1"
    style="@style/MyText" />

<TextView
    android:id="@+id/txt_2"
    android:text="SampleText 2"
    style="@style/MyText" />

در استایل هم با مفهوم ارث بری سروکار داریم که کمک زیادی در کاهش حجم برنامه و افزایش سرعت توسعه برنامه می کند. به عنوان مثال من قصد دارم چند دکمه دیگر نیز در پروژه خودم بکار ببرم که ویژگی های آن با دکمه های قبل یکسان است و فقط در رنگ پس زمینه تفاوت دارد. اگر امکان ارث بری نداشته باشیم لازم است یک استایل جدید بسازم که تمام خواص استایل قبلی را تکرار کرده و فقط خاصیت background مقدار متفاوتی به خود گرفته. اما با استفاده از این قابلیت، تنها لازم است پس از ایجاد استایل جدید، خاصیت یا خواصی که قصد تغییر آنها را دارم بازتعریف کرده و مقدار مدنظر را جایگزین کنم.

<style name="MyButton">
    <item name="android:layout_width">match_parent</item>
    <item name="android:layout_height">wrap_content</item>
    <item name="android:textSize">18sp</item>
    <item name="android:textColor">#ffffff</item>
    <item name="android:background">#9365ca</item>
    <item name="android:layout_margin">5dp</item>
</style>

<style name="MyButton.Green">
    <item name="android:background">#51c125</item>
</style>

به کد بالا دقت کنید. نام استایل جدید را MyButton.Green تعریف کردم که اضافه شدن MyButton. قبل از Green باعث شده این استایل تمامی خواص استایل والد یعنی MyButton را نیز شامل شود. حالا خاصیت رنگ پس زمینه را با مقداری که مدنظر دارم به استایل اضافه می کنم. در نهایت این کار باعث می شود در هر ویجتی که MyButton.Green را به عنوان استایل تعریف میکنم، تمامی خواص MyButton را بپذیرد با این تفاوت که مقدار background در آن Override (جایگزین) شده است. برای اینکه ببینم استایل جدید من به درستی کار می کند یا نه، یکی از دکمه ها را به این استایل مرتبط می کنم:

<Button
    style="@style/MyButton"
    android:id="@+id/button_one"
    android:text="Button One" />

<Button
    style="@style/MyButton.Green"
    android:id="@+id/button_two"
    android:text="Button Two" />

<Button
    style="@style/MyButton"
    android:id="@+id/button_three"
    android:text="Button Three" />

ارث بری در استایل اندروید

مشاهده می کنید دکمه دوم تنها در رنگ پس زمینه متفاوت است. در نحوه ارث بری و نامگذاری استایل فوق مشکل خاصی متوجه ما نیست. اما بسته به نیاز توسعه دهنده ممکن است نامی که در این روش برای استایل جدید انتخاب می‌کنیم تا حدی طولانی شود که باعث شلوغی کدها خواهد شد. نام زیر را در نظر بگیرید:

MyButton.SmallButton.BorderButton.Green

– SmallButton: استایلی که در آن layout_width مقدار ۱۰۰dp دارد و عرض را کامل اشغال نمی کند.
– BorderButton: استایلی که خاصیت مربوط به حاشیه به آن اضافه شده تا دکمه ها در حاشیه خود یک نوار رنگی داشته باشند.
در نهایت با ساخت استایلی با نام فوق، دکمه ای خواهیم داشت که سایز آن کوچک است، حاشیه دارد و رنگ پس زمینه سبز می باشد. این نام قدری طولانی است و بکار بردن آن در داخل اکتیویتی ها از زیبایی و خوانایی کد می کاهد. استایل مدنظر فوق را به صورت زیر می توانیم ارث بری کنیم:

<style name="GreenButton" parent="MyButton.SmallButton.BorderButton">
    
</style>

استایل هایی که لازم است ارث بری شوند در قالب parent تعریف می شوند و دیگر نیازی نیست به ابتدای نام اضافه شوند. یعنی به جای

<Button
    style="@style/MyButton.SmallButton.BorderButton.Green"
    android:id="@+id/button_one"
    android:text="Button One" />

به اینصورت استایل را به ویجت اضافه می کنیم:

<Button
    style="@style/GreenButton"
    android:id="@+id/button_one"
    android:text="Button One" />

خب! با کلیت Style در اندروید آشنا شدیم. حالا به معرفی Theme می پردازم. قبلا ذکر شد تم همان استایل است با این تفاوت که یکبار و در سطح کلی پروژه و اکتیویتی ها تعریف می شود نه در تعدادی ویجت خاص.

قبلا با فایل AndroidManifest.xml آشنا شدیم. تنظیمات کلی پروژه اندروید در این فایل تعیین می شود:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="ir.android_studio.style">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

درون تگ application خاصیتی با نام theme وجود دارد که مشابه آنچه قبلا دیدیم، به یک استایل با نام AppTheme مرتبط شده است. به styles.xml برگردید (با نگه داشتن ctrl و کلیک روی @style/AppTheme نیز به این فایل منتقل می شوید.

<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
</style>

استایلی با نام AppTheme که از Theme.AppCompat.Light.DarkActionBar ارث بری شده است. با Theme.AppCompat ما به راحتی به تم های متریال که در کتابخانه appcompat-v7 گنجانده شده دسترسی داریم. با اضافه شدن Light، قسمت های مختلف تم (از جمله رنگ پس زمینه، نوشته، اکشن بار و…) به سبک لایت (روشن) تعیین شده اند که با حذف آن ظاهر تم از روشن به تاریک (Dark) تغییر می کند. تم متریال به صورت پیش فرض یک ActionBar دارد که رنگ آن روشن است و اگر DarkActionBar به تم اضافه کنیم به رنگ تیره تغییر می یابد.

تم theme تاریک و روشن در متریال دیزاین اندروید

رنگ بندی و تمامی attribute های پیش فرض تم را می توان تغییر داد. شاید این سوال برا شما مطرح شود که اگر این خواص قابل تغییر هستند پس چرا اندروید دو تم تاریک و روشن را به عنوان تم پایه معرفی کرده. پاسخ این است که توسعه دهنده با انتخاب نسخه ای که با تم نهایی مد نظرش همخوانی و هماهنگی بیشتری دارد، در جزئیات (مانند رنگ متن و آیکون های موجود در اکشن بار) نیاز به اعمال تغییرات کمتری خواهد داشت.

قسمت های مختلف تم متریال دیزاین اندروید

تم متریال خواصی را می پذیرد که از قبل تعریف شده است. به تصویر بالا دقت کنید. به عنوان مثال colorPrimaryDark مربوط به رنگ StatusBar (استاتوس بار) و colorPrimary مربوط به رنگ ActionBar می شود.
پروژه خودم را روی جنی موشن و اندروید نسخه ۷٫۰ اجرا می کنم. با توجه به آنچه قبلا اشاره شد، قطعا این نسخه از اندروید، متریال دیزاین را کاملا پشتیبانی می کند:

اجرای پروژه روی اندروید 7.0

یکی از ویژگی های متریال دیزاین این است که توسعه دهنده امکان تغییر رنگ پس زمینه استاتوس بار را دارد. همانطور که در تصویر بالا مشاهده می کنید بر اساس تم پیش فرض AppTheme در قسمت استاتوس بار رنگ سورمه ای پر رنگ و برای اکشن بار، سورمه ای کم رنگ لحاظ شده است. همچنین متن و آیکون در این دو قسمت به رنگ سفید نمایش داده می شود.
حالا پروژه را یکبار دیگر و بر روی نسخه ای از اندروید اجرا می کنم که پایین‌تر از API 21 باشد. یعنی نسخه ای قبل از آنکه متریال دیزاین معرفی شده باشد. من API 19 را انتخاب و اجرا می کنم:

اجرای پروژه روی اندروید 4

همانطور که مشاهده می کنید در API 21 و به پایین، امکان تغییر رنگ استاتوس بار وجود ندارد و همواره با رنگ مشکی نمایش داده می شود. موارد دیگری نیز به اینصورت در نسخه های قبل از اندروید ۵٫۰ پشتیبانی نمی شوند که لازم است توسعه دهنده تمامی قسمت های پروژه را در نسخه های مختلف اندروید تست و در صورت نیاز اصلاح کند.
من خواص فعلی داخل AppTheme را حذف و سپس پروژه را مجدد Run می کنم:

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">

</style>

تم AppTheme

در تصویر بالا یک اکشن بار تاریک و استاتوس بار مشکی داریم که رنگ متن و آیکون در هر دو سفید است. در مرحله بعد DarkActionBar را حذف می کنم تا تم از آن ارث بری نکند:

<style name="AppTheme" parent="Theme.AppCompat.Light">

</style>

حذف DarkActionBar از تم

با حذف آن، یک اکشن بار روشن و استاتوس بار خاکستری رنگ در اختیار دارم که رنگ متن اکشن بار به مشکی تغییر یافته است. حالا Light را هم حذف و مجدد اجرا می کنم:

<style name="AppTheme" parent="Theme.AppCompat">

</style>

حذف Light از تم AppTheme

به دلیل اینکه تم متریال پایه در اندروید از نوع تاریک است، با حذف Light، سبک کلی تم نیز تاریک می شود. یعنی استاتوس بار و اکشن بار و همچنین پس زمینه اکتیویتی.
ممکن است در یک اپلیکیشن نیازی به اکشن بار نداشته باشیم. به راحتی و با جایگزین کردن NoActionBar به جای DarkActionBar اکشن بار از اکتیویتی ها حذف می شود:

جایگزین کردن NoActionBar به جای DarkActionBar

حالا اگر Light را مجدد به تم اضافه کنم، یک تم روشن بدون اکشن بار داریم:

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">

</style>

اضافه کردن Light به تم

روی Theme.AppCompat.Light.NoActionBar کلید ctrl را نگه داشته، کلیک کنید. به فایلی منتقل می شوید که خواص مربوط به انواع تم ها در آن تعریف شده و به صورت پیش فرض استایل مربوط به NoActinBar را برای ما پیدا می کند:

دو آیتم در این استایل قرار دارد. اولی windowActionBar با مقدار false که باعث حذف اکشن بار شده و دومی windowNoTitle که تایتل موجود در اکشن بار (یعنی کلمه Style) را نیز حذف می کند. اینجا ارث بری کار توسعه دهنده را ساده کرده و بجای اینکه لازم باشد در تم خود این دو خاصیت را تعریف کنیم، تنها با افزودن NoActionBar به Theme.AppCompat.Light این عمل انجام می شود. با بررسی سایر استایل های موجود در این فایل می توانید به تفاوتهای سایر تم ها با یکدیگر پی برده و از آنها استفاده کنید.
مجدد DarkActionBar را جایگزین NoActionBar می کنم تا اکشن بار به اکتیویتی اضافه شود. یعنی الان اپلیکیشن من به اینصورت است:

تم پیش فرض متریال اندروید

مجدد به تصویر زیر توجه کنید:

تم متریال اندروید

می خواهم مقدار رنگ استاتوس بار را با رنگ فعلی یعنی مشکی جایگزین کنم. یک آیتم با نام colorPrimaryDark و مقدار مدنظرم به تم اضافه می کنم:

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="colorPrimaryDark">#8e2525</item>
</style>

اضافه کردن colorPrimaryDark به تم متریال

به همین ترتیب یک مقدار نیز برای colorPrimary در نظر می گیرم:

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="colorPrimaryDark">#8e2525</item>
    <item name="colorPrimary">#b43232</item>
</style>

اضافه کردن colorPrimary به تم متریال

مورد بعدی که به تم اضافه می کنم colorAccent است. این رنگ در کنترل هایی مانند CheckBox، Switch، RadioButton، RatingBar، ProgressBar و… اعمال می شود. من دو مورد را به اکتیویتی اضافه کرده و کد رنگ #d22701 را نیز برای این خاصیت قرار می دهم:

اضافه کردن colorAccent به تم متریال

مشاهده می کنید رنگ چک باکس و سوئیچ در حالت فعال، همان رنگی است که به colorAccent نسبت داده ام.
در مرحله بعد تمایل دارم برای NavigationBar (نوار مشکی رنگ پایین صفحه که شامل ۳ دکمه است) پس زمینه دیگری تعریف کنم:

تغییر رنگ پس زمینه NavigationBar

تغییر بک گراند NavigationBar

بر خلاف موارد پیشین، این مورد با پیشوند android: شروع شده که پس از تکمیل، اخطاری مطابق تصویر بالا نشان می دهد. به این معنی که این گزینه نیازمند حداقل API 21 است. خواصی که با android: شروع می شوند متعلق به appcompat نبوده و مربوط به خود اندروید می باشد. بنابراین این ویژگی فقط مربوط به نسخه هایی است که متریال دیزاین را به صورت بومی پشتیبانی می کنند:

تغییر پس زمینه نویگیشن بار

اما رنگ هایی که من استفاده کردم برای متریال استاندارد نیست.

وب سایتهای مرجع متریال دیزاین

وب سایتهای زیادی در دسترس هستند که برای انتخاب رنگهای استاندارد به ما کمک می کنند. من Material design colors را سرچ کردم و چند وب سایتی که در صفحه اول نتایج معرفی می شود (تصویر بالا) در زمان تهیه این آموزش جزء منابع مطرح هستند.
Material.io متعلق به خود گوگل است و در خصوص متریال مطالب و مثالهای خوبی دارد که توصیه می کنم یکبار همه صفحات آن را مرور کنید. صفحه زیر مربوط به رنگهاست:

https://material.io/guidelines/style/color.html

در قسمت Color Pallete تمامی رنگهای متریال با جزئیات ذکر شده است:

توضیحات متریال دیزاین در material.io

هر رنگ اصلی دارای چند زیرمجموعه با درصدهای مختلف است که می توان در نقاط مختلف از آنها استفاده کرد. به عنوان مثال عموما برای استاتوس بار از عدد ۷۰۰ و اکشن بار از عدد ۵۰۰ آن رنگ استفاده می کنند.

https://www.materialpalette.com

این وب سایت هم گزینه خوبی برای انتخاب رنگهای استاندارد است:

پالت رنگ متریال

کافیست از داخل پالت، چند رنگ مدنظر خود را انتخاب کنید. در سمت راست هم یک دمو از صفحه اپلیکیشن فرضی را با همان رنگها مشاهده می کنید و هم کد رنگهای استفاده شده نمایش داده می شود که امکان دانلود با فرمت های مختلف نیز تعبیه شده.
موارد زیادی برای تم قابل تعریف است که فرصت بررسی همه آنها نیست. در هنگام افزودن آیتم جدید، لیستی باز می شود که تعداد زیادی از گزینه های در دسترس را می توان مشاهده کرد. علاوه بر این با بررسی مثالهای موجود در سطح وب هم در این زمینه اطلاعات بیشتری کسب خواهید کرد. ضمن اینکه این خواص محدود به رنگ نیست. به عنوان مثال با تعریف textSize، کلیه متون داخل پروژه که textSize اختصاصی ندارند، از طریق گزینه موجود در تم مقدار دهی خواهند شد.
اگر بخاطر داشته باشید آیتم هایی که به صورت پیش فرض درون AppTheme وجود داشت مستقیم به مقدار رنگ اشاره نداشتند:

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
</style>

در اندروید فایلی با نام colors.xml داریم که وظیفه ای مشابه با strings.xml دارد که قبلا با آن آشنا شدیم. به جای تعریف مستقیم رنگ ها در استایل یا خواص ویجت ها، می توان ابتدا آنها را در colors.xml تعریف کرده سپس داخل پروژه استفاده کرد. به این ترتیب در مواقعی که نیاز به تغییر رنگ های داخل پروژه داریم، با سرعت بیشتری می توان تغییرات را اعمال کرد و در مواردی که قصد داریم در چند جای مختلف از یک رنگ یکسان استفاده کنیم، نیاز به تعریف چندباره نبوده و تنها با یک بار تعریف آن درون colors.xml می توان به تعداد نیاز آنرا در سراسر پروژه فراخوانی کرد.
نکته پایانی در خصوص تم اینکه قبلا با نحوه تعریف یک تم در مانیفست آشنا شدیم که با اضافه کردن تم به تگ application، در سراسر پروژه و تمامی اکتیویتی ها اعمال می شد. اما ممکن است در یک پروژه نیاز به استفاده از دو یا چند تم مختلف داشته باشیم. به عنوان مثال تصمیم دارم اکتیویتی اصلی دارای اکشن بار باشد اما اکتیویتی دوم که باید یک نقشه را نمایش دهد، بدون اکشن بار باشد. در اینجا لازم است تم مدنظر را ایجاد کرده و سپس داخل مانیفست و در تگ اکتیویتی مربوطه آن را اضافه کنیم.
styles.xml:

<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="colorPrimaryDark">#8e2525</item>
    <item name="colorPrimary">#b43232</item>
    <item name="colorAccent">#1d2270</item>
    <item name="android:navigationBarColor">#cfda53</item>
    <item name="android:textSize">20dp</item>
</style>

<style name="NoActionbarTheme" parent="AppTheme">
    <item name="windowActionBar">false</item>
    <item name="windowNoTitle">true</item>
</style>

AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="ir.android_studio.style">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity
            android:name=".NoActionBarActivity"
            android:theme="@style/NoActionbarTheme">

        </activity>

    </application>

</manifest>

من یک تم جدید با نام NoActionBarTheme ایجاد و از AppTheme ارث بری کردم تا تمامی خواص آنرا دربر گیرد. سپس دو موردی که برای حذف اکشن بار لازم بود به تم اضافه نمودم. در قدم بعد یک اکتیویتی با نام NoActionBarActivity به پروژه اضافه کردم که به وسیله intent و با لمس دکمه Go to activity two از اکتیویتی اصلی به آن منتقل می شود و در نهایت تم را به تگ activity مربوط به این اکتیویتی اضافه کردم. هر اکتیویتی که تم اختصاصی نداشته باشد، از تم موجود در تگ application استفاده می کند:

ساخت اکتیویتی بدون اکشن بار

کار با استایل و تم نیازمند تمرین، آزمون و خطا و مشاهده مثالهای بیشتری است و هرچه پروژه های بیشتری انجام دهید، نکات جدیدی خواهید آموخت.
توجه : سورس پروژه درون پوشه Exercises قرار داده شده است

دانلود فایل این آموزش با فرمت PDF به همراه سورس پروژه
تعداد صفحات : ۳۹
حجم : ۱٫۷ مگابایت
قیمت : رایگان
دانلود رایگان با حجم ۱٫۷ مگابایت لینک کمکی
این مطلب چقدر برایتان مفید بود؟ لطفا امتیاز دهید
4.5/5 - (34 امتیاز)
پرسش‌ها و دیدگاه‌های کاربران
دوره آموزش برنامه نویسی اندروید
دوره آموزش برنامه نویسی اندروید

با دریافت این دوره به تمامی آموزش‌های غیر رایگان و رایگان موجود در وب سایت دسترسی دارید که تخفیفی برای آموزش‌های غیر رایگان نیز درنظر گرفته شده. این پکیج به دو صورت دانلودی و ارسال پستی ارائه می‌گردد.
آموزش‌های اندروید استودیو در دو دسته «پایه» و «تکمیلی» منتشر می‌شوند.
آموزش‌های پایه شامل مباحث اصلی و ضروری و آموزش‌های تکمیلی مطالبی است که می‌بایست در کنار مطالب اصلی بررسی شود.
با خرید این دوره، به تمامی آموزش‌های غیر رایگانی که در آینده منتشر می‌شود نیز به صورت رایگان دسترسی خواهید داشت!

یک دیدگاه بنویسید

پرسش‌های زیر تایید و پاسخ داده نـــخواهند شد:
۱: جزء موارد مطرح شده در صفحات مشکلات و پرسش‌های رایج و بروزرسانی‌های محتوای آموزشی باشد
۲: سوال قبلا توسط کاربران در دیدگاه‌ها مطرح و پاسخ داده شده باشد
۳: پرسش خارج از مبحث آموزشی موجود در این صفحه باشد

  • کمال طومار بزرگ گفت:

    با سلام ایا راهی هست که دگمه های پایین موبایل رو سیاه کرد

  • طیبی فر گفت:

    عالی بود. دست مریزاد

  • مصطفی گفت:

    سلام
    چطوری میتونم به image view داخل برنامه radios بدم؟

  • Erfan گفت:

    سلام و وقت بخیر
    من کتابخانه Lottie رو به پروژه خودم اضافه کردم
    بعد یک فایل json رو روی پروژه خودم اجرا کردم
    میشه کاری کرد که بعد از به پایان رسیدن انیمیشن دستوری که می‌خوام اجرا بشه؟
    مثلاً بعد از پایان انیمیشن یک تست نمایش داده بشه
    ببخشید اگر سوالم به این آموزش مربوط نبود

  • فرزاد عسکری گفت:

    سلام مهندس/در این بخش می خوام رنگ دکمه هام عوض کنم در قسمت xml و حتی در styles.xml هم رنک دکمه رو عوض می کنم ولی در قسمت Design یا حتی در run کردن پروژه تغییر رنگ نمایش داده نمیشه و فقط رنگ آبی رو نشون میده البته با کمی بررسی فهمیدم در قسمت Design در بخش Theme for preview میشه درستش کرد و در قسمت Design رنگ دکمه ها رو درست نشون میده ولی موقع run بازم رنگ آبی هستند و تغییری نمی کنه.مشکل کجاست/ممنون/

  • امیرحسین گفت:

    سلام ، سایتتون عالیه , بسیار مطالب مفیدی ارائه میکنید ، از شما سپاسگزارم!

  • میلاد گفت:

    من وقتی قسمت ارث بری را گذاشتم خطای زیر رو میده
    doe not set the required layout_with attribute
    یعنی عملا نمیشناسه در قسمت تم هم بصورت زیر نوشتم.

    match_parent
    wrap_content
    ۱۸sp
    #ffffff
    #۹۳۶۵ca
    ۵dp

    #۵۱c125

  • رضا گفت:

    سلام
    چطور میشه پس زمینه دکمه رنگ نداشته باشه یعنی هم رنگ صفحه باشه من جایی خوندم گفته بود background رو @null قرار بده این کارو کردم ولی باز همون رنگ پیش فرض اندروید هست
    ممنون میشم راهنمایی کنید

  • Abolfazl گفت:

    سلام ، خسته نباشید
    توضیحات کامل و فوق العاده بود
    اما توی برنامه روبیکا و بعضی برنامه های دیگه این مسیر و فایل وجود نداره
    res/values/style.xml
    پس اون چیزا که گفتین رو نمیتونم انجام بدم و نمیدونم چیکار باید بکنم
    اما در عوض چیزای دیگه ایی وجود داره که تهشون xxhdpi نوشته
    اگه میشه راه عوض کردنه تم این مدل برنامه هارو هم بگین

  • ِDriveShaft گفت:

    این سایت یکی از بهترین مرجع های هست که همیشه ازش استفاده و خرید میکنم و بیشتر اوقات مشکلم رو رفع میکنم .
    مرسی از زحماتتون.

  • سالار گفت:

    با سلام و احترام جناب سید مهدی مطهری بنده خیلی به دنبال این مطالب و استایل دهی به کامپوننت ها بودم که به مطالب شما رسیدم قبلا پکیج رو از سایت شما تهیه کرده بودم و باز هم به سایت شما خوردم که از سر موضوع که شروع به مطالعه کردم از بس از مطالب شما لذت بردم از این که این قدر با حوصله و با جزییات توضیح دادید همون وسط مطالعه رو رها کردم که بیام از شما تشکر کنم بنده به دنبال این بودم که مثلا برای یک MaterialAlertDialog چطور می شه فقط عنوان رو استایل دهی کرد که دیروز کلی گشتم ولی نتونستم مطلب مناسب پیدا کنم البته باز میرم ادامه مطالب شما رو می خونم امیدوارم به جواب سوالم رسیده باشم خلاصه وظیفه دانستم از شما تشکر کنم خیلی عالیه مطالبتون و از این به بعد اول به مطالب شما سر خواهم زد زنده و پایدار باشید.

  • مهدی گفت:

    با سلام
    بسیار عالی بود و بسیار سپاس گذارم از آموزش شما.
    شاد پیروز سلامت باشید

  • رضا گفت:

    سلام من میخام ب نوگیشن بار خود گوشی رنگ اختصاص بدم ب این صورت که رنگ سبز از سمت چپ شروع بشه و ب سمت راست که میره رنگش آبی بشه داخل سایتا هم هر چیزی که خوندم فقط میشه ی تک رنگ ب نوگیش بار اختصاص داد ممنون میشم کسی جوابم رو بده

  • محمد گفت:

    سلام فقط یه جمله خیلی کوتاه
    خیلی ماهی. انشا الله سلامت و موفق و پیروز باشی
    خدا نگهدارت

  • nkm96 گفت:

    سلام. خیلی آموزش جامع و مفیدی بود.
    بسیار متشکرم:)

  • احسان کلالی گفت:

    با سلام و سپاس از آموزشهاتون
    فقط یه سوال اینکه بنده برای برنامه ام در بخش تنطیمات اپلیکیشن، حالت تنظیم تم رو قرار دادم که میتونه کاربر تم برنامه رو انتخاب کنه…
    زمانی که تم رو انتخاب میکنه، برنامه کاربر رو به صفحه قبل برمیگردونه و تم اجرا میشه… (مثه تنظیم تم تلگرام)
    یعنی اگه صفحه تنظیم تم اکتیویتی A باشه و صفحه قبلی اکتیویتی B باشه، در متد onResume و onStart اکتیویتی B بررسی میکنم که اگه تم تغییر کرده، بیاد و تمام تم اون اکتیویتی رو تغییر بده… این تغییر تم اجرا میشه بجز رنگ استاتوس بار…
    یعنی تم برای همه اکتیویتی ها اعمال میشه ولی برای رنگ استاتوس بار اکتیویتی B اعمال نمیشه مگر اینکه کاربر برنامه رو کلا ببنده و دوباره باز کنه که در متد onCreate اکتیویتی B بررسی تم انحام میشه و رنگ استاتوس بار هم برای تم جدید اعمال میشه… (یعنی کل مشکل تغییر تم برنامه در متد onResume و onStart اکتیویتی B هست. با وجود اینکه قبل از متد setContentView() تم رو بررسی و اعمال میکنه ولی باز هم این مشکل هست)
    همچنین بنده تغییر رنگ و تم برنامه رو با استفاده از SharedPreferences بررسی میکنم
    برای این مسئله چه راه حلی رو پیشنهاد میکنید؟

    ممنون میشم راهنمایی بفرمایید.

  • یاسین گفت:

    سلام
    یکی از بهترین تجربیاتی که تا به حال داشتم استفاده از paletton.com بوده
    وارد که میشید در قسمت بالا سمت چپ پنجره ۴ حالت داره
    تک رنگ، دو تا سه رنگ و چهار رنگ که با انتخاب هر کدوم و جا به جا کردن دستیگره ها ترکیب رنگ های قشنگی رو سمت راستش بهتون میده که داخل خود وبسایت هم میتونید تستشون کنید.

  • سلام .

    من یه مشکل دارم که وقتی چیزی وارد میکنم رند نمیشه . از لیست میتونم اتریبیوت بهش بدم اما ۰ و ۰ میشه و با لیوت میرن یه گوشه .

    این مشکل در مرجع تم appcompat هست و منو خیلی درگیر کرده .
    برای این که بتونم دیزاین رو ببینم مجبورم تم رو به holo light تغییر بدم ولی با اولین باز و بسته شدن اکتیویتی تغییرات برمیگردن به حالت قبلی .

    چکار کنم ؟؟

    اینم اسکرین شات :

    http://private-uploads.icgsite.ir/Screenshot%20from%202018-07-11%2012-04-42.png

  • reza abdollahi گفت:

    سلام در هنگام استفاده از appcompat-v7:26.0.0-alpha1 این پیام میاد:

    When using a compileSdkVersion android-O revision 2 or higher, the support library version should be 26.0.0-beta1 or higher (was 26.0.0-alpha1))
    There are some combinations of libraries, or tools and libraries, that are incompatible, or can lead to bugs. One such incompatibility is compiling with a version of the Android support libraries that is not the latest version (or in particular, a version lower than your targetSdkVersion.)

    در حالی که sdk من آخرین نسخه منتشر شده هست پس چرا میگه از نسخه قدیمی داری استفاده می کنی؟

    • سید مهدی مطهری (مدیر) گفت:

      نسخه قدیمی مربوط به سورسی هست که ایمپورت کردید نه SDK شما. داخل build.gradle کتابخانه appcompat موجود رو حذف و طبق اموزش، کتابخانه موجود در sdk خودتونو به پروژه اضافه کنید