در بحث گذشته کنترلهای مورد نظر را بصورت داینامیک تولید کردیم که در طراحی Appهای پیچیده مناسب نمیباشد و بهتر است فرم و طراحی گرافیکی را از قبل آماده کرده و در activity اجرا نماییم. به فرمهای از قبل طراحی شده، layout گفته میشود. layoutها با فرمت xml ساخته میشوندو بنابراین به زبان سی شارپ مربوط نمیباشد.
در زامارین 2 نوع layout داریم
1: صفحات razor از قبل پردازش شده PreProcessRazorPaged
2: layout استاندارد اندروید با ساختار xml که در زامارین ساخته میشود. در اینجا یک طراح جهت طراحی گرافیکی اندروید نصب میشود که خروجی را به xml تبدیل میکند.
محل استاندارد طراحی layout
در دایرکتوری resource بر روی دایرکتوری layout راست کلیک نموده و add new item و سپس android layout را انتخاب میکنیم که در این حالت یک فایل با پسوند xml اضافه میشود. با انتخاب layout و زدن دکمه f4، پنجره properties باز شده و میتوانیم خصوصیات layout را تغییر دهیم.
پس از ایجاد layout که دستورات غیر اجرایی (مرده) میباشد، بایستی آنها به کلاسهای کنترل معادل خود در اندروید تبدیل شوند که به این عملیات inflating و یا inflation نیز گفته میشود. پس از عملیات inflating میتوان کنترلها را پیدا کرده و آنها را برنامه نویسی کنیم.
FindViewbyid در پارامتر ورودی خود از طریق Resource.id، نام کنترل را دریافت نموده و بصورت Object باز میگرداند که بایستی نتیجه خروجی را به کلاس همان کنترل Cast نماییم:
Button btn = FindViewById<Button>(Resource.Id.button1);
در اینجا جهت ساده شدن دستور Find، از ساختار Generic استفاده میشود. در این روش پس از دستور FindviewById، نوع کنترل را مشخص مینماییم و نتیجه خروجی را در متغیری از نوع var ذخیره نماییم که بطور اتوماتیک در سی شارپ نوع var به نوع آن شیء تبدیل میشود.
EditText همان Textbox خودمان میباشد. در Toolbox کنترلی به نام PlainText یک TextBox را به layout اضافه میکند؛ ولی در Activity یا همان برنامه نویسی، نام کلاس اصلی Textbox جهت برنامه نویسی، EditText میباشد:
FindViewById<EditText>(Resource.Id.txtname).Text = "";
که برای دسترسی مستقیم به Text و مقدار دهی به Property و یا Eventها، اینطور هم استفاده میشود.
ساخت برنامههای چندین فرمی
هر layout یک اکتیویتی مربوط به خودش را دارد. اگر بخواهیم بین فرمهای مختلف حرکت کنیم، به ازای هر فرم، یک Activity کنترل کننده همان فرم را اضافه میکنیم. در یک Activity با ارسال سیگنالی به نام Intent که پارامتر اول آن، Activity مبدا یا This و پارامتر دوم آن، Activity مقصد میباشد. سپس به سیستم عامل اطلاع میدهیم که میخواهیم این سیگنال را ارسال کنیم و ارسال آن با StartActivity میباشد:
Intent _intent = new Intent(this, typeof(Activity2));
StartActivity(_intent);
ارسال پارامتر
جهت ارسال دادههای معمولی bool,double,string,float,long,... و آرایه ای از آن، از دستور PutExtra استفاده میشود:
Intent _intent = new Intent(this, typeof(Activity2));
string Test="Vlaueone";
_intent.PutExtra("mainactivity", Test);
StartActivity(_intent);
گرفتن پارامتر
در Activity مقصد، اطلاعات مربوط به سیگنال ارسال شده، در مقادیر Global intent ذخیره میشود و بر اساس نوع داده ارسال شده، تابع GetExtra را مینویسیم:
string Getintent = Intent.GetStringExtra("mainactivity");
ارسال object از کلاسها بین Activityها
در زامارین، object کلاسها را به یک string استاندارد بصورت json تبدیل میکنیم و به این عملیات Serialization گفته میشود. این کار نیز توسط کتابخانههای مختلفی انجام میشود مانند NewtonSoft. سپس رشته json ایجاد شده را با تابع PutExtra، به همراه سیگنال Intent، به Activity دوم پاس میدهیم:
List<Product> products;
products = new List<Product>();
Product p = new Product
{
name = FindViewById<EditText>(Resource.Id.mainedittextname).Text,
price = Convert.ToInt32(FindViewById<EditText>(Resource.Id.mainedittextprice).Text)
};
products.Add(p);
Intent _intent = new Intent(this, typeof(Activity2));
_intent.PutExtra("products", JsonConvert.SerializeObject(products));
StartActivity(_intent);
در Activity دوم آنرا توسط کتابخانهی NewtonSoft، به کلاس اصلی DeSerialize میکنیم که عملیات آن با دستورات ذیل انجام میشود:
var p = JsonConvert.DeserializeObject<List<Product>>(Intent.GetStringExtra("products"));
foreach (var item in p)
{
Toast.MakeText(this, $" {item.name} , {item.price}",
ToastLength.Long).Show();
};
در کد بالا Toast را در حلقه میبینید که در واقع همانند alert در وب و یا همانند MessageBox در ویندوز فرم یا Wpf عمل میکند. در پارامتر اول به آن میگوییم که برای این اکتیویتی است. پارامتر دوم آن جهت نمایش مقدار و در پارامتر سوم، مدت نمایش طولانی برای آن در نظر گرفته شده است و با جایگزینی Long با Short، زمان نمایش آن کوتاهتر میشود و در آخر با show هم آن را نمایش میدهد.
در ادامه کلاسهای مورد نظر را جهت تعریف در دایرکتوری Model ایجاد میکنیم. مثل کلاس Product که بصورت Public نیز میباشد. از منوی Tools -> Nuget Package Management -> Manage nuget Package for Solution را انتخاب و سپس NewtonSoft را اضافه میکنیم.
از متد Finish برای اتمام کار و یا پایان Activity نیز استفاده میشود.
AleartDialog
AlertDialog زیر کلاس Dialog است که میتواند یک، دو و یا سه دکمه را نمایش دهد. اگر فقط میخواهید یک رشته را در این کادر محاوره ای نمایش دهید، از روش SetMessage استفاده کنید. قطعه کد زیر میتواند برای ایجاد یک AlertDialog ساده با دو دکمه حذف و لغو استفاده شود:
//set alert for executing the task
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.SetTitle("Confirm delete");
alert.SetMessage("Lorem ipsum dolor sit amet, consectetuer adipiscing elit.");
alert.SetPositiveButton("Delete", (senderAlert, args) =>
{
Toast.MakeText(this, "Deleted!", ToastLength.Short).Show();
});
alert.SetNegativeButton("Cancel", (senderAlert, args) =>
{
Toast.MakeText(this, "Cancelled!", ToastLength.Short).Show();
});
Dialog dialog = alert.Create();
dialog.Show();
DialogFragment
AleartDialog فقط میتواند اطلاعات محدودی را نمایش دهد؛ مانند نمایش حداکثر یک یا دو Button,EditText به کاربر. در حالیکه در برنامه نیاز به دیالوگی با ظاهر سفارشی داریم تا بتوانیم ظاهر آن را تغییر دهیم.
Fragment
با استفاده از Fragment میتوانیم layout هایی از پیش طراحی شده را بسازیم و چند صفحه را بصورت layout با Activity پیاده سازی کنیم. در اینجا اگر layout دوم load بشود، در این حالت اگر نیاز به ورود دادههای مورد نیاز از طریق layout اول یا اصلی را داشته باشد، برای کاربر خسته کننده میشود. از این رو بهتر است layout دوم کوچکی بر روی layout اول یا اصلی نمایش داده شود. از این رو میتوان بجای layout از DialogFragment استفاده کنیم.
کلاس DialogFragment میتواند دیالوگی را در وسط صفحه نمایش دهد ولی view و ظاهر آن از یک layout از قبل ساخته شده load و نمایش داده میشود. این کار از طریق تکنیک Inflate انجام میشود.
در ادامه یک کلاس را از Dialog Fragment مشتق کرده و در تابع oncreate، ظاهر و یا layout از پیش طراحی شده را بصورت ذیل در حافظه load کرده و بعنوان نتیجه خروجی باز میگردانیم:
public class DialogFragmentActivity : DialogFragment
{
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
var dlg = inflater.Inflate(Resource.Layout.layoutDialog, container, false);
return dlg;
}
}
و در اکتیویتی اصلی برای فراخوانی کلاس فرگمنت از کد زیر استفاده میشود:
DialogFragmentActivity dlg = new DialogFragmentActivity ();
dlg.Show(this.FragmentManager, "Fragment");
پارامتر اول در واقع بعنوان fragment manager معاون اکتیویتی میباشد و در پارامتر دوم هم یک اسم دلخواه را برای آن انتخاب میکنیم. در متد باز نویسی شده On start نیز میتوان طول و عرض آن و رنگ پشت زمینه Fragment را تغییر داد که کدهای آن را در زیر میتوانید مشاهده کنید:
public override void OnStart()
{
base.OnStart();
Dialog dialog = Dialog;
if (dialog != null)
{
dialog.Window.SetLayout(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.WrapContent);
dialog.Window.SetBackgroundDrawable(new ColorDrawable(Color.Transparent));
}
}
اگر برنامه را اجرا و فرگمنت را نیز اجرا کنید، میبینید که یک کادر، بصورت Header در بالای فرگمنت نمایان شدهاست که زیاد جالب نیست. برای حذف Header فرگمنت، از کد ذیل میتوانید استفاده کنید:
public override Dialog OnCreateDialog(Bundle savedInstanceState)
{
Dialog NotTitle = base.OnCreateDialog(savedInstanceState);
NotTitle.Window.RequestFeature(WindowFeatures.NoTitle);
return NotTitle;
}