Theme
برای اینکه بتوانیم ظاهر گرافیکی layoutها را کنترل نماییم، از Theme که مجموعهای از styleهای گرافیکی میباشد، استفاده میکنیم. در اندروید مجموعهای از تمهای از پیش ساخته شده که به آنها Builtin Theme نیز گفته میشود میتوانیم استفاده کنیم. تمها ظاهر گرافیکی کلیه کنترلهای Layout را با نامهای زیر، کنترل میکنند:
statusBarColor
textColorPrimary
colorAccent
ColorPrimary
WindowBackground
اگر ساختار زیر را در یک صفحه استاندارد برنامههای موبایل را در نظر بگیریم، styleها هر بخش، یک نام منحصر به فرد دارد:
اگر بخواهیم از styleهای از پیش طراحی شدهی اندروید استفاده نماییم، ابتدا میتوانیم در صفحهی layout در زامارین، style مربوطه را از بخش Theme استفاده کرده و نتیجه را مشاهده کنیم. ولی تغییر style سبب تغییر layout در زمان اجرا نمیشود. هرگاه بخواهیم از styleهای استاندارد یا builtin اندروید استفاده نماییم، در Activity توسط خصوصیت Theme با فرمت:
[Activity(Theme = "@style/NameThem")]
تم را بهعنوان تم داخلی وسپس نام کامل تم را مینویسیم.
CustomTheme
در طراحی فرمها ممکن است بخواهیم از یک استایل خاص builtin استفاده کنیم؛ ولی ممکن است بعضی از استایلهای آن را استفاده نکنیم، مانند تمی که از قبل استفاده شدهاست، از روش زیر استفاده میکنیم:
- بر روی دایرکتوری value راست کلیک میکنیم. گزینه add new item را انتخاب و یک فایل xmlfile را با نام style ایجاد میکنیم.
- styleهای جدید منابع application میباشند که در بخش resource از آنها استفاده میکنیم. هر استایل جدید را توسط Style Tag مشخص میکنیم و در خصوصیت Name، نام Style را مشخص میکنیم.
ممکن است در یک Style نتوانیم و یا نخواهیم تمامی Styleهای مورد نیاز را تامین کنیم. از این رو توسط Parent، یک StyleBuition تعریف نموده که این Styleها از آن مشتق میشوند. اگر در Theme جدید گزینهها مشخص شوند، تم اصلی تغییر نمیکند. در غیر اینصورت تمامی گزینههای تعریف شده در تم جدید از ThemParent مقدار دهی میشود.
توسط item میتوان یک style را تعریف نمود. در Activity توسط
[Activity(Theme = "@style/NameThem")]
میتوانیم استایل سفارشی شده خودمان را مشخص کنیم.
<?xml version="1.0" encoding="utf-8" ?>
<resources>
<style name="MainTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="windowNoTitle">true</item>
<item name="windowActionBar">false</item>
</style>
</resources>
Navigation Menu
کتابخانه متریال دیزاین کتابخانه جدیدی میباشد که به اندروید اضافه شدهاست. توسط آن میتوانیم کنترلهای جدید را با استایلهای جدید برای Appهای اندروید، تولید کنیم. ابتدا نیاز به نصب Componentهای ذیل در زامارین میباشد.
استفاده که از کتابخانه متریال دیزاین
برای اینکه بتوانیم Navigation menu را ایجاد کنیم، باید از نیوگت، کتابخانههای Appcompat و Designlibrary را انتخاب و نصب نماییم و اگر نگارش ویژوال استودیوی شما 15.7.3باشد، از ابتدا بصورت اتوماتیک نصب شده است و احتیاجی به این مراحل نمیباشد. بدیهی است در زمان نصب باید از نرم افزارهای تحریم گذر نیز استفاده کرد.
ساخت Menu
NavigationMenu، منوی اصلی منو میباشد که با Swipping از گوشه راست به چپ، باز یا بسته میشود یا با کلیک بر روی دکمهی Menu بر روی Toolbar، منو را باز یا بسته میکند.
قدم اول: نصب منو
منظور همان اضافه کردن کتابخانهها است.
قدم دوم:
ابتدا باید گزینههای منو را در یک فایل xml تعریف نمود. هر گزینهی آن از دو بخش متن اصلی منو و ID منو تشکیل شدهاست.
بر روی دایرکتوری Resource راست کلیک کرده و یک دایرکتوری یا پوشه را به نام Menu ایجاد میکنیم. بر روی دایرکتوری منو، راست کلیک کرده و یک فایل Xml را به آن اضافه میکنیم. برای آنکه بتوانیم در این فایل دستورات ساخت منو را نوشته و به نحوی که توسط اندروید قابل خواندن و تبدیل به منو باشد، ساختار منو را از آدرس زیر ویژوال استودیو دانلود میکنیم.
<menu xmlns:android="http://schemas.android.com/apk/res/android">
توسط Group مجموعه گزینههای منو را را معرفی میکنیم.
هر گزینه در منو ی اصلی توسط یک آیتم مشخص میشود. برای آنکه هنگام کلیک بر روی هر گزینه از طریق برنامه نویسی بتوانیم گزینه انتخاب شده را شناسایی کنیم، یک آی دی منحصر بفرد را به هر گزینه اختصاص میدهیم. زمانیکه بر روی یک گزینه کلیک میشود، توسط این IDها میتوانیم شناسایی کنیم کدام گزینه انتخاب شدهاست.
خصوصیت Android :Title متن اصلی منو را مشخص میکند.
<?xml version="1.0" encoding="utf-8" ?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<group android:checkableBehavior="single">
<item android:id="@+id/menuItemHome" android:title="صفحه اصلی"></item>
<item android:id="@+id/menuItemInsertProduct" android:title="ورود کالا جدید" ></item>
<item android:id="@+id/menuItemListProduct" android:title="مشاهده کالاها"></item>
<item android:id="@+id/menuItemExit" android:title="خروج"></item>
</group>
</menu>
سپس باید در Layout مورد نظر همانند صفحه Main، ساختار اصلی برنامه شامل Toolbar و Menu را بصورت زیر تعریف نماییم:
<android.support.v7.widget.Toolbar
android:layout_width="match_parent"
android:id="@+id/toolbar1"
android:background="#33B86C"
android:minHeight="?android:attr/actionBarSize"
android:layout_height="wrap_content">
</android.support.v7.widget.Toolbar>
ساختار منو به صورت زیر است:
<?xml version="1.0" encoding="utf-8" ?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<group android:checkableBehavior="single">
<item android:id="@+id/menuItemHome" android:title="صفحه اصلی"></item>
<item android:id="@+id/menuItemInsertProduct" android:title="ورود کالا جدید" ></item>
<item android:id="@+id/menuItemListProduct" android:title="مشاهده کالاها"></item>
<item android:id="@+id/menuItemExit" android:title="خروج"></item>
</group>
</menu>
در Linearlayout ریشه، گزینه Fitssystemwindow را true میکنیم که سایز linearlayout را با سایز موبایل جدید اندازه میکند. سپس از toolbox، کنترل Toolbarرا به پنجره اضافه میکنیم که در بالای صفحه قرار میگیرد.
toolbar اضافه شده، toolbar استاندارد قبل از متریال دیزاین میباشد. در واقع toolbar اول، Toolbar استاندارد اندروید میباشد. برای آنکه از Toolbar متریال دیزاین استفاده کنیم، کنترلهای متریال دیزاین در بخش supportlibrary اضافه میشود و Toolbar متریال دیزاین را اضافه میکنیم. علامت ؟ یعنی اینکه میخواهیم از اندازه سیستمی استفاده کنیم. اگر بخواهیم حداقل سایز Toolbarبر اساس پیش فرض در دستگاههای مختلف باشد، از علامت Android :attr ? استفاده میکنیم. اگر بخواهیم حداقل ارتفاع پیشنهادی اندروید در هر موبایل متصل شود، از خصوصیت Action Bar Size استفاده میکنیم. این خصوصیت زمانی عمل میکند که Height آن Wrapcontent باشد.
گذاشتن دکمه منو: برای آنکه بتوانیم دکمه منو را به Toolbar اضافه کنیم، از دکمه Image Button استفاده میکنیم که یک دکمهی معمولی میباشد ولی خلاصهی آن عکس است. در خصوصیت Back ground دکمه، بصورت زیر نام فایل آیکن منو را در دایرکتوری Drawable، مشخص میکنیم و خصوصیت src آنرا null میکنیم تا تصویری بجز تصویر انتخابی نباشد.
برای آنکه بتوانیم پنجره اصلی منو را به صورتیکه دارای قابلیت حرکت به راست و چپ باشد، ایجاد کنیم، از کنترلی بهنام Drowerlayout استفاده میکنیم که بر روی صفحه قرار میگیرد. DrawerLayout در linearlayout ریشه قرار میگیرد و یا بعد از ToolBar و حتما باید خصوصیت fitsystemwindow کنترل Drawer را True کنیم. جهت نمایش گزینههای اصلی در Drawer از کنترل NavigationٰView استفاده میکنیم.
گزینههای منو در کنترلی به نام Navigationview قرار دارد. این کنترل باید در Drawerlayout قرار گیرد. توسط فضای نام منو، محل فایل xml را که منو درون آن قرار گرفته است، مشخص میکنیم. آدرس این دستور در این مسیر میباشد:
xmlns:app="http://schemas.android.com/apk/res-auto"
Layout gravity آن را end قرار میدهیم که از سمت راست قرار بگیرد. Fit system Window را هم True میکنیم تا گزینههای داخل آنرا هم Fit کند. Theme باید از نوع تمهای متریال دیزاین و با کلمه Them . App Compact. ligth.NoActionBar باشد. برای آنکه اکتیویتیها، متریال دیزاین را ساپورت کنند، میتوان از کلاس App compact Activity استفاده کنیم. Tool bar بصورت پیش فرض لیبل اکتیویتی را نشان میدهد و دستور زیر عنوان Toolbar را حذف میکند.
SupportActionBar.SetDisplayShowTitleEnabled(false);
مدیریت گزینههای منو
به محض انتخاب یک گزینه درون NavigationView، رخدادی به نام NavigationItemSelected صادر میشود که توسط آن میتوانیم گزینهی انتخاب شده را از طریق برنامه نویسی مدیریت کنیم. این کنترل در Android.Support.V7.Widget و NameSpace بالا قرار میگیرد. سپس یک رخداد گردان را با نام navigationItemSelected پیاده سازی میکنیم. اطلاعات مربوط به گزینهی انتخاب شده، در پارامتر دوم از این تابع NavigationView.NavigationItemSelectedEventArgs ذخیره میشود. ID، آیتم انتخاب شده در فایل Menu را باز میگرداند.
var navigationview = this.FindViewById<NavigationView>(Resource.Id.navigationView1);
navigationview.NavigationItemSelected += Navigationview_NavigationItemSelected;
private void Navigationview_NavigationItemSelected(object sender, NavigationView.NavigationItemSelectedEventArgs e)
{
Intent intent = null;
switch (e.MenuItem.ItemId)
{
case Resource.Id.menuItemHome:
break;
case Resource.Id.menuItemExit:
Finish();
break;
case Resource.Id.menuItemInsertProduct:
break;
case Resource.Id.menuItemListProduct:
break;
}
}
مدیریت اکتیویتیها توسط Menu
با انتخاب گزینه Menu باید اکتیویتی مربوطه انتخاب شود. بنابراین برای هر گزینهی منو یک Layout و اکتیویتی را ایجاد میکنیم و اجرا میکنیم. ولی در اکتیویتی جدید Toolbar وجود ندارد.
تکنیک ادغام:
برای آنکه در Layoutهای مختلف، تولبار و منو و یا هر View دیگری را بصورت مشترک استفاده کنیم، یک فایل xml را به دایرکتوری Layout اضافه میکنیم. دستور Merge میتواند تمام layoutها را به درون layoutهای دیگر مانند home,insert ادغام و یا تزریق کند. جهت استفاده از Merge در layoutهای دیگر نیاز به Id منحصر به فرد میباشد.
<?xml version="1.0" encoding="utf-8" ?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/toolbarlayout">
<android.support.v7.widget.Toolbar android:layout_width="match_parent" android:id="@+id/toolbar1" android:background="#33B86C" android:minHeight="?android:attr/actionBarSize" android:layout_height="wrap_content">
<ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/imageButton1" android:background="@drawable/mainmenu" android:layout_gravity="end" />
</android.support.v7.widget.Toolbar>
<android.support.v4.widget.DrawerLayout android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/drawerLayout1" android:fitsSystemWindows="true">
<android.support.design.widget.NavigationView android:minWidth="25px" android:minHeight="25px" android:layout_width="200dp" android:layout_height="match_parent" android:layout_gravity="end" app:menu="@menu/menu" android:id="@+id/navigationView1" android:fitsSystemWindows="true" />
</android.support.v4.widget.DrawerLayout>
</merge>
در اکتیویتیهای دیگر باید Toolbar و مدیریت گزینههای منو با کدهای مشابه Main انجام شود.
private void Navigationview_NavigationItemSelected(object sender, NavigationView.NavigationItemSelectedEventArgs e)
{
Intent intent = null;
switch (e.MenuItem.ItemId)
{
case Resource.Id.menuItemHome:
intent = new Intent(this, typeof(MainActivity));
break;
case Resource.Id.menuItemExit:
Finish();
break;
case Resource.Id.menuItemInsertProduct:
intent = new Intent(this, typeof(InsertActivity));
break;
case Resource.Id.menuItemListProduct:
intent = new Intent(this, typeof(ListProductsActivity));
break;
}
if (intent != null) { }
}
بنابراین دستورات xmlTollbar و darawer layout در تمامی Layoutها و دستورات سی شارپ، کنترل کننده Toolbar و منو در تمامی اکتیویتیها تکرار شدهاند.
حل مشکلات Layout
یک فایل Xml را به Layout اضافه میکنیم و درون آن Tag merge و کدهای مشترک Drawer out و Toolbar را داخل تگ Merge اضافه میکنیم. جهت استفاده از کدهای (مقدار فایل ایکس ام ال ساخته شده که Tag merge داخل آن است) Merge، در layout های دیگر، از دستور Include استفاده میکنیم.
نام لیآوت را در خصوصیت Layout اضافه میکنیم. برای آنکه کدهای سی شارپ کنترل کنندهی Toolbar و Menu چندین Toolbar وجود دارد که در یکی از آنها یک کلاس واسط از کلاس app compat Activity را به ارث میبریم. تابع Protected را از آن بازنویسی کرده و تمام کدهای مدیریت Toolbar و منو را در آن پیاده سازی میکنیم. تمام اکتیویتیهای برنامه را از این کلاس به ارث میبریم. بنابراین تابع InitieToolbar به تمامی فرزندان نیز به ارث برده میشود. در زمان اجرای دستورات، this ، اکتیویتی جاری میباشد.
public class BaseAcitivity : AppCompatActivity
{
protected void InitieToolbar()
{
var toolbar = this.FindViewById<widgetV7.Toolbar>(Resource.Id.toolbar1);
this.SetSupportActionBar(toolbar);
//SupportActionBar.SetDisplayShowTitleEnabled(false);
var imagebutton = toolbar.FindViewById<ImageButton>(Resource.Id.imageButton1);
imagebutton.Click += Imagebutton_Click;
var navigationview = this.FindViewById<NavigationView>(Resource.Id.navigationView1);
navigationview.NavigationItemSelected += Navigationview_NavigationItemSelected;
}
private void Navigationview_NavigationItemSelected(object sender, NavigationView.NavigationItemSelectedEventArgs e)
{
Intent intent = null;
switch (e.MenuItem.ItemId)
{
case Resource.Id.menuItemHome:
intent = new Intent(this, typeof(MainActivity));
break;
case Resource.Id.menuItemExit:
Finish();
break;
case Resource.Id.menuItemInsertProduct:
intent = new Intent(this, typeof(InsertActivity));
break;
case Resource.Id.menuItemListProduct:
intent = new Intent(this, typeof(ListProductsActivity));
break;
}
if (intent != null)
StartActivity(intent);
}
private void Imagebutton_Click(object sender, EventArgs e)
{
var drawerlayout = this.FindViewById<DrawerLayout>(Resource.Id.drawerLayout1);
if (drawerlayout.IsDrawerOpen(Android.Support.V4.View.GravityCompat.End) == false)
{
drawerlayout.OpenDrawer(Android.Support.V4.View.GravityCompat.End);
}
else
{
drawerlayout.CloseDrawer(Android.Support.V4.View.GravityCompat.End);
}
}
}
اگر بخواهیم یک تم در تمامی اکتیویتیها به صورت سراسری استفاده شود، از فایل تنظمیات اندروید بنام AndroidManifest در دایرکتوری Properties استفاده میکنیم و در بخش Application Theme، نام تم را مشخص میکنیم:
android:theme="@style/Theme.AppCompat.Light.NoActionBar"
ساخت TabPage
پیشنیاز: نصب کتابخانههای متریال دیزاین همانند قبل و طبق ورژن Sdk نصب شده
اگر بخواهیم چندین صفحه را بر روی یکدیگر Stack و یا Overload نماییم، از Tabpage استفاده میکنیم. صفحاتیکه از TabPage استفاده میکنند، با انگشت جابجا میشوند و همانند برنامهی واتساپ Fragment میباشند و هر Fragment دارای layout و اکتیویتی مربوط به خود میباشد. معماری layout آن بصورت زیر است:
ToolBar، در بالای فرم قرار میگیرد. TabLayou که بصورت TabPage آنها را به عهده دارد. Viewpager مدیریت Layoutها را به هنگام Swipe یا جابجایی به عهده دارد.
یک layout را برای Toolbar قرار میدهیم. سپس Layout اصلی main را طراحی میکنیم. پس از اضافه کردن ToolBar، ابزار TabLayout را در بخش SupportLibrary متریال دیزاین انتخاب و در صفحه میکشیم. TabLayout در پایین Toolbar قرار میگیرد و با انتخاب رنگ یکسان برای هر دو، متصل و یکنواخت به نظر میرسد. سپس از Layout از Toolbar آیتم ViewPager را بر روی صفحه قرار میدهیم. اگر LayoutWeight آن را یک قرار دهیم، تمام ارتفاع صفحه را به ما تخصیص میدهد. زمانیکه در TabLayout تبها جابجا میشوند یا بر روی یک آیکن کلیک میشود، صفحه مربوطه در بخش ViewPager به کاربر نمایش داده میشود. هر Page یک فرگمنت میباشد. به ازای هر فرگمنت یک Layout به دایرکتوری layout اضافه کرده و به ازای هر layoutFragment یک Activity Fragment را اضافه میکنیم. یک اکتیویتی از نوع را Android.Support.v4.AppFragment ایجاد میکنیم.
public class Fragment1 : Android.Support.V4.App.Fragment
{
public override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
}
public override View OnCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState)
{
return inflater.Inflate(Resource.Layout.FragmentLayout1, container, false);
}
}
ابتدا باید viewpager در Layout اصلی را پیدا کرده و با دستور زیر به Tablayout متصل کنیم:
var tablayout = FindViewById<Android.Support.Design.Widget.TabLayout>(Resource.Id.tabLayout1);
var viewpager = FindViewById<ViewPager>(Resource.Id.viewPager1);
tablayout.SetupWithViewPager(viewpager);
زمانیکه آیکن را در TabLayout انتخاب میکنیم یا با انگشت Swipe میکنیم، به ترتیب بین صفحات که از Position صفحه آغاز شدهاند، حرکت میکنیم، باید فرگمنت همان Position را نشان دهیم و این مدیریت توسط بخشی بهنام Adapter انجام میشود. یک Adapter را به کنترلر اضافه میکنیم و از کلاس Fragment pager adapter به ارث میبریم. بر روی کلاس Fragment pager adapter ، دکمههای کنترل و نقطه را میزنیم و سپس کلاس را پیاده سازی میکنیم. در این حالت دو تابع را به ما میدهد: تابع Get item .count مجددا بر روی کلاس پدر راست کلیک میکنیم. در تابع کانت تعداد کل صفحهها را (Layout ها) را انتخاب میکنیم. هرگاه از یک صفحه به صفحهی دیگری انتقال پیدا کنیم، موقعیت صفحه جدیدی که از یک شروع میشود را به تابع Get Item بر اساس موقعیت Object از fragment مربوطه new کرده و بعنوان خروجی باز میگرداند.
class TabFragmentAdapter : FragmentPagerAdapter
{
public TabFragmentAdapter(FragmentManager fm) : base(fm)
{
}
public override int Count => 3;
public override Fragment GetItem(int position)
{
switch (position)
{
case 0: return new Fragment1();
case 1: return new Fragment2();
case 2: return new Fragment3();
default: return new Fragment1();
}
}
//int f1() { return 100; }
//int f1 => 100;
}
و در اکتیویتی اصلی، کد زیر را برای Load فرگمنتها نیز قرار میدهیم:
viewpager.Adapter = new TabFragmentAdapter(this.SupportFragmentManager);
آیکن برای TabPage
سپس اگر بخواهیم آیکنهای Tab را به ترتیب تعریف کنیم، از تابع Gettabat استفاده میکنیم. پارامتر ورودی آن موقعیت Tab page میباشد و Set icon هم آیکنهای دایرکتوری Drawable را انتخاب میکند.
tablayout.GetTabAt(0).SetIcon(Resource.Drawable.iconCall);
نمایش متن همراه با عکس
اگر بخواهیم آیکنهای تب پیج را سفارشی کنیم، از Layout استفاده میکنیم که عرض و ارتفاع آن wrap Content باشند و درون آن یک Text view که معادل Lable میباشند، قرار میدهند:
View iconlayout1 = LayoutInflater.Inflate(Resource.Layout.custom_TabIconLayout, null);
var txt = iconlayout1.FindViewById<TextView>(Resource.Id.tabTextIcon);
txt.Text = "تماس";
txt.SetCompoundDrawablesWithIntrinsicBounds(Resource.Drawable.iconCall, 0, 0, 0);
tablayout.GetTabAt(0).SetCustomView(iconlayout1);
کدهای مطلب جاری برای دریافت: Navigation-TabPage-samples.zip