通常我們先將 Fragment 給模組化, 以方便在 Activity 裡添加.
換句話說, 我們可以用 Activity 來管控(數個) Fragment, 並藉此來決定 UI 畫面的顯示.
Framgent 的緣起
會有這樣的設計, 最早是因為須要處理不同尺寸螢幕的資料顯示, 例如: 平板.
平板的畫面比手機大得多, 因此我們希望可以將畫面切割成兩個部分, 讓左側 Fragment A 的點擊, 直接影響右側 Fragment B 的顯示.
而手機的部分, 因為畫面小, 只能將 Fragment A 與 Fragment B 分別使用不同的 Activity 來處理.
在沒有 Fragment 的情況下, 很可能必須為平板和手機分別寫不同的 Activity,
但有了 Fragment, 只需要做一個畫面就能在不同的 Activity 使用, 減少了重復寫 code 的可能性,
畫面模組化的好處在此時顯現了出來.
以至於在 Android 3.0 之後, Google 將許多元件都以 Fragment 來設計.
例如: Tab, ViewPager ... 等等.
Fragment 的 LifeCycle
Fragment 是依附 Activity 的模組畫面, 因此它的 LifeCycle 本身也與 Activity 息息相關.
看圖示可以知道, Fragment 跟 Activity 的 LifeCycle 很像.
當 Activity 添加 Fragment 時, 會先執行 onAttach(),
如果有預先儲存的 Bundle 資料, 應該在此載入.
在 onCreateView() 時, 要返回 Fragment 的 View.
接著, Framgent 的 onResume(), onStop(), onDestroy() 都是依附著 Activity 執行的. 也就是說,
當 Activity 處於畫面前端, 執行 onResume() 時, Framgent 就執行 onResume() ,
當 Activity 要結束了, 執行 onDestroy() 時, Fragment 就執行 onDestroy().
Fragment 的 LifeCycle 做得比 Activity 更細緻, 應是為了方便更細微的處理.
Fragment 的範例
這一次我們的範例取自 Android Developer 的 FragmentBasics,
用意就是做一個像我們最上面那個圖示一樣的 App,
在大尺寸的平板上, 左側是故事章節選擇的 ListView, 右側則是故事內容的 TextView,
平板畫面:
手機畫面:
Fragment 範例實作
1. 撰寫兩個同檔名 news_articles.xml 的 Layout
一個放在 res/layout 的資料夾, 一個放在 res/large-large 的資料夾. 放在 res/layout 資料夾下的即是提供手機使用, 在 res/layout-large 下的則提供平板使用
res/layout/news_articles.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
res/layout-large/news_articles.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment android:name="com.example.android.fragments.HeadlinesFragment"
android:id="@+id/headlines_fragment"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent" />
<fragment android:name="com.example.android.fragments.ArticleFragment"
android:id="@+id/article_fragment"
android:layout_weight="2"
android:layout_width="0dp"
android:layout_height="match_parent" />
</LinearLayout>
這裡可以注意到, 在大尺寸的畫面上, 我們塞了兩個 fragment, 並且用 layout_weight 分別派給了這兩個不同等分的大小. (HeadlinesFragment 佔 1/3, ArticleFragment 佔 2/3)
2. 分別撰寫 HeadlinesFragment 以及 ArticleFragment
HeadLinesFramgnet 跟 ArticleFragment 即是我們要用到的模組畫面, 可以注意到我們在大尺寸的畫面上已經悄悄用到這兩個 Fragment 了. 這種方法叫做靜態載入.
HeadlinesFragment.class
public class HeadlinesFragment extends ListFragment {
OnHeadlineSelectedListener mCallback;
public interface OnHeadlineSelectedListener {
public void onArticleSelected(int position);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
int layout = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ?
android.R.layout.simple_list_item_activated_1 : android.R.layout.simple_list_item_1;
setListAdapter(new ArrayAdapter<String>(getActivity(), layout, Ipsum.Headlines));
}
@Override
public void onStart() {
super.onStart();
if (getFragmentManager().findFragmentById(R.id.article_fragment) != null) {
getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
}
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mCallback = (OnHeadlineSelectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnHeadlineSelectedListener");
}
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
mCallback.onArticleSelected(position);
getListView().setItemChecked(position, true);
}
}
在 HeadlinesFragment.class, 我們寫了一個 onHeadlineSelectedListener 的 interface, 裡頭有 onArticleSelected() , 代表 implement 這個 onHeadlineSelectedListener 的 Activity 要實作 onArticleSelected(). 藉此, 便可以在 onAttach() 的時候取得這個 Listener 物件, 做為 Fragment 跟 Activity 溝通使用.
ArticleFragment.class
public class ArticleFragment extends Fragment {
final static String ARG_POSITION = "position";
int mCurrentPosition = -1;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (savedInstanceState != null) {
mCurrentPosition = savedInstanceState.getInt(ARG_POSITION);
}
return inflater.inflate(R.layout.article_view, container, false);
}
@Override
public void onStart() {
super.onStart();
Bundle args = getArguments();
if (args != null) {
updateArticleView(args.getInt(ARG_POSITION));
} else if (mCurrentPosition != -1) {
updateArticleView(mCurrentPosition);
}
}
public void updateArticleView(int position) {
TextView article = (TextView) getActivity().findViewById(R.id.article);
article.setText(Ipsum.Articles[position]);
mCurrentPosition = position;
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(ARG_POSITION, mCurrentPosition);
}
}
在ArticleFragment.class 的任務很簡單, 就是要把資料顯示在 article_view.xml 上.
3. 在 MainActivity 裡調用.
在MainActivity 裡, 我們必須大尺寸的雙畫面, 跟小尺寸的單畫面分別作不同的處置.
那要如何知道目前是大尺寸或小尺寸呢? 可以用有沒有 R.id.fragment_container 來判別.
(因為之前有說過, 裝置會自動調用 res/layout for 小尺寸, res/layout-large for 大尺寸)
如果有 R.id.fragment_container => 小尺寸, 沒有 R.id.fragment_container => 大尺寸.
MainActivity.class
public class MainActivity extends FragmentActivity
implements HeadlinesFragment.OnHeadlineSelectedListener {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.news_articles);
if (findViewById(R.id.fragment_container) != null) {
if (savedInstanceState != null) {
return;
}
HeadlinesFragment firstFragment = new HeadlinesFragment();
firstFragment.setArguments(getIntent().getExtras());
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, firstFragment).commit();
}
}
public void onArticleSelected(int position) {
ArticleFragment articleFrag = (ArticleFragment)
getSupportFragmentManager().findFragmentById(R.id.article_fragment);
if (articleFrag != null) {
articleFrag.updateArticleView(position);
} else {
ArticleFragment newFragment = new ArticleFragment();
Bundle args = new Bundle();
args.putInt(ArticleFragment.ARG_POSITION, position);
newFragment.setArguments(args);
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
transaction.commit();
}
}
}
同理, 如果 article_fragment 存在 => 大尺寸, 如果 article_fragment 不存在 => 小尺寸.
大尺寸當 HeadlinesFragment 被點擊時要 update ArticleFragment 的內容,
小尺寸當 HeadlinesFragment 被點擊時要產生新的 ArticleFragment, 並更新畫面.
原始碼連結
how we can show in fragment to display query data from SQLite database?
回覆刪除Can you show by example how SQLite query data to be returned in String[] array that can be used by fragment?