مشخصات مقاله
-
3702
-
0.0
-
8143
-
0
-
0
طرح بندی چند قابه-multiple-pane layout در اندروید
دوره آموزش برنامه نویسی اندروید
کلیه حقوق مادی و معنوی این مقاله متعلق به آموزشگاه تحلیل داده می باشد و هر گونه استفاده غیر قانونی از آن پیگرد قانونی دارد.
ایجاد طرح بندی چند قابه قسمت اول
بکاربردن Fragment ها در برنامه های کاربردی اندروید
در این مبحث با نحوه ی استفاده از کلاس fragment برای ایجاد طرح بندی چند قابه (multiple-pane layout) در اپلیکیشن های اندروید آشنا می شوید (برای مثال برنامه هایی که بر اساس پهنای موجود صفحه نمایش، مقیاس و اندازه ی تصویر خود را تنظیم می کنند). این مقاله ی آموزشی بر پایه ی Eclipse 4.4 (Lunu)، Java 1.7 و Android 5.0. (Lollipop) نوشته شده است.
فهرست محتوا
1. مبانی اندروید
2. Fragments
طرح بندی تک قابه (single-pane layout) و چند قابه (multiple-pane layout) چیست؟
Fragment چیست؟
Fragments و دسترسی به Context
مزایای استفاده از fragments
آموزش نحوه ی پشتیبانی از صفحه نمایش با اندازه های متفاوت به کمک fragments
3. آموزش نحوه ی تعریف و استفاده از fragments
نحوه ی تعریف کردن fragment
برقراری ارتباط با برنامه از طریق fragment
4. طول عمر fragment
5. تعریف fragments برای activity مورد نظر
افزودن fragment ها به صورت ایستا (static)به فایل layout
نحوه ی مدیریت fragment ها به صورت پویا (dynamic)
بررسی کنید آیا fragment در طرح کلی (layout) موجود است
بررسی حالت یا مُد activity (single)، double، multiple mode
6. افزودن fragment transition به پشته (backstack)
7. تعریف animations برای fragment transition
8. ماندگار کردن داده ها (persisting data) در fragment ها
ماندگار کردن اطلاعات بین راه اندازی مجدد برنامه های کاربردی
ماندگار کردن اطلاعات بین تغییرات پیکربندی (configuration changes)
9. استفاده از fragment برای پردازش پیش زمینه ای
Headless fragments
حفظ Headless fragment ها به منظور مدیریت تغییرات پیکربندی
10. نکات آموزشی درباره ی fragment ها
هدف اصلی این تمرین آموزشی
ایجاد طرح های کلی (layout) متعارف و استاندارد
ایجاد کلاس های fragment
RSSfeedActivity
تست کردن نمونه
11. طرح بندی (layout) با نمای عمودی (portrait mode)
ایجاد طرح کلی با نمای عمودی
DetailActivity
تنظیم و اصلاح RSSfeedActivity
اجرا
12. تمرین : افزودن fragment ها به activity به صورت پویا (dynamic)
هدف این تمرین
استفاده از placeholder برای فایل های layout
تعیین حالت (dual-pane mode یا single-pane) با تعریف متغیر
جایگزین کردن fragment ها با استفاده از fragment manager
1. مبانی اندروید
برای درک بهتر این مبحث لازم است پیش زمینه ای از برنامه نویسی و طراحی تحت اندروید داشته باشید. در دروس پیشین کلیه ی مقدمات و پیش نیازهای آموزشی تشریح شد.
2. Fragments
طرح بندی تک قابه (single-pane layout) و چند قابه (multiple-pane layout) چیست؟
دستگاه های اندروید با چگالی تصویر و اندازه صفحه نمایش های گوناگون عرضه می شوند.
Panel یا همان pane بخشی از رابط کاربری را تشکیل می دهد. کلمه ی pane، واژه ای عمومی است که به مفهوم چندین view گنجانده شده در view ای مرکب (واحد) اشاره دارد. view مرکب بر اساس مقدار فضای موجود تنظیم می شود.
در صورت نبود فضای کافی، تنها یک panel نمایش داده می شود. به این نوع طرح بندی، single-pane layout (طرح بندی تک قابی( می گویند.
چنانچه فضای کافی وجود داشته باشد، چندین panel نمایش داده می شود.
Fragment چیست؟
Fragment عبارتند از کامپوننت یا مولفه ای مستقل که توسط هر activity ای می تواند مورد استفاده قرار بگیرد. fragment قابلیت (functionality) های لازم را در خود کپسوله سازی کرده و به همین دلیل استفاده ی مجدد از آن در activity و layout سهل می باشد.
Fragment در متن (context) activity قرار دارد، با این وجود طول عمر (life-cycle) و رابط کاربری مجزا و مختص به خود را دارد. این امکان نیز وجود دارد که fragment را بدون رابط کاربری تعریف کرد (نمونه ی آن headless fragment است).
Fragments و دسترسی به Context
Fragment ها زیرکلاس Context نمی باشد. به همین دلیل باید از متد getActivity ()برای دستیابی به activity والد استفاده کرد.
مزایای استفاده از fragments
Fragment ها استفاده ی مجدد از کامپوننت ها را در طرح بندی های متفاوت آسان می سازند، به عنوان مثال، می توانید برای گوشی طرح بندی تک-قابی و برای تبلت چند قابی تعبیه کنید. می توان برای گوشی های هوشمند اندروید نیز از fragment هایی استفاده کرد که از طرح بندی های مختلف برای هر دو نمای افقی و عمودی استفاده و پشتیبانی کند.
به این خاطر که می توان fragment ها را به صورت پویا (dynamic) به activity افزود یا از آن حذف کرد، قابلیت ایجاد رابط کاربری بسیار انعطاف پذیر بوجود می آید.
معمول ترین نمونه آن، فهرستی از آیتم های activity است که روی تبلت در تنها یک صفحه قابل نمایش می باشد (برای مثال، می توانید روی آیتم مورد نظر در سمت راست صفحه کلیک کنید)، ولی در یک گوشی اندروید مجبورید به detail screen دیگری برای مشاهده ی تمام جزئیات مراجعه کنید.
مبحث زیر فرض می گیرد شما دو fragment دارید (یکی اصلی / main و دیگری detail / جانبی(. همچنین دو activity که یکی main و دیگری detailed خوانده می شود برای این بخش استفاده می کنیم. توجه داشته باشید که در تبلت activity اصلی شامل هر دو fragment در طرح کلی خود می باشد، در حالی که گوشی هوشمند (handset) تنها fragment اصلی (main) را در activity اصلی دربر خواهد داشت.
آموزش نحوه ی پشتیبانی از صفحه نمایش با اندازه های متفاوت به کمک fragments
می توان fragment های activity را در فایل layout تعریف کرد (که به آن تعریف ایستا / static definition گفته می شود) یا آن ها را در زمان اجرا (runtime) معرفی کرد (که به آن تعریف پویا / dynamic definition گفته می شود).
به منظور نمایش fragment های متفاوت بر اساس فضای موجود در activity های مورد نظر
از activity ای استفاده کنید که در تبلت یا گوشی، تنها دو fragment نمایش می دهد. در این مورد، در صورت نیاز باید fragment مربوطه را در زمان اجرا اصلاح کرد. در این مثال شما نمونه هایی از کلاس FrameLayoutرا به عنوان placeholder در طرح کلی خود تعریف کرده، سپس fragment ها را در زمان اجرا به آن ها اضافه می کنید.
در گوشی های هوشمند (handset) از activity های مجزا برای هر fragment (به عنوان میزبان( استفاده کنید. به عنوان مثال اگر به تبلت توجه کرده باشید، رابط کاربری (UI) آن از دو fragment در یک activity استفاده می کند، برای گوشی نیز از همین activity استفاده کنید ولی طرح کلی جایگزینی برای آن درنظر بگیرید که تنها یک fragment را در خود جای می دهد. چنانچه detailed fragment از قبل آنجا حضور دارد، main activity به fragment مذکور دستور بروز رسانی خود را می دهد، اما اگر detail fragment در دسترس نباشد، activity اصلی detailed activity را راه اندازی می کند.
لازم به ذکر است که تعریف پویا انعطاف پذیرتر بوده، در عین حال پیاده سازی آن کمی دشوارتر می باشد.
3. آموزش نحوه ی تعریف و استفاده از fragments
نحوه ی تعریف کردن fragment
برای تعریف fragment جدید، کافی است کلاس android.app.Fragment یا یکی از زیرکلاس های آن را گسترش و توسعه دهید (به طور مثال، زیر کلاس های ListFragment، DialogFragment، PreferenceFragment یا WebViewFragment (.
مثال
package com.example.android.rssfeed;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
public class DetailFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater، ViewGroup container،
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_rssitem_detail،
container، false);
return view;
}
public void setText(String item) {
TextView view = (TextView) getView().findViewById(R.id.detailsText);
view.setText(item);
}
}
برقراری ارتباط با برنامه از طریق fragment
برای افزایش قابلیت استفاده ی دوباره از fragment ها، آن ها نباید مستقیم با هم ارتباط داشته باشند، بلکه هرگونه برقراری ارتباط بین fragment ها لازم است از طریق activity میزبان (host activity) صورت گیرد.
برای این منظور، fragment باید رابط کاربری به عنوان نوع داخلی (inner type) تعریف کند و activityای که از fragment مزبور استفاده می کند باید سرانجام این رابط کاربری را بکار بگیرد. از این طریق مانع از هرگونه آگاهی fragment از activity میزبان (activity ای که fragment نام برده را بکار می برد) می شوید. fragment با بررسی متد onAttach() چک می کند آیا activity رابط مورد نظر را به درستی پیاده سازی کرده یا خیر.
به عنوان مثال، فرض کنید fragment ای دارید که باید مقداری را به activity والد خود مخابره کند مانند مثال زیر
package com.example.android.rssfeed;
import android.app.Activity;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
public class MyListFragment extends Fragment {
private OnItemSelectedListener listener;
@Override
public View onCreateView(LayoutInflater inflater، ViewGroup container،
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_rsslist_overview،
container، false);
Button button = (Button) view.findViewById(R.id.button1);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
updateDetail();
}
});
return view;
}
public interface OnItemSelectedListener {
public void onRssItemSelected(String link);
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (activity instanceof OnItemSelectedListener) {
listener = (OnItemSelectedListener) activity;
} else {
throw new ClassCastException(activity.toString()
+ " must implemenet MyListFragment.OnItemSelectedListener");
}
}
@Override
public void onDetach() {
super.onDetach();
listener = null;
}
// may also be triggered from the Activity
public void updateDetail() {
// create a string just for testing
String newTime = String.valueOf(System.currentTimeMillis());
// inform the Activity about the change based
// interface defintion
listener.onRssItemSelected(newTime);
}
}
4. طول عمر fragment (life-cycle)
طول عمر یک fragment به طول عمر activity میزبان مرتبط است.
اگرچه fragment چرخه ی دوام خود را دارد، اما از این لحاظ به طول عمر activity والد نیز وابسته است.
![]()
در صورت متوقف شدن activity، fragment های آن هم متوقف می شوند. به همین روش در صورت نابود شدن activity، fragment های آن نیز نابود می شوند.
جدول 1. Fragment life-cycle
|
متد |
توصیف و شرح عملکرد |
|
onAttach() |
نمونه ی fragment ونمونه ی activity را به هم متصل می کند. البته fragment و activity کاملاً مقداردهی اولیه نمی شوند، بلکه معمولاً یک ارجاع / reference به activity داخل این متد قرار داده می شود که ازfragment مزبور برای ادامه و تکمیل فرایند مقداردهی اولیه /initialization استفاده می کند. |
|
onCreate() |
fragment لازمه را ایجاد می کند. متد onCreate() پس از متد onCreate() اکتیویتی و پیش از متد onCreateView() فرگمنت فراخوانی می شود. |
|
onCreateView() |
نمونه ی fragment سلسله مراتب view / view hierarchy خود را بوجود می آورد. fragment رابط کاربری مورد نیاز خود را در متد onCreateView() ایجاد می کند. اینجا می توانید با فراخوانی متد inflate () از شی inflator که به عنوان پارامتر به این متد ارسال شده، طرح کلی را ظاهر (inflate) کنید. در این متد شما نمی توانید با activity تعامل داشته و ارتباط برقرار کنید زیرا که activity مربوطه هنوز کامل تعیین وضعیت و مقداردهی اولیه نشده است. البته نیازی به پیاده سازی این متد برای headless fragment نیست. view های ظاهر شده (inflated views) بخشی از سلسه مراتب view و در نهایت activity میزبان می شود. |
|
onActivityCreated() |
onActivityCreated() پس از متد onCreateView() و زمانی که activity میزبان به وجود می آید، فراخوانده می شود. Activity، نمونه ی fragment و همچنین سلسه مراتب activity همگی در این زمان بوجود آمده اند. حال می توان با استفاده از متد findViewById() به view دسترسی پیدا کرد. در این متد جهت نمونه سازی اشیا به Context object نیاز دارید. |
|
onStart() |
این متد بلافاصله پس از ظاهر شدن fragment فراخوانی می شود. |
|
onResume() |
Fragment به وسیله ی این متد فعال می شود. |
|
onPause() |
در این برهه، fragment مورد نظر قابل رویت می باشد ولی دیگر فعال نیست. |
|
onStop() |
با استفاده از این متدfragment متوقف شده و دیگر قابل رویت نمی باشد. |
|
onDestroyView() |
به وسیله ی این متد view (مربوط به) fragment مورد نظر نابود می گردد. در صورت بازسازی fragment مذکور از طریق backstack، ابتدا این متد فراخوانی می شود سپس متد onCreateView صدا زده می شود. |
|
onDestroy() |
در محیط اندروید هیچ تضمینی به فراخوانی آن وجود ندارد (به این معنا که ممکن است هیچ گاه توسط پلت فرم اندروید صدا زده نشود). |
5. تعریف fragments برای activity مورد نظر
افزودن fragment ها به صورت ایستا (static) به فایل layout
می توانید برای استفاده از fragment جدید، آن را به صورت ایستا به XML layout خود اضافه کنید. در مثال زیر خصیصه ی android : name به کلاس مربوط اشاره دارد، همان طور که در تکه کد زیر مشاهده می کنید.
<?xml version="1.0" encoding="utf-8" ?>
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:baselinealigned="false"
android:orientation="horizontal">
<fragment android:id="@+id/listFragment"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
class="com.example.android.rssfeed.MyListFragment">
</fragment>
<fragment android:id="@+id/detailFragment"
android:layout_width="0dp"
android:layout_weight="2"
android:layout_height="match_parent"
class="com.example.android.rssfeed.DetailFragment">
</fragment>
</linearlayout>
چنانچه برای پیکربندی های متفاوت دستگاه (نحوه ی دیگر پیکربندی دستگاه)، فایل های static layout مجزا و متفاوتی دارید، می توانید از این سناریو نهایت استفاده را ببرید.
نحوه ی مدیریت fragment ها به صورت پویا (dynamic)
می توان با استفاده از متد getFragmentManager ()، به کلاس fragmentmanager در activity دسترسی پیدا کرد. کلاس مزبور این امکان را در اختیار شما قرار می دهد که fragment های مورد نظر را از شَمای کلی (layout) فعالیت (activity) خود حذف کرده یا به آن fragment جدید اضافه کنید و یا حتی آن ها را با fragment های دیگر جایگزین کنید.
این اصلاحات باید در قالب یک تراکنش (transaction) و توسط کلاس FragmentTranscationصورت گیرد.
به منظور اصلاح fragment های بیان شده، لازم است یک FrameLayout placeholder در فایل layout خود تعریف کنید.
<?xml version="1.0" encoding="utf-8" ?>
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<framelayout android:id="@+id/listcontainer"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<framelayout android:id="@+id/detailscontainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />
</linearlayout>
همچنین برای جایگزین کردن fragment (درونcontainer) از FragmentManager استفاده کنید.
// get fragment manager
FragmentManager fm = getFragmentManager();
// add
FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.your_placehodler، new YourFragment());
// alternatively add it with a tag
// trx.add(R.id.your_placehodler، new YourFragment()، "detail");
ft.commit();
// replace
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.your_placehodler، new YourFragment());
ft.commit();
// remove
Fragment fragment = fm.findFragmentById(R.id.your_placehodler);
FragmentTransaction ft = fm.beginTransaction();
ft.remove(fragment);
ft.commit();
همان طور که مستحضر هستید، fragment ای جدید جایگزین fragment جاری (منظور فرگمنت موجود در container است) می شود.
اگر می خواهید تراکنش (transaction) مربوط را به backstack اندروید اضافه کنید، باید از متد addtoBackStack () برای این منظور کمک بگیرید. این کار عمل یا کنش (action) مورد نظر را به تاریخچه ی پشته ی history stack activity اضافه می کند (به طور مثال، به شما اجازه می دهد اصلاحات وارد آمده به Fragment را با استفاده از دکمه ی برگشت (back button) به حالت قبلی بازگردانید).
