مشخصات مقاله
-
3254
-
0.0
-
5474
-
0
-
0
ایجاد طرح بندی چند قابه - fragmentدر اندروید
دوره آموزش برنامه نویسی اندروید
کلیه حقوق مادی و معنوی این مقاله متعلق به آموزشگاه تحلیل داده می باشد و هر گونه استفاده غیر قانونی از آن پیگرد قانونی دارد.
ایجاد طرح بندی چند قابه قسمت دوم
بررسی کنید آیا fragment در طرح کلی (layout) موجود است.
برای بررسی این که آیا fragment مربوطه بخشی از شَمای کلی دلخواه (layout) شده یا خیر، از کلاس FragmentManager بهره گیرید. متد isInLayout ()در صورتی کار می کند که fragment نام برده از طریق layout به activity مورد نظر افزوده شده باشد.
DetailFragment fragment = (DetailFragment) getFragmentManager().
findFragmentById(R.id.detail_frag);
if (fragment==null || ! fragment.isInLayout()) {
// start new Activity
}
else {
fragment.update(...);
}
بررسی حالت یا مُد (single، double،multiple mode) activity
چندین روش برای بررسی مُد وجود دارد. یک راه این است که configuration file را در پوشه ی منبع Values پروژه ی خود با دو مقدار تعریف کنید. یک مقدار به صورت پیش فرض false می شود. با تعریف configuration file دیگر، مقدار ثانوی را به true تنظیم کنید.
مثال زیر همان فایل پیکربندی config. xml پیش فرض است.
<resources>
<item type="bool" name="dual_pane">false</item>
</resources>
حال در پوشه ی values-land فایل پیکربندی config.xml با مقداری متفاوت قرار می دهید، به مثال زیر توجه کنید.
<resources>
<item type="bool" name="dual_pane">true</item>
</resources>
می توانید (در کد خود) با استفاده از snippet (تکه کد) زیر به حالت (state) دسترسی پیدا کنید.
getResources().getBoolean(R.bool.dual_pane);
6. افزودن fragment transition به پشته (backstack)
می توان با افزودن fragment transition به پشته (backstack) به کاربر اجازه داد با استفاده از دکمه ی بازگشت تغییرات را به حالت قبلی بازگرداند.
برای نیل به این هدف می توانید از متد addToBackStack () برای شی FragmentTransition استفاده کنید.
7. تعریف animations برای fragment transition
می توان طی تراکنش انیمیشن هایی تعریف کرد که (مبتنی بر رابط برنامه سازی کاربردیProperty Animation (API)) با استفاده از متد setcustomanimation () مورد استفاده قرار گیرد.
همچنین می توانید با فراخوانی متد setTransition () از انیمیشن های متعارف و استاندارد خود اندروید بهره بگیرید. همگی این موارد توسط ثابت هایی که با *_FragmentTransaction.TRANSIT_FRAGMENT آغاز می گردند تعریف می شوند.
هر دو متد به شما اجازه می دهند هم انمیشن ورودی (entry animation) و هم انیمیشن خروجی (exit animation) تعریف کنید.
8. ماندگار کردن داده ها (persisting data) در fragment ها
ماندگار کردن اطلاعات بین راه اندازی مجدد برنامه های کاربردی
گفتنی است که در fragment باید داده های برنامه ی کاربردی را ذخیره کرد. برای این منظور لازم است داده ها را در یک مرکزی واحد ماندگار کرد، به عنوان مثال
پایگاه داده ی SQlite
File
Application object (چنانچه لازم است برنامه ی کاربردی، / storageذخیره سازی را مدیریت کند)
ماندگار کردن اطلاعات بین تغییرات پیکربندی (configuration changes)
اگر می خواهید اطلاعات بین تغییرات پیکربندی ماندگار شوند، باز می توانید از application object استفاده کنید.
علاوه بر آن می توانید متد setRetainState (true)را در fragment ها بکارببرید. این کار نمونه های fragment را بینابین تغییرات پیکربندی نگه داشته و حفظ می کند ولی فقط زمانی کارگر واقع می شود که fragment های مذکور به backstack اضافه نشده باشند. لازم به ذکر است که استفاده از این متد به خصوص از جانب گوگل برای fragment هایی که رابط کاربری دارند توصیه نمی شود. در این مورد باید داده ها را به عنوان عضو (فیلد) ذخیره کرد.
در صورت پشتیبانی کلاس Bundle از داده های موردنظر (داده هایی که قرار است ذخیره شوند)، می توانید از متد onSaveInstanceState () برای جای گذاری داده ها در کلاس Bundle استفاده کرد، سپس اطلاعات مزبور را از طریق متد onActivityCreated () بازیابی کرد.
9. استفاده از fragment برای پردازش پیش زمینه ای
Headless fragments
می توان fragment ها را بدون تعریف رابط کاربری معین بکاربرد.
جهت بکارگیری headless fragment، کافی است مقداری که از متد فرگمنت onCreateView ()برمی گردانید null باشد.
نکته
توصیه می شود برای پردازش پیش زمینه ای headless fragment را به همراه متد setRetainInstance () بکارگیرید، زیرا که از این طریق دیگر نیازی نیست تغییرات پیکربندی را خود هنگام فرایند پردازش ناهمگام (asynchronous processing) مدیریت کنید.
حفظ Headless fragment ها به منظور مدیریت تغییرات پیکربندی
Headless fragment ها به طور معمول به منظور کپسوله سازی حالت (state) معینی سرتاسر تغییراتی که در پیکربندی صورت می گیرد و همچنین برای task ای که پردازش خود را در پیش زمینه انجام می دهد بکار گرفته می شود. برای این منظور لازم است که headless fragment خود را روی retained تنظیم کنید، به عبارت دیگر با حفظ کردن headless fragment (قرار دادن و تنظیم کردن آن روی حالت retained) از نابود شدن آن حین تغییرات پیکربندی جلوگیری کنید.
![]()
جهت تنظیم fragment موردنظر به حالت retained، کافی است متد مرتبط با آن را فرابخوانید : setRetainInstance ().
به منظور افزودن چنین Fragment ای به activity معین، متد add () را که مشتق کلاس FragmentManager می باشد، بکار ببرید. چنانچه در آینده به هر دلیلی ملزم به ارجاع یا اشاره به این Fragment هستید، لازم است باید هنگام افزودن آن به activity، یک تگ ویژه ی fragment مورد نظر انتخاب کنید تا بدین وسیله جستجو و مکان یابی آن با استفاده از متد findFragmentByTag () (مشتق کلاس FragmentManager) سهل گردد.
توجه
کاربرد متد onRetainNonConfigurationInstance() دیگر منسوخ (نامناسب و ناکارامد) تلقی می گردد. توصیه می شود در عوض از headless fragment ها برای این منظور استفاده کنید.
10. نکات آموزشی درباره ی fragment ها
هدف اصلی این تمرین آموزشی
در این بخش با نحوه ی بکارگیری fragment ها آشنا می شوید. برنامه ای که در نظر گرفته ایم شَماهایی با fragment های مختلف (بسته به حالت نمایش تصویر یعنی; نمای عمودی / portrait mode یا نمای افقی / landscape mode) بکار می گیرد.
در حالت عمودی (portrait mode)، RssfeedActivity تنها یک fragment نمایش می دهد، سپس کاربر برای پیمایش و دستیابی به fragment دیگر از طریق همین fragment اقدام می کند.
این در حالی است که landscape mode (نمای افقی( هر دو fragment را کنارهم در صفحه ای واحد به نمایش می گذارد.
ایجاد پروژه
یک پروژه ی جدید اندروید با داده های زیر ایجاد کنید.
جدول 2. پروژه ی اندروید RSSFeed
|
Property (ویژگی) |
Value (مقدار) |
|
Application Name |
RSS Reader |
|
Project Name |
com.example.android.rssfeed |
|
Package name |
com.example.android.rssfeed |
|
)قالب Template ( |
Empty Activity |
|
)فعالیت Activity ( |
RssfeedActivity |
|
شمای کلی ( (Layout |
activity_rssfeed |
ایجاد طرح های کلی (layout) متعارف و استاندارد
فایل های layout زیر را در فولدر res/layout/ ایجاد کرده و در صورت وجود چنین فایل هایی در پوشه ی مذکور آن ها طبق شمای کلی زیر اصلاح کنید.
فایل layout ای با نام fragment_rssitem_detail.xml ایجاد کنید. این فایل سپس توسط DetailFragment مورد استفاده قرار می گیرد.
<?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="vertical">
<textview android:id="@+id/detailsText"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal|center_vertical"
android:layout_margintop="20dip"
android:text="Default Text"
android:textappearance="?android:attr/textAppearanceLarge"
android:textsize="30dip" />
</linearlayout>
اکنون فایل layout جدیدی به نام fragment_rsslist_overview.xml ایجاد کنید. این فایل layout نیز توسط MyListFragment مورد استفاده قرار می گیرد.
<?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="vertical">
<button android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Press to update"
/>
</linearlayout>
حال فایل activity_rssfeed.xml موجود را اصلاح کنید. این شمای کلی در واقع layout پیش فرض برای RssfeedActivity بوده و دو fragment نمایش می دهد.
<?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>
ایجاد کلاس های fragment
در این مرحله، کلاس های fragment را ایجاد می کنیم. ابتدا کلاس DetailFragment را می سازیم.
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);
}
}
کلاس MylistFragment را ایجاد کنید. برخلاف آنچه از اسمش بر می آید این کلاس هیچ فهرست آیتمی نمایش نمی دهد، ولی در عوض دکمه ای ارائه می دهد که به وسیله ی آن زمان جاری به details fragment ارسال می شود.
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");
}
}
// May also be triggered from the Activity
public void updateDetail() {
// create fake data
String newTime = String.valueOf(System.currentTimeMillis());
// Send data to Activity
listener.onRssItemSelected(newTime);
}
}
RssfeedActivity
را به کد زیر تغییر دهید. RssfeedActivity کلاس
package com.example.android.rssfeed;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
public class RssfeedActivity extends Activity implements MyListFragment.OnItemSelectedListener{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_rssfeed);
}
// if the wizard generated an onCreateOptionsMenu you can delete
// it، not needed for this tutorial
@Override
public void onRssItemSelected(String link) {
DetailFragment fragment = (DetailFragment) getFragmentManager()
.findFragmentById(R.id.detailFragment);
if (fragment != null && fragment.isInLayout()) {
fragment.setText(link);
}
}
}
تست کردن نمونه
برنامه ی خود را اجرا کنید. هر دو fragment باید در دو حالت نمای افقی و عمودی به نمایش گذاشته شود. با زدن دکمه ی کلاس ListFragment باید کلاس DetailFragment بروز رسانی شود.
11. طرح بندی (layout) با نمای عمودی (portrait mode)
RssfeedActivity باید برای نمایش عمودی تصویر از فایل layout مخصوص استفاده کند. در این حالت نمایش، سیستم عامل اندروید در پوشه ی Layout-port به دنبال فایل های layout همخوان و مربوطه می گردد. در صورت پیدا نکردن فایل مناسب، اندروید به صورت پیش فرض پوشه ی layout را بکار می برد.
به همین دلیل لازم است پوشه ی res/layout-port را بوجود آورید. پس از آن فایل layout، activity_rssfeed.xml را در پوشه ی res/layout-port ایجاد کنید.
<?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">
<fragment android:id="@+id/listFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margintop="?android:attr/actionBarSize"
class="com.example.android.rssfeed.MyListFragment" />
</linearlayout>
همچنین نیاز است فایل layout activity_detail.xml را ایجاد کنید زیرا که در کلاس DetailActivity مورد استفاده قرار می گیرد. توجه داشته باشید که این فایل را می توان در پوشه ی res/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="vertical">
<fragment android:id="@+id/detailFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.example.android.rssfeed.DetailFragment" />
</linearlayout>
DetailActivity
Activity جدیدی به نام DetailActivity با کلاس زیر ایجاد کنید.
به خاطر داشته باشید که activity مورد نظر را در فایل AndroidManifest.xml نیز ثبت کنید.
package com.example.android.rssfeed;
import android.app.Activity;
import android.app.Application;
import android.content.res.Configuration;
import android.os.Bundle;
public class DetailActivity extends Activity {
public static final String EXTRA_URL = "url";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Need to check if Activity has been switched to landscape mode
// If yes، finished and go back to the start Activity
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
finish();
return;
}
setContentView(R.layout.activity_detail);
Bundle extras = getIntent().getExtras();
if (extras != null) {
String url = extras.getString(EXTRA_URL);
DetailFragment detailFragment = (DetailFragment) getFragmentManager()
.findFragmentById(R.id.detailFragment);
detailFragment.setText(url);
}
}
}
تنظیم و اصلاح RSSfeedActivity
کلاس RssfeedActivity را تنظیم کرده تا DetailActivity را در صورت نبود دیگر fragment در layout نمایش دهد.
package com.example.android.rssfeed;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
public class RssfeedActivity extends Activity implements
MyListFragment.OnItemSelectedListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_rssfeed);
}
@Override
public void onRssItemSelected(String link) {
DetailFragment fragment = (DetailFragment) getFragmentManager()
.findFragmentById(R.id.detailFragment);
if (fragment != null && fragment.isInLayout()) {
fragment.setText(link);
} else {
Intent intent = new Intent(getApplicationContext()،
DetailActivity.class);
intent.putExtra(DetailActivity.EXTRA_URL، link);
startActivity(intent);
}
}
}
اجرا
برنامه ی نمونه ی خود را اجرا کنید. چنانچه برنامه را در حالت نمای عمودی اجرا کنید، تنها یک fragment باید برای شما قابل رویت باشد. می توان با گرفتن کلیدهای Ctrl+F11 به طور همزمان جهت قرار گیری تصویر را تغییر داد. در حالت افقی دو fragment قابل مشاهده می باشد. پس از زدن دکمه ی مربوطه (در حالت نمای عمودی)، DetailActivity جدید راه اندازی شده و زمان جاری را نمایش می دهد. این در حالی است که با نمای افقی هر دو fragment را می توان مشاهده کرد.
![]()
12. تمرین : افزودن fragmentها به activity به صورت پویا (dynamic)
هدف این تمرین
در این تمرین با استفاده از FragmentManager، فرگمنت های لازم را به صورت پویا به activity خود اضافه می کنیم.
استفاده از placeholder برای فایل های layout
FrameLayout را به عنوان ) placeholderدر پوشه های layout) جایگزین Fragment های ایستا (static) کنید. لیست زیر ورودی (entry) پوشه ی res/layout می باشد. در این layout، container دوم مورد استفاده قرار نمی گیرد.
<?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>
کد زیر ورودی پوشه ی res/layout-land می باشد.
<?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">
<fragment android:id="@+id/listFragment"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:layout_margintop="?android:attr/actionBarSize"
class="com.example.android.rssfeed.MyListFragment" />
<fragment android:id="@+id/detailFragment"
android:layout_width="0dp"
android:layout_weight="2"
android:layout_height="match_parent"
class="com.example.android.rssfeed.DetailFragment" />
</linearlayout>
تعیین حالت (dual-pane mode یا single-pane) با تعریف متغیر
ابتدا فایل پیکربندی res/values/config.xml را ایجاد کنید.
<resources>
<item type="bool" name="dual_pane">false</item>
</resources>
در پوشه ی res/values-land، فایل پیکربندی config.xml را با مقداری متفاوت جای گذاری کنید.
<resources>
<item type="bool" name="dual_pane">true</item>
</resources>
جایگزین کردن fragment ها با استفاده از fragment manager
راه کاری را بکار ببرید که در آن تنها یک activity، fragment ها را به مجرد تقاضا مدیریت و کنترل می کند.
activity_rssfeed.xml
activity_rssfeedland.xml
replace.java
RssfeedActivity.java
