【移動開發】Android中的底部菜單框架(Fragment)


今天,我將總結一下Android應用中大家經常見到的底部導航欄的幾種實現!


一。TabHost + RadioGroup實現方式

在我們平時開發過程中使用的TabHost是在上方,這裏我們簡單修改了一下<TabHost>的佈局,讓葉片放到了底部。

main.xml

<?xml version="1.0" encoding="utf-8"?>
<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/tabhost"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal" >
    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="vertical" >
        <TabWidget
            android:id="@android:id/tabs"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:visibility="gone" />
        <FrameLayout
            android:id="@android:id/tabcontent"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:layout_weight="1.0" />
        <RadioGroup
            android:id="@+id/radioGroup"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom"
            android:background="@drawable/bar"
            android:gravity="center_vertical"
            android:orientation="horizontal" >
            <RadioButton
                android:id="@+id/rd_home"
                style="@style/main_btn_style"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:drawableTop="@drawable/home_icon"
                android:text="主頁" />
            <RadioButton
                android:id="@+id/rd_wt"
                style="@style/main_btn_style"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:drawableTop="@drawable/wb_icon_write_n"
                android:text="發表" />
            <RadioButton
                android:id="@+id/rd_msg"
                style="@style/main_btn_style"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:drawableTop="@drawable/msg_icon"
                android:text="信息" />
            <RadioButton
                android:id="@+id/rd_more"
                style="@style/main_btn_style"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:drawableTop="@drawable/more_icon"
                android:text="更多" />
        </RadioGroup>
    </LinearLayout>
</TabHost>

styles.xml

<style name="main_btn_style">
       <item name="android:button">@null</item>
       <item name="android:textSize">10dp</item>
       <item name="android:textColor">#ffffff</item>
       <item name="android:gravity">center_horizontal</item>
       <item name="android:drawablePadding">4dp</item>
       <item name="android:layout_weight">1.0</item>
       <item name="android:background">@drawable/main_btn_bg_d</item>
   </style>

main_btn_bg_d.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/main_btn_bg" android:state_enabled="true" android:state_pressed="true"></item>
    <item android:drawable="@drawable/main_btn_bg" android:state_checked="true" android:state_enabled="true"></item>
</selector>

這裏需要注意的是:

1.main.xml中:TabWidget的id必須是@android:id/tabs,FrameLayout的id必須是 @android:id/tabcontent。

2.把TabWidget的Visibility設置成了gone!也就是默認難看的風格不見了2011-3-1_2.jpg取而代之的是5個帶風格的單選按鈕.


MainActivity類

package com.zhf.android_tabhost;
import android.app.TabActivity;
import android.content.Intent;
import android.os.Bundle;
import android.widget.RadioGroup;
import android.widget.RadioGroup.OnCheckedChangeListener;
import android.widget.TabHost;
import android.widget.TabHost.TabSpec;
public class MainActivity extends TabActivity {
    private TabHost tabHost;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           
        tabHost = this.getTabHost();
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           
        TabSpec homeSpec = tabHost.newTabSpec("home").setIndicator("home").setContent(new Intent(this,HomeActivity.class));
        TabSpec writeSpec = tabHost.newTabSpec("write").setIndicator("write").setContent(new Intent(this,WriteActivity.class));
        TabSpec msgSpec = tabHost.newTabSpec("msg").setIndicator("msg").setContent(new Intent(this,MsgActivity.class));
        TabSpec moreSpec = tabHost.newTabSpec("more").setIndicator("more").setContent(new Intent(this,MoreActivity.class));
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           
        tabHost.addTab(homeSpec);
        tabHost.addTab(writeSpec);
        tabHost.addTab(msgSpec);
        tabHost.addTab(moreSpec);
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           
        RadioGroup rg = (RadioGroup) this.findViewById(R.id.radioGroup);
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           
        rg.setOnCheckedChangeListener(new OnCheckedChangeListener() {
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               
            @Override
            public void onCheckedChanged(RadioGroup group, int checkedId) {
                switch (checkedId) {
                case R.id.rd_home:
                    tabHost.setCurrentTabByTag("home");
                    break;
                case R.id.rd_wt:
                    tabHost.setCurrentTabByTag("write");
                    break;
                case R.id.rd_msg:
                    tabHost.setCurrentTabByTag("msg");
                    break;
                case R.id.rd_more:
                    tabHost.setCurrentTabByTag("more");
                    break;
                default:
                    break;
                }
            }
        });
    }
}

注:

1.由於TabWidget被隱藏,所以相關的事件也會無效,這裏取巧用RadioGroup與RadioButton的特性來處理切換,然後監聽事件調用setCurrentTabByTag來切換Activity。

2.注意即使TabWidget被隱藏,也要爲其設置indicator,否則會保持。

效果圖:

174435324.png

點擊底部就可以實現切換不同的Activity的操作了,這裏需要注意的一點是,切換底部菜單時不會重新調用onCreate()方法的!!)



二.底部回調接口實現(使用Fragment)--- 重要!


這種方式使用了最新的Fragment,採用了底部導航欄回調接口的方法,來切換底部菜單,並且每次切換回重新調用onCreate()方法!!


