【移动开发】Android中的底部菜单框架(Fragment)

2014-09-02 14:38:50

Tags: android


今天,我将总结一下Android应用中大家经常见到的底部导航栏的几种实现!

 

一。TabHost + RadioGroup实现方式

在我们平时开发过程中使用的TabHost是在上方,这里我们简单修改了一下<TabHost>的布局,让叶片放到了底部。

main.xml

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<?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

1
2
3
4
5
6
7
8
9
<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

 

1
2
3
4
5
<?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类

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
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:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<?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类:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
<?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界面都需继承它)

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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()

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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

 

原文链接:http://smallwoniu.blog.51cto.com/3911954/1324123

上一篇: LBS 查询附近的方法

下一篇: Android所有系统资源图标android.R.drawable查看