如何使用Fragment建立TabActivity之三

不知道有沒有看過類似這樣的畫面?

其實很多app都會用到這樣的頁面, 它其實就是把一個TabHost其中一個分頁,
再塞入TabHost而已。

只要利用我們在如何使用Fragment建立TabActivity之二當中,
所做的專案進行一些小幅度的修改, 也可以達到相同效果。


那我們就將一開始第一個分頁塞入相同的TabHost,

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
    Bundle savedInstanceState) {
    // TODO Auto-generated method stub
    v = inflater.inflate(R.layout.fragment1_tabs, container, false);
    mTabHost = (TabHost)v.findViewById(R.id.TabHost01);
    mTabHost.setup();
        
    mTabManager = new TabManager(getActivity(), mTabHost, R.id.realtabcontents);
    mTabHost.setCurrentTab(0);
    mTabManager.addTab(mTabHost.newTabSpec("Tab1")
        .setIndicator("Tab1",this.getResources().getDrawable(R.drawable.car1)),
        Fragment1_Tab1.class, null);
    mTabManager.addTab(mTabHost.newTabSpec("Tab2")
        .setIndicator("Tab2",this.getResources().getDrawable(R.drawable.car2)),
        Fragment1_Tab2.class, null);
    mTabManager.addTab(mTabHost.newTabSpec("Tab3")
        .setIndicator("Tab3",this.getResources().getDrawable(R.drawable.car3)),
        Fragment3.class, null);
    mTabManager.addTab(mTabHost.newTabSpec("Tab4")
        .setIndicator("Tab4",this.getResources().getDrawable(R.drawable.car4)),
        Fragment4.class, null);
        
    DisplayMetrics dm = new DisplayMetrics();   
    getActivity().getWindowManager().getDefaultDisplay().getMetrics(dm); //先取得螢幕解析度  
    int screenWidth = dm.widthPixels;   //取得螢幕的寬
                      
    TabWidget tabWidget = mTabHost.getTabWidget();   //取得tab的物件
    int count = tabWidget.getChildCount();   //取得tab的分頁有幾個
    if (count > 3) {   //如果超過三個就來處理滑動
        for (int i = 0; i < count; i++) {   
            tabWidget.getChildTabViewAt(i).setMinimumWidth((screenWidth) / 4);//設定每一個分頁最小的寬度   
        }   
    }
    return v;
}
這邊跟一開始的FragmentTabs.java做的事情都相同,
只是差別在一個是繼承FragmentActivity, 一個是繼承Fragment.java,
因此寫的地方放在不同的地方。

那麼一樣建立四個不同分頁類別, 分別是Fragment1_Tab1、Fragment1_Tab2,
剩下的兩個就用之前用過的, Fragment3以及Fragment4,
這邊點出一個重點, 假設你Fragment類別寫成樣版的模式,
那麼你就可以利用傳遞參數的方式, 來進行頁面的更換。

所以第一個頁面程式碼。

public class Fragment1_Tab1 extends Fragment {
    private View v;
    private ListView listView;
    private SimpleAdapter simpleAdapter;
    private int[] image = {
        R.drawable.cat, R.drawable.flower, R.drawable.hippo,
        R.drawable.monkey, R.drawable.mushroom, R.drawable.panda,
        R.drawable.rabbit, R.drawable.raccoon
    };
    private String[] imgText = {
        "cat", "flower", "hippo", "monkey", "mushroom", "panda", "rabbit", "raccoon"
    };
    @Override
    public void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        v = inflater.inflate(R.layout.fragment1_tab1, container, false);
        listView = (ListView)v.findViewById(R.id.fragment1_tab1_list);
        List<Map<String, Object>> items = new ArrayList<Map<String,Object>>();
        for (int i = 0; i < image.length; i++) {
            Map<String, Object> item = new HashMap<String, Object>();
            item.put("image", image[i]);
            item.put("text", imgText[i]);
            items.add(item);
        }
        simpleAdapter = new SimpleAdapter(getActivity(), 
            items, R.layout.fragment1_tab1_simpleadapter, new String[]{"image", "text"},
            new int[]{R.id.image, R.id.text});
        listView.setAdapter(simpleAdapter);
        return v;
    }
 
}
參考如何使用SimpleAdapter, 那你就會看到以下的畫面。


第二個分頁
public class Fragment1_Tab2 extends ListFragment {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        String[] arr = new String[]{
            "滷肉飯","雞腿飯","油雞飯","排骨飯","孫悟飯","雞排飯","礦肉飯"
        };
        ArrayAdapter<String> adapter = 
            new ArrayAdapter<String>(getActivity(),android.R.layout.simple_list_item_1,arr);
        setListAdapter(adapter);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        return inflater.inflate(R.layout.fragment2_list, container, false);
    }
}
就會看到以下畫面