main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical"
    tools:context=".MainActivity" >
    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="10"
        android:orientation="vertical" >
        <RelativeLayout
            android:id="@+id/main_title_RelativeLayout"
            android:layout_width="fill_parent"
            android:layout_height="50dp"
            android:background="@drawable/fragment_bottom_normal"
            android:orientation="horizontal" >
            <TextView
                android:id="@+id/main_title_TextView"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true"
                android:text="主頁"
                android:textColor="@android:color/white"
                android:textSize="24sp" />
        </RelativeLayout>
        <FrameLayout
            android:id="@+id/main_detail_FrameLayout"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:background="#ffffff" >
        </FrameLayout>
    </LinearLayout>
    <fragment
        android:id="@+id/bottom_fragment"
        android:name="com.zhf.frameworkdemo02.fragments.BottomFragment"
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />
</LinearLayout>


這裏由於我們底部菜單我們採用了fragment,所以佈局裏面要留出位置!


BottomFragment類:

package com.zhf.frameworkdemo02.fragments;
import com.zhf.frameworkdemo02.R;
import android.app.Activity;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RadioGroup;
import android.widget.RadioGroup.OnCheckedChangeListener;
/**
 * 頁面底部導航欄
 *
 * @author ZHF
 *
 */
public class BottomFragment extends Fragment {
    //默認回調接口實現類的對象
    private Callbacks callbacks = defaultCallbacks;
    /** Fragment和Activity建立關聯的時候調用 **/
    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        //當前類是否實現了底部導航欄點擊事件回調接口
        if(!(activity instanceof Callbacks)) {
            throw new IllegalStateException("Activity must implements fragment's callbacks !");
        }
        callbacks = (Callbacks) activity;
    }
    /** 爲Fragment加載佈局時調用 **/
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        RadioGroup radioGroup = (RadioGroup) inflater.inflate(R.layout.fragment_bottom, container, false);
        //綁定監聽器
        radioGroup.setOnCheckedChangeListener(changeListener);
        return radioGroup;
    }
                                                                                                                                                                                                                                                                                                                  
    /**RadioGroup監聽器**/
    private OnCheckedChangeListener changeListener = new OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(RadioGroup group, int checkedId) {
            System.out.println(checkedId);
            callbacks.onItemSelected(checkedId); //調用接口中方法
        }
    };
    public interface Callbacks{
        /**導航欄回調接口**/
        public void onItemSelected(int item);
    }
    /**默認回調實現類的對象**/
    private static Callbacks defaultCallbacks = new Callbacks() {
        @Override
        public void onItemSelected(int item) {
        //什麼都不幹
        }
    };
                                                                                                                                                                                                                                                                                                                  
    /**Fragment和Activity解除關聯的時候調用**/
    @Override
    public void onDetach() {
        super.onDetach();
       callbacks = defaultCallbacks;
    }
}

底部菜單佈局fragment_bottom.xml

<?xml version="1.0" encoding="utf-8"?>
<RadioGroup xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal" >
    <RadioButton
        android:id="@+id/fragment_bottom_home"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_weight="1"
        android:background="@drawable/fragment_bottom_selector"
        android:button="@null"
        android:drawableTop="@drawable/main_readiobutton_home"
        android:gravity="center"
        android:text="@string/home"
        android:textColor="@color/white"
        android:textSize="12sp" />
    <View
        android:layout_width="1dp"
        android:layout_height="fill_parent"
        android:background="@color/white" />
    <RadioButton
        android:id="@+id/fragment_bottom_order"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_weight="1"
        android:background="@drawable/fragment_bottom_selector"
        android:button="@null"
        android:drawableTop="@drawable/main_readiobutton_order"
        android:gravity="center"
        android:text="@string/order"
        android:textColor="@color/white"
        android:textSize="12sp" />
    <View
        android:layout_width="1dp"
        android:layout_height="fill_parent"
        android:background="@color/white"
        />
    <RadioButton
        android:id="@+id/fragment_bottom_notice"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_weight="1"
        android:background="@drawable/fragment_bottom_selector"
        android:button="@null"
        android:drawableTop="@drawable/main_readiobutton_notice"
        android:gravity="center"
        android:text="@string/notice"
        android:textColor="@color/white"
        android:textSize="12sp" />
    <View
        android:layout_width="1dp"
        android:layout_height="fill_parent"
        android:background="@color/white" />
    <RadioButton
        android:id="@+id/fragment_bottom_more"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_weight="1"
        android:background="@drawable/fragment_bottom_selector"
        android:button="@null"
        android:drawablePadding="3dip"
        android:drawableTop="@drawable/main_readiobutton_more"
        android:gravity="center"
        android:text="@string/more"
        android:textColor="@color/white"
        android:textSize="12sp" />
</RadioGroup>


這裏我們定義了一個框架類:GeneralFragment(所有的fragment界面都需繼承它)

