出現了這個界面之後,總算可以在一個activity上面做任意的view切換。
原本是用來針對較大畫面的tablet機種使用,不過早期的tabhost元件也因為一次只能卡一個view,造成要在每個tab上面做view切換的時候需要實做一些旁枝末節的東西。
有了fragment之後,這些問題終於得到解決。
在這裡要介紹如何使用fragment達到使用tabhost,並在每個單獨tab裡面擁有activity的切換效果。
首先第一步,你需要使用api level 13+的package。若是還在使用舊的api包的話請更新。
之後在ADT的Menu Bar上面的project-->properties裡面更換Target。
接著建立一個fragment_layout.xml放在res/layout下面。(因為下面會介紹的fragment切換必須用到。)
<framelayout android:id="@+id/fl_fc" android:layout_height="fill_parent" android:layout_width="fill_parent" xmlns:android="http://schemas.android.com/apk/res/android"> </framelayout>
再繼承一個FragmentActivity。要給Tabhost使用的。
public class MyFragmentActivity extends FragmentActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) //可以判斷是不是第一次產生
{
setContentView(R.layout.fragmentlayout);
FrameLayout frameLayout = (FrameLayout)findViewById(R.id.fl_fc);
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.fl_fc, new MyFragment());
ft.commit();
}
}
}
再來一個基本的Fragment:
(之前的設計後來會出現無法使用popBackStack跳轉到指定的Fragment的問題,
所以把frame tracsaction 獨立出來為一個function, 更新為可以泛用的設計。)
public class MyFragment extends Fragment implements View.OnClickListener {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { //補充:據說savedInstanceState這東西永遠不會出現... 另外onCreateView會一直被call,就算是從上一層回來,你並沒有去destroy它...
super.onCreateView(inflater, container, savedInstanceState);
View view = inflater.inflate( R.layout.myfragmentlayout, container, false); //attachToRoot 必須為 false!
RelativeLayout rl = (RelativeLayout)view.findViewById(R.id.rl); //這只是demo該怎麼取layout上面的view object
view.setFocusableInTouchMode(true); //這行是為了要讓back鍵可以作用在fragment上面而加的。
view.requestFocus(); //這行是為了要讓back鍵可以作用在fragment上面而加的。
return view;
}
//按鈕換頁的動作。
public void onClick(View arg0) {
Bundle args = new Bundle();
args.putInt("data", 0);
//下面的這個method, 使用在FragmentActivity class上面的時候,用「getSupportFragmentManager」,使用在Fragment class上面,用「getFragmentManager()」
FragmentTran(MyFragment.class, getSupportFragmentManager(), null);
}
}
以下是更換Fragment的動作。當作Static Method用。
public static void FragmentTran(Class fragment, FragmentManager fm, Bundle bundle)
{
FragmentTransaction ft = fm.beginTransaction();
try {
Fragment newFragment = (Fragment) fragment.newInstance();
if (bundle != null)
newFragment.setArguments(bundle);
ft.replace(R.id.fl_fragmentcontainer, newFragment, fragment.getName());
ft.setTransition(FragmentTransaction.TRANSIT_ENTER_MASK);
ft.addToBackStack(fragment.getName()); //此為讓popBackStack可以回到指定fragment的關鍵。
ft.commit();
} catch (InstantiationException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IllegalAccessException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
可以用這個架構如法炮製。然後tabhost裡面setcontent的動作:tabHost.addTab(tabHost.newTabSpec("0")
.setIndicator(xxxView).setContent(new Intent(this, MyFragmentActivity.class)));
下面是一些註解:
fragment_layout.xml只是一個簡單的layout,當做容納fragment的container view。
可不可以只用程式產生不用xml指定? 解決ft.replace的第一個參數要帶resource的問題的話應該就可以,自己試看看吧?
FragmentTransaction是一個重要的動作。相當於intent的作用。能夠切換不同的fragment。使用的流程: new, 設定fragment的動作,commit()。 commit一定要在最後做。
newFragment.setArguments(args); 相當於intent的putExtra.
在你的下一個Fragment裡面使用getArguments();來取得傳來的Bundle。
ft.setTransition可以指定fragment切換的時候的換頁淡入淡出效果。
ft.addToBackStack(fragment.getName()); 可以將這次的整個切換流程記錄在stack裡面,方便按下back按鈕的時候恢復成之前的狀態。
手動pop的方法:
getActivity().getSupportFragmentManager().popBackStack();
要操作context的時候用 getActivity() 來替代。
view.setFocusableInTouchMode(true);
view.requestFocus();
這兩個動作是為了保證這個fragment一定會在focus狀態,不會出現按了back鍵結果整個
FragmentActivity被關掉。(為了找這問題花了點時間...)
inflate的時候使用View.inflate(getActivity)的方法會出問題。有興趣的話也可以試看看。
簡述transaction的各個動作:
註解寫得不錯,
返信削除但是function 放錯地方, 名稱有的沒有改到
若都修正了應該會很不錯