第三、四個分頁是引導到之前寫好的Fragment3、Fragment4,
因此不必寫任何程式碼。



這樣四個小分頁內容就都完成了。

再來就是要講一下, 當如果小分頁要進行切換到其他分頁的時候,
例如回到第一個小分頁Fragment1_Tab1.java這邊,
假設使用者按下去, 跳至另外一支Fragment1_Tab1_ImageView.java,
接著使用者按下back, 理所當然又要回到Fragment1_Tab1.java這邊,
這時候該怎麼作呢?

先定義xml, 用來存放轉過去的畫面。
<?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" >
    <ImageView
     android:layout_width="100dp"
     android:layout_height="100dp"
     android:id="@+id/image_view"    
        />

</LinearLayout>


再來寫一支轉跳過去的class, 叫做Fragment1_Tab_ImageView.java,
public class Fragment1_Tab1_ImageView extends Fragment {
    private int[] image = {
        R.drawable.cat, R.drawable.flower, R.drawable.hippo,
        R.drawable.monkey, R.drawable.mushroom, R.drawable.panda,
        R.drawable.rabbit, R.drawable.raccoon
    };
    @Override
    public void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
 super.onCreate(savedInstanceState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        View v = inflater.inflate(R.layout.fragment1_tab1_imageview, container, false);
        ImageView img = (ImageView)v.findViewById(R.id.image_view);
        img.setImageResource(image[getArguments().getInt("position")]);
        return v;
    }

}
你會發現, 其實這邊變的很公式化, 只需要將xml讀進去,
就跟使用Activity沒什麼兩樣。


再來回到Fragment1_Tab1.java, 並且對listView進行新增事件的動作。
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
    Bundle savedInstanceState) {
    // TODO Auto-generated method stub
    v = inflater.inflate(R.layout.fragment1_tab1, container, false);
    listView = (ListView)v.findViewById(R.id.fragment1_tab1_list);
    List<Map<String, Object>> items = new ArrayList<Map<String,Object>>();
    for (int i = 0; i < image.length; i++) {
        Map<String, Object> item = new HashMap<String, Object>();
        item.put("image", image[i]);
        item.put("text", imgText[i]);
        items.add(item);
    }
    simpleAdapter = new SimpleAdapter(getActivity(), 
        items, R.layout.fragment1_tab1_simpleadapter, new String[]{"image", "text"},
             new int[]{R.id.image, R.id.text});
    listView.setAdapter(simpleAdapter);
    listView.setOnItemClickListener(new OnItemClickListener(){

        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position,
            long id) {
            // TODO Auto-generated method stub
            Bundle args = new Bundle();
            args.putInt("position", position);
             
            Fragment newFragment = new Fragment1_Tab1_ImageView();
             
            FragmentTransaction ft = 
                getActivity().getSupportFragmentManager().beginTransaction();
            newFragment.setArguments(args);
             
            ft.replace(R.id.realtabcontents, newFragment);
            ft.setTransition(FragmentTransaction.TRANSIT_NONE);
            ft.addToBackStack(null);
            ft.commit();
        }
         
    });
    return v;
}
你會看到我new一個Fragment1_Tab1_ImageView類別的物件出來,
取得getActivity().getSupportFragmentManager()的物件, 這個物件就是Fragment管理員,
接著利用管理員的方法進行fragment的轉換,
而當中你會看到一個setArguments的方法, 其實這個方法是可以幫你傳送Bundle物件的,
就跟Intent使用方法相同。

接著就利用FragmentTransaction的物件進行replace,
並且可以設定是否要將此fragment存入一個堆疊(stack),
接著送交出去, 即可進行畫面的切換。




這樣轉換就很方便了。
但是如果要回到上一頁怎麼辦呢?
其實也不難, 回到FragmentTabs.java這支程式,
我們只要覆寫onKeyDown方法, 來捕捉使用者按下back,
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    // TODO Auto-generated method stub
    if(keyCode==KeyEvent.KEYCODE_BACK){
        getSupportFragmentManager().popBackStack();
    }
    return true;
}
由於我們在進行畫面替換的時候,
Fragment管理員會幫我們把上一次的Fragment存到堆疊裡面,
因此只要利用Fragment管理員, 把堆疊取出fragment,
就可以讓畫面回到上一個Fragment, 很簡單吧?

程式碼
http://uploadingit.com/file/bc3bojv2oco1wjh8/FragmentTabs.zip

https://dl.dropboxusercontent.com/u/24682760/Android/FragmentTabs.zip

先前的教學
如何使用Fragment建立TabActivity之一
http://givemepass.blogspot.tw/2012/07/tabactivity.html
如何使用Fragment建立TabActivity之二
http://givemepass.blogspot.tw/2012/07/fragmenttabactivity.html