package com.zhf.frameworkdemo02.fragments;
import java.io.Serializable;
import com.zhf.frameworkdemo02.R;
import com.zhf.frameworkdemo02.view.OrderView;
import com.zhf.frameworkdemo02.view.HomeView;
import com.zhf.frameworkdemo02.view.MoreView;
import com.zhf.frameworkdemo02.view.NoticeView;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
/**
 * 框架類,抽象公共方法
 * @author ZHF
 *
 */
public class GeneralFragment extends Fragment implements Serializable{
    /**
     *
     */
    private static final long serialVersionUID = 1L;
                                                                                                                                                                                                                                                              
    private int item; //用於區分底部菜單項
    protected static View main_title_RelativeLayout; //標題欄
    protected final static String key = "Bundle";   //跳轉值傳遞key的名稱
                                                                                                                                                                                                                                                              
    @Override
    public void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        if(getArguments() != null) {  //根據接收子類傳來的arguments判斷item的哪一項
            if(getArguments().containsKey(MainFragment.Item)) {
                item = getArguments().getInt(MainFragment.Item);
            }
        }
    }
                                                                                                                                                                                                                                                              
    /**爲Fragment加載佈局時調用 **/
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_general, container, false);
        GeneralFragment fragment = null;
        switch(item) {
        case R.id.fragment_bottom_home:  //初始化主頁
            fragment = new HomeView();
            break;
        case R.id.fragment_bottom_order:
            fragment = new OrderView();  //初始化訂單
             break;
        case R.id.fragment_bottom_notice:
            fragment = new NoticeView();   //初始化公告
            break;
        case R.id.fragment_bottom_more:
            fragment = new MoreView();  //初始化更多
            break;
        default:
            break;
        }
        if(fragment != null) {
            //更換mainView中間的內容(home,msg,at,more)
            getActivity().getSupportFragmentManager().beginTransaction().replace(R.id.general_fragment, fragment).commit();
        }
        main_title_RelativeLayout =  ((View) container.getParent()).findViewById(R.id.main_title_RelativeLayout);
        //將生成的view返回
        return view;
    }
                                                                                                                                                                                                                                                              
    /**設置標題**/
    protected void setTitle(Object title) {
        if(main_title_RelativeLayout != null) {
            //標題欄中的文字
            TextView mTvTitle = (TextView) main_title_RelativeLayout.findViewById(R.id.main_title_TextView);
            if(mTvTitle != null) {
                if(title instanceof Integer) {  //整型
                    mTvTitle.setText((Integer)title);
                } else { //字符類型
                    mTvTitle.setText((CharSequence)title);
                }
            }
        }
    }
                                                                                                                                                                                                                                                              
    /**頁面跳轉值傳遞**/
    protected void setBundle(Object... objects) {
        Bundle arguments = new Bundle();
        arguments.putSerializable(key, objects);
        GeneralFragment generalFragment = new GeneralFragment();
        generalFragment.setArguments(arguments);
    }
                                                                                                                                                                                                                                                              
    /**獲取所傳遞的值**/
    protected Object[] getBundle() {
        if(getArguments() != null) {
            System.out.println("getBundle");
            if(getArguments().containsKey(key)) {
                Object[] object = (Object[]) getArguments().getSerializable(key);
                return object;
            }
        }
        return null;
    }
                                                                                                                                                                                                                                                              
    /**無參頁面跳轉**/
    protected void toIntent(GeneralFragment generalFragment) {
        if(generalFragment != null) {
            getActivity().getSupportFragmentManager().beginTransaction().replace(R.id.general_fragment, generalFragment).commit();
        }
    }
}


程序入口MainFragment:

package com.zhf.frameworkdemo02.fragments;
import com.zhf.frameworkdemo02.R;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
/**
 *MainView
 * @author ZHF
 *
 */
public class MainFragment extends FragmentActivity implements BottomFragment.Callbacks {
                                                                                                                                                                                                                                 
    public final static String Item = "item";
                                                                                                                                                                                                                                 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        //初始化默認調用接口中item選中方法
        onItemSelected(R.id.fragment_bottom_home);
    }
    @Override
    public void onItemSelected(int item) {
        Bundle arguments = new Bundle();
        arguments.putInt(Item, item); //將選中的底部radio的Id放進去
        GeneralFragment generalFragment = new GeneralFragment();
        generalFragment.setArguments(arguments); //設置參數值
        //這裏根據item的ID進行界面跳轉
        FragmentManager fm = getSupportFragmentManager();
        fm.beginTransaction().replace(R.id.main_detail_FrameLayout, generalFragment).commit();
    }
}


說明:這裏我們的每個界面都將採用Fragment,故每個界面需重寫onCreateView()


package com.zhf.frameworkdemo02.view;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.zhf.frameworkdemo02.R;
import com.zhf.frameworkdemo02.fragments.GeneralFragment;
/**
 * 主頁面
 * @author ZHF
 *
 */
public class HomeView extends GeneralFragment{
    @Override
    public void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        super.setTitle("主頁");
    }
                                                                                                                                              
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        return inflater.inflate(R.layout.home, container, false);
    }
}

(其他三個略)

最終效果圖:

225533378.png


ok!大功告成!相當實用的!有興趣的可以學習一下!


源碼下載:http://down.51cto.com/data/1009354



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章