出現了這個界面之後,總算可以在一個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 放錯地方, 名稱有的沒有改到
若都修正了應該會很不錯