Frame是Android3.0才出來的一個控件,在過去我們顯示的都彩Activity然后進行設計,而Frame出來是為了使一個窗口里面能夠具有多個Activity。
通過下圖下分析,Frame可以在窗口內嵌入多個fragments,接下來的會給出源碼來實現下面的功能, ,主要完成的任務是當你點擊左側的listView項,右邊的fragments內容是隨着你的更新而更新的。

Frame生命周期

OnCreate:當你需要在創建frame時對一些組件進行相應的初始化,就可以在這里面寫。
onCreateView:當你需要繪制一個界面的時候就需要調用這個回調的方法來返回一個view對象,如果你不想使用任何的界面時可以返回null,還有注意的一件事情是當你橫豎屏切換的時候會多次回調這個方法。
onPause():當用戶離開這個fragment的時候會回調這個方法,而離開並不代表這個應用的結束
DialogFragment:這個方法是用於繪制一個對話框,這個對話框采用棧的方法,當你結束后可以返回前一個窗體。
ListFragment:這個類似listView,顯示一個列表,列表中的項可以通過adapter(比如一個SimpleCursorAdapter)來指定列表的項內容,當你單擊的時候同樣跟ListView一樣會有一個onListItemClick()回調的函數,來進行單擊列表項時的操作。
PreferenceFragment:這個fragment的作用類似於 PreferenceActivity,顯示一個 Preference對象的層次結構的列表,就是用戶設置界面來理解就可以了。
增加界面:
通常Fragment的界面采用interface的方式來選擇一個以有的layout XML文件來設計界面,通過前面的知識我們可以了解到,Fragments有一個oncreateView()的回調方法來決定顯示的界面,那么就可以在里面用infalter來選擇,而且返回一個View對象。
傳入 onCreateView() 的 container 參數是你的fragment layout將要插入的父ViewGroup(來自activity的layout)。savedInstanceState 參數是一個 Bundle,如果fragment是被恢復的,它提供關於fragment的之前的實例的數據。
inflate() 方法有3個參數:
-
想要加載的layout的resource ID。
-
加載的layout的父ViewGroup。
傳入container是很重要的,目的是為了讓系統接受所要加載的layout的根view的layout參數,
由它將掛靠的父view指定。 -
布爾值指示在加載期間,展開的layout是否應當附着到ViewGroup (第二個參數)。
(在這個例子中, 指定了false, 因為系統已經把展開的layout插入到container – 傳入true會在最后的layout中創建一個多余的view group。)
publicstaticclassExampleFragmentextendsFragment{
@Override
publicView onCreateView(LayoutInflater inflater,ViewGroup container,
Bundle savedInstanceState){
// Inflate the layout for this fragment
return inflater.inflate(R.layout.example_fragment, container,false);
}
}
添加一個Fragment到activity中:
我們可以把Fragment加入到一個Activity的Layout布局文件中,需要注意的是name屬性的作用就是指定一個fragment Class到布局文件中。
有3種方法來為一個fragment提供一個標識:
-
為 android:id 屬性提供一個唯一ID。
-
為 android:tag 屬性提供一個唯一字符串。
-
如果以上2個你都沒有提供,系統使用容器view的ID。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragmentandroid:name="com.example.news.ArticleListFragment"
android:id="@+id/list"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent"/>
<fragmentandroid:name="com.example.news.ArticleReaderFragment"
android:id="@+id/viewer"
android:layout_weight="2"
android:layout_width="0dp"
android:layout_height="match_parent"/>
</LinearLayout>
並且fragment可以添加到viewgroup里面。
FragmentManager fragmentManager = getFragmentManager()
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
此時你就可以彩Add方法來添加一個view來插入到viewgrou中了
ExampleFragment fragment =newExampleFragment();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();
還有,當你添加完成后一定要調用commit()方法,這個方法的作用類似於Sqllite中的事務,只要你調用這個方法才會提交你所做的操作。
下圖是不同時期會回調的函數

例子:
那么我們還是采用官方的案例來講解吧,那么最終完成的功能如下圖所示,左邊是標題列表,右邊是根據你的選擇來顯示相應的內容。
首先我們需要設計一個layout:
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"android:layout_height="match_parent">
<fragmentclass="com.example.android.apis.app.FragmentLayout$TitlesFragment"
android:id="@+id/titles"android:layout_weight="1"
android:layout_width="0px"android:layout_height="match_parent"/>
<FrameLayoutandroid:id="@+id/details"android:layout_weight="1"
android:layout_width="0px"android:layout_height="match_parent"
android:background="?android:attr/detailsElementBackground"/>
</LinearLayout>
當完成XML的設計后我們還需要在Activity中的oncreate應用這個局面文件
protectedvoid onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_layout);
}
TitleFragment是繼承listFragment主要用於標題的顯示。
publicstaticclassTitlesFragmentextendsListFragment{
boolean mDualPane;
int mCurCheckPosition =0;
@Override
//下面的回調方法是用來設計你的簾布
publicvoid onActivityCreated(Bundle savedInstanceState){
super.onActivityCreated(savedInstanceState);
// 通過setListAdapter來進行綁定列表項,simple_list_item_activated_1是系統自定義的,采用的是text來顯示列表項。
setListAdapter(newArrayAdapter<String>(getActivity(),
android.R.layout.simple_list_item_activated_1,Shakespeare.TITLES));
View detailsFrame = getActivity().findViewById(R.id.details);
mDualPane = detailsFrame !=null&& detailsFrame.getVisibility()==View.VISIBLE;
if(savedInstanceState !=null){
// 恢復你最后選擇的狀態。
mCurCheckPosition = savedInstanceState.getInt("curChoice",0);
}
if(mDualPane){
// In dual-pane mode, the list view highlights the selected item.
getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
// Make sure our UI is in the correct state.
showDetails(mCurCheckPosition);
}
}
@Override
publicvoid onSaveInstanceState(Bundle outState){
super.onSaveInstanceState(outState);
outState.putInt("curChoice", mCurCheckPosition);
}
@Override
//當你單擊列表項就會回調這個方法,其中position是你單擊項的下標,showDetails函數的作用是通過你傳送的標題項來進行內容的選擇。
publicvoid onListItemClick(ListView l,View v,int position,long id){
showDetails(position);
}
/**
* Helper function to show the details of a selected item, either by
* displaying a fragment in-place in the current UI, or starting a
* whole new activity in which it is displayed.
*/
void showDetails(int index){
mCurCheckPosition = index;
if(mDualPane){
// We can display everything in-place with fragments, so update
// the list to highlight the selected item and show the data.
getListView().setItemChecked(index,true);
// Check what fragment is currently shown, replace if needed.
DetailsFragment details =(DetailsFragment)
getFragmentManager().findFragmentById(R.id.details);
if(details ==null|| details.getShownIndex()!= index){
//重新設計一張新的視圖來顯示你所選擇標題的正文內容。
details =DetailsFragment.newInstance(index);
// 執行一個事務來替代以前存在的fragment
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.details, details);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.commit();
}
}else{
//否則用一個新的DetailsActivity的來進行顯示。
// the dialog fragment with selected text.
Intent intent =newIntent();
intent.setClass(getActivity(),DetailsActivity.class);
intent.putExtra("index", index);
startActivity(intent);
}
}
}
DetailsFragment類:通過TitlesFragment來顯示相應的正文。
publicstaticclassDetailsFragmentextendsFragment{
/**
* Create a new instance of DetailsFragment, initialized to
*通過傳送過來的Index來選擇相應的內容
*/
publicstaticDetailsFragment newInstance(int index){
DetailsFragment f =newDetailsFragment();
// Supply index input as an argument.
Bundle args =newBundle();
args.putInt("index", index);
f.setArguments(args);
return f;
}
publicint getShownIndex(){
return getArguments().getInt("index",0);
}
@Override
publicView onCreateView(LayoutInflater inflater,ViewGroup container,
Bundle savedInstanceState){
if(container ==null){
// We have different layouts, and in one of them this
// fragment's containing frame doesn't exist. The fragment
// may still be created from its saved state, but there is
// no reason to try to create its view hierarchy because it
// won't be displayed. Note this is not needed -- we could
// just run the code below, where we would create and return
// the view hierarchy; it would just never be used.
returnnull;
}
ScrollView scroller =newScrollView(getActivity());
TextView text =newTextView(getActivity());
int padding =(int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
4, getActivity().getResources().getDisplayMetrics());
text.setPadding(padding, padding, padding, padding);
scroller.addView(text);
text.setText(Shakespeare.DIALOGUE[getShownIndex()]);
return scroller;
}
}
