安卓开发笔记——多种方式实现底部菜单栏(仿微信界面)
分类:
IT文章
•
2025-01-17 10:08:13
关于底部菜单是什么,我想没必要介绍了,在市场上的APP里太常见了,这里提供两种方式来实现。
记得之前写过几篇关于底部菜单实现的方法,有兴趣的朋友可以看看:
1、《安卓开发复习笔记——TabHost组件(一)(实现底部菜单导航)》
安卓开发复习笔记——TabHost组件(二)(实现底部菜单导航)》
安卓开发笔记——Fragment+FragmentTabHost组件(实现新浪微博底部菜单)》
今天带来种相对更通俗易懂的写法,不再和过去一样去沿用TabHost了,这次我们直接用LinearLayout布局来实现,先来看下实现效果图:
中间内容区域有两种实现方式:
1、ViewPager --可滑动界面 2、Fragment --固定界面
1、界面分析

这里有个小技巧,把底部菜单栏的每一个小的LinearLayout的宽度都设置成0dp,然后用weight权重去分配它,中间内容区域也是把高度设置成0dp,然后用weight权重去分配它。(weight默认是把界面里空闲的位置作为划分位置,所以这里的宽度或者高度要注意设置成0dp)
2、具体实现(内容区域为ViewPager可滑动界面)
布局文件:
activity_top.xml(顶部布局)
1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
2 xmlns:tools="http://schemas.android.com/tools"
3 android:layout_width="match_parent"
4 android:layout_height="55dp"
5 android:background="@drawable/title_bar">
6
7 <TextView
8 android:layout_width="wrap_content"
9 android:layout_height="wrap_content"
10 android:layout_centerInParent="true"
11 android:text="微信"
12 android:textSize="20dp"
13 android:textColor="#ffffff"/>
14
15 </RelativeLayout>
View Code
activity_bottom.xml(底部布局)
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
2 xmlns:tools="http://schemas.android.com/tools"
3 android:layout_width="match_parent"
4 android:layout_height="wrap_content"
5 android:background="@drawable/bottom_bar" >
6
7 <LinearLayout
8 android:id="@+id/ll_home"
9 android:layout_width="0dp"
10 android:layout_height="wrap_content"
11 android:layout_weight="1"
12 android:gravity="center"
13 android:orientation="vertical" >
14
15 <ImageView
16 android:id="@+id/iv_home"
17 android:layout_width="wrap_content"
18 android:layout_height="wrap_content"
19 android:src="@drawable/tab_weixin_pressed" />
20
21 <TextView
22 android:id="@+id/tv_home"
23 android:layout_width="wrap_content"
24 android:layout_height="wrap_content"
25 android:layout_marginTop="3dp"
26 android:text="首页"
27 android:textColor="#1B940A"
28 android:textStyle="bold" />
29 </LinearLayout>
30
31 <LinearLayout
32 android:id="@+id/ll_address"
33 android:layout_width="0dp"
34 android:layout_height="wrap_content"
35 android:layout_weight="1"
36 android:gravity="center"
37 android:orientation="vertical" >
38
39 <ImageView
40 android:id="@+id/iv_address"
41 android:layout_width="wrap_content"
42 android:layout_height="wrap_content"
43 android:src="@drawable/tab_address_normal" />
44
45 <TextView
46 android:id="@+id/tv_address"
47 android:layout_width="wrap_content"
48 android:layout_height="wrap_content"
49 android:layout_marginTop="3dp"
50 android:text="通讯录"
51 android:textColor="#ffffff"
52 android:textStyle="bold" />
53 </LinearLayout>
54
55 <LinearLayout
56 android:id="@+id/ll_friend"
57 android:layout_width="0dp"
58 android:layout_height="wrap_content"
59 android:layout_weight="1"
60 android:gravity="center"
61 android:orientation="vertical" >
62
63 <ImageView
64 android:id="@+id/iv_friend"
65 android:layout_width="wrap_content"
66 android:layout_height="wrap_content"
67 android:src="@drawable/tab_find_frd_normal" />
68
69 <TextView
70 android:id="@+id/tv_friend"
71 android:layout_width="wrap_content"
72 android:layout_height="wrap_content"
73 android:layout_marginTop="3dp"
74 android:text="朋友"
75 android:textColor="#ffffff"
76 android:textStyle="bold" />
77 </LinearLayout>
78
79 <LinearLayout
80 android:id="@+id/ll_setting"
81 android:layout_width="0dp"
82 android:layout_height="wrap_content"
83 android:layout_weight="1"
84 android:gravity="center"
85 android:orientation="vertical" >
86
87 <ImageView
88 android:id="@+id/iv_setting"
89 android:layout_width="wrap_content"
90 android:layout_height="wrap_content"
91 android:src="@drawable/tab_settings_normal" />
92
93 <TextView
94 android:id="@+id/tv_setting"
95 android:layout_width="wrap_content"
96 android:layout_height="wrap_content"
97 android:layout_marginTop="3dp"
98 android:text="设置"
99 android:textColor="#ffffff"
100 android:textStyle="bold" />
101 </LinearLayout>
102
103 </LinearLayout>
View Code
activity_main.xml(主布局,引入上下布局)
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
2 xmlns:tools="http://schemas.android.com/tools"
3 android:layout_width="match_parent"
4 android:layout_height="match_parent"
5 android:orientation="vertical" >
6
7 <include layout="@layout/activity_top" />
8
9 <android.support.v4.view.ViewPager
10 android:id="@+id/vp_content"
11 android:layout_width="match_parent"
12 android:background="#ffffff"
13 android:layout_height="0dp"
14 android:layout_weight="1" >
15 </android.support.v4.view.ViewPager>
16
17 <include layout="@layout/activity_bottom" />
18
19 </LinearLayout>
View Code
page_01.xml-page_04.xml(4个ViewPager的滑动界面,由于内容简单这里只给出其中1个)
1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
2 xmlns:tools="http://schemas.android.com/tools"
3 android:layout_width="match_parent"
4 android:layout_height="match_parent">
5
6 <TextView
7 android:layout_width="wrap_content"
8 android:layout_height="wrap_content"
9 android:layout_centerInParent="true"
10 android:text="我是微信首页"
11 android:textSize="30dp" />
12
13 </RelativeLayout>
View Code
具体实现代码:
ViewPager适配器(关于ViewPager的使用方法这里就不多说了,不清楚的朋友看我这篇文章吧《安卓开发笔记——ViewPager组件(仿微信引导界面)》)
1 package com.rabbit.tabdemo;
2
3 import java.util.List;
4
5 import android.support.v4.view.PagerAdapter;
6 import android.view.View;
7 import android.view.ViewGroup;
8 /**
9 * ViewPager适配器
10 * @author Balla_兔子
11 *
12 */
13 public class ContentAdapter extends PagerAdapter {
14
15 private List<View> views;
16
17 public ContentAdapter(List<View> views) {
18 this.views = views;
19 }
20
21 @Override
22 public int getCount() {
23 return views.size();
24 }
25
26 @Override
27 public boolean isViewFromObject(View arg0, Object arg1) {
28 return arg0 == arg1;
29 }
30
31 @Override
32 public Object instantiateItem(ViewGroup container, int position) {
33 View view = views.get(position);
34 container.addView(view);
35 return view;
36 }
37
38 @Override
39 public void destroyItem(ViewGroup container, int position, Object object) {
40 container.removeView(views.get(position));
41 }
42
43 }
MainActivity(主界面代码)
初始化控件后,完成对底部菜单的4个LinearLayout的点击监听事件,在用户触发点击事件的时候,设置选择状态然后跳转相对应的界面。为了使得滑动ViewPager也能同时触发底部菜单状态的改变,这里也对ViewPager设置了滑动监听。其他的代码注释很全,看注释就可以了。
1 package com.rabbit.tabdemo;
2
3 import java.util.ArrayList;
4 import java.util.List;
5
6 import android.app.Activity;
7 import android.os.Bundle;
8 import android.support.v4.view.ViewPager;
9 import android.support.v4.view.ViewPager.OnPageChangeListener;
10 import android.view.View;
11 import android.view.View.OnClickListener;
12 import android.widget.ImageView;
13 import android.widget.LinearLayout;
14 import android.widget.TextView;
15
16 public class MainActivity extends Activity implements OnClickListener,OnPageChangeListener{
17
18 // 底部菜单4个Linearlayout
19 private LinearLayout ll_home;
20 private LinearLayout ll_address;
21 private LinearLayout ll_friend;
22 private LinearLayout ll_setting;
23
24 // 底部菜单4个ImageView
25 private ImageView iv_home;
26 private ImageView iv_address;
27 private ImageView iv_friend;
28 private ImageView iv_setting;
29
30 // 底部菜单4个菜单标题
31 private TextView tv_home;
32 private TextView tv_address;
33 private TextView tv_friend;
34 private TextView tv_setting;
35
36 // 中间内容区域
37 private ViewPager viewPager;
38
39 // ViewPager适配器ContentAdapter
40 private ContentAdapter adapter;
41
42 private List<View> views;
43
44 @Override
45 protected void onCreate(Bundle savedInstanceState) {
46 super.onCreate(savedInstanceState);
47 setContentView(R.layout.activity_main);
48
49 // 初始化控件
50 initView();
51 // 初始化底部按钮事件
52 initEvent();
53
54 }
55
56 private void initEvent() {
57 // 设置按钮监听
58 ll_home.setOnClickListener(this);
59 ll_address.setOnClickListener(this);
60 ll_friend.setOnClickListener(this);
61 ll_setting.setOnClickListener(this);
62
63 //设置ViewPager滑动监听
64 viewPager.setOnPageChangeListener(this);
65 }
66
67 private void initView() {
68
69 // 底部菜单4个Linearlayout
70 this.ll_home = (LinearLayout) findViewById(R.id.ll_home);
71 this.ll_address = (LinearLayout) findViewById(R.id.ll_address);
72 this.ll_friend = (LinearLayout) findViewById(R.id.ll_friend);
73 this.ll_setting = (LinearLayout) findViewById(R.id.ll_setting);
74
75 // 底部菜单4个ImageView
76 this.iv_home = (ImageView) findViewById(R.id.iv_home);
77 this.iv_address = (ImageView) findViewById(R.id.iv_address);
78 this.iv_friend = (ImageView) findViewById(R.id.iv_friend);
79 this.iv_setting = (ImageView) findViewById(R.id.iv_setting);
80
81 // 底部菜单4个菜单标题
82 this.tv_home = (TextView) findViewById(R.id.tv_home);
83 this.tv_address = (TextView) findViewById(R.id.tv_address);
84 this.tv_friend = (TextView) findViewById(R.id.tv_friend);
85 this.tv_setting = (TextView) findViewById(R.id.tv_setting);
86
87 // 中间内容区域ViewPager
88 this.viewPager = (ViewPager) findViewById(R.id.vp_content);
89
90 // 适配器
91 View page_01 = View.inflate(MainActivity.this, R.layout.page_01, null);
92 View page_02 = View.inflate(MainActivity.this, R.layout.page_02, null);
93 View page_03 = View.inflate(MainActivity.this, R.layout.page_03, null);
94 View page_04 = View.inflate(MainActivity.this, R.layout.page_04, null);
95
96 views = new ArrayList<View>();
97 views.add(page_01);
98 views.add(page_02);
99 views.add(page_03);
100 views.add(page_04);
101
102 this.adapter = new ContentAdapter(views);
103 viewPager.setAdapter(adapter);
104
105 }
106
107 @Override
108 public void onClick(View v) {
109 // 在每次点击后将所有的底部按钮(ImageView,TextView)颜色改为灰色,然后根据点击着色
110 restartBotton();
111 // ImageView和TetxView置为绿色,页面随之跳转
112 switch (v.getId()) {
113 case R.id.ll_home:
114 iv_home.setImageResource(R.drawable.tab_weixin_pressed);
115 tv_home.setTextColor(0xff1B940A);
116 viewPager.setCurrentItem(0);
117 break;
118 case R.id.ll_address:
119 iv_address.setImageResource(R.drawable.tab_address_pressed);
120 tv_address.setTextColor(0xff1B940A);
121 viewPager.setCurrentItem(1);
122 break;
123 case R.id.ll_friend:
124 iv_friend.setImageResource(R.drawable.tab_find_frd_pressed);
125 tv_friend.setTextColor(0xff1B940A);
126 viewPager.setCurrentItem(2);
127 break;
128 case R.id.ll_setting:
129 iv_setting.setImageResource(R.drawable.tab_find_frd_pressed);
130 tv_setting.setTextColor(0xff1B940A);
131 viewPager.setCurrentItem(3);
132 break;
133
134 default:
135 break;
136 }
137
138 }
139
140 private void restartBotton() {
141 // ImageView置为灰色
142 iv_home.setImageResource(R.drawable.tab_weixin_normal);
143 iv_address.setImageResource(R.drawable.tab_address_normal);
144 iv_friend.setImageResource(R.drawable.tab_find_frd_normal);
145 iv_setting.setImageResource(R.drawable.tab_settings_normal);
146 // TextView置为白色
147 tv_home.setTextColor(0xffffffff);
148 tv_address.setTextColor(0xffffffff);
149 tv_friend.setTextColor(0xffffffff);
150 tv_setting.setTextColor(0xffffffff);
151 }
152
153 @Override
154 public void onPageScrollStateChanged(int arg0) {
155
156 }
157
158 @Override
159 public void onPageScrolled(int arg0, float arg1, int arg2) {
160
161 }
162
163 @Override
164 public void onPageSelected(int arg0) {
165 restartBotton();
166 //当前view被选择的时候,改变底部菜单图片,文字颜色
167 switch (arg0) {
168 case 0:
169 iv_home.setImageResource(R.drawable.tab_weixin_pressed);
170 tv_home.setTextColor(0xff1B940A);
171 break;
172 case 1:
173 iv_address.setImageResource(R.drawable.tab_address_pressed);
174 tv_address.setTextColor(0xff1B940A);
175 break;
176 case 2:
177 iv_friend.setImageResource(R.drawable.tab_find_frd_pressed);
178 tv_friend.setTextColor(0xff1B940A);
179 break;
180 case 3:
181 iv_setting.setImageResource(R.drawable.tab_find_frd_pressed);
182 tv_setting.setTextColor(0xff1B940A);
183 break;
184
185 default:
186 break;
187 }
188
189 }
190
191 }
3、具体实现(内容区域为Fragment固定界面)
布局文件:
布局文件基本没变化,只是把主界面的ViewPager改成了FramLayout,其他文件保持一致,就不贴出来了。
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
2 xmlns:tools="http://schemas.android.com/tools"
3 android:layout_width="match_parent"
4 android:layout_height="match_parent"
5 android:orientation="vertical" >
6
7 <include layout="@layout/activity_top" />
8
9 <FrameLayout
10 android:id="@+id/fl_content"
11 android:layout_width="match_parent"
12 android:background="#ffffff"
13 android:layout_height="0dp"
14 android:layout_weight="1" >
15 </FrameLayout>
16
17 <include layout="@layout/activity_bottom" />
18
19 </LinearLayout>
View Code
具体实现代码:
由于这次的内容是基于Fragment的,所以需要有4个Fragment文件,由于代码相同这里只贴出一个。
Frgament(HomeFragment,AddressFragment,FriendFragment,SettingFragment)
1 package com.rabbit.tabdemo;
2
3 import android.os.Bundle;
4 import android.support.annotation.Nullable;
5 import android.support.v4.app.Fragment;
6 import android.view.LayoutInflater;
7 import android.view.View;
8 import android.view.ViewGroup;
9
10 public class HomeFragment extends Fragment {
11 @Override
12 public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
13 return inflater.inflate(R.layout.page_01, container, false);
14 }
15
16 }
View Code
MainActivity(主界面代码)
代码很简单,一看就能明白就不多说什么了,只提个需要注意的地方,由于便于向下兼容这里的Fragment是用V4包下的,在导入包的时候需要注意一下。
1 package com.rabbit.tabdemo;
2
3 import android.os.Bundle;
4 import android.support.v4.app.Fragment;
5 import android.support.v4.app.FragmentActivity;
6 import android.support.v4.app.FragmentManager;
7 import android.support.v4.app.FragmentTransaction;
8 import android.view.View;
9 import android.view.View.OnClickListener;
10 import android.widget.ImageView;
11 import android.widget.LinearLayout;
12 import android.widget.TextView;
13
14 public class MainActivity extends FragmentActivity implements OnClickListener {
15 // 底部菜单4个Linearlayout
16 private LinearLayout ll_home;
17 private LinearLayout ll_address;
18 private LinearLayout ll_friend;
19 private LinearLayout ll_setting;
20
21 // 底部菜单4个ImageView
22 private ImageView iv_home;
23 private ImageView iv_address;
24 private ImageView iv_friend;
25 private ImageView iv_setting;
26
27 // 底部菜单4个菜单标题
28 private TextView tv_home;
29 private TextView tv_address;
30 private TextView tv_friend;
31 private TextView tv_setting;
32
33 // 4个Fragment
34 private Fragment homeFragment;
35 private Fragment addressFragment;
36 private Fragment friendFragment;
37 private Fragment settingFragment;
38
39 @Override
40 protected void onCreate(Bundle savedInstanceState) {
41 super.onCreate(savedInstanceState);
42 setContentView(R.layout.activity_main);
43
44 // 初始化控件
45 initView();
46 // 初始化底部按钮事件
47 initEvent();
48 // 初始化并设置当前Fragment
49 initFragment(0);
50
51 }
52
53 private void initFragment(int index) {
54 // 由于是引用了V4包下的Fragment,所以这里的管理器要用getSupportFragmentManager获取
55 FragmentManager fragmentManager = getSupportFragmentManager();
56 // 开启事务
57 FragmentTransaction transaction = fragmentManager.beginTransaction();
58 // 隐藏所有Fragment
59 hideFragment(transaction);
60 switch (index) {
61 case 0:
62 if (homeFragment == null) {
63 homeFragment = new HomeFragment();
64 transaction.add(R.id.fl_content, homeFragment);
65 } else {
66 transaction.show(homeFragment);
67 }
68 break;
69 case 1:
70 if (addressFragment == null) {
71 addressFragment = new AddressFragment();
72 transaction.add(R.id.fl_content, addressFragment);
73 } else {
74 transaction.show(addressFragment);
75 }
76
77 break;
78 case 2:
79 if (friendFragment == null) {
80 friendFragment = new FriendFragment();
81 transaction.add(R.id.fl_content, friendFragment);
82 } else {
83 transaction.show(friendFragment);
84 }
85
86 break;
87 case 3:
88 if (settingFragment == null) {
89 settingFragment = new SettingFragment();
90 transaction.add(R.id.fl_content, settingFragment);
91 } else {
92 transaction.show(settingFragment);
93 }
94
95 break;
96
97 default:
98 break;
99 }
100
101 // 提交事务
102 transaction.commit();
103
104 }
105
106 //隐藏Fragment
107 private void hideFragment(FragmentTransaction transaction) {
108 if (homeFragment != null) {
109 transaction.hide(homeFragment);
110 }
111 if (addressFragment != null) {
112 transaction.hide(addressFragment);
113 }
114 if (friendFragment != null) {
115 transaction.hide(friendFragment);
116 }
117 if (settingFragment != null) {
118 transaction.hide(settingFragment);
119 }
120
121 }
122
123 private void initEvent() {
124 // 设置按钮监听
125 ll_home.setOnClickListener(this);
126 ll_address.setOnClickListener(this);
127 ll_friend.setOnClickListener(this);
128 ll_setting.setOnClickListener(this);
129
130 }
131
132 private void initView() {
133
134 // 底部菜单4个Linearlayout
135 this.ll_home = (LinearLayout) findViewById(R.id.ll_home);
136 this.ll_address = (LinearLayout) findViewById(R.id.ll_address);
137 this.ll_friend = (LinearLayout) findViewById(R.id.ll_friend);
138 this.ll_setting = (LinearLayout) findViewById(R.id.ll_setting);
139
140 // 底部菜单4个ImageView
141 this.iv_home = (ImageView) findViewById(R.id.iv_home);
142 this.iv_address = (ImageView) findViewById(R.id.iv_address);
143 this.iv_friend = (ImageView) findViewById(R.id.iv_friend);
144 this.iv_setting = (ImageView) findViewById(R.id.iv_setting);
145
146 // 底部菜单4个菜单标题
147 this.tv_home = (TextView) findViewById(R.id.tv_home);
148 this.tv_address = (TextView) findViewById(R.id.tv_address);
149 this.tv_friend = (TextView) findViewById(R.id.tv_friend);
150 this.tv_setting = (TextView) findViewById(R.id.tv_setting);
151
152 }
153
154 @Override
155 public void onClick(View v) {
156
157 // 在每次点击后将所有的底部按钮(ImageView,TextView)颜色改为灰色,然后根据点击着色
158 restartBotton();
159 // ImageView和TetxView置为绿色,页面随之跳转
160 switch (v.getId()) {
161 case R.id.ll_home:
162 iv_home.setImageResource(R.drawable.tab_weixin_pressed);
163 tv_home.setTextColor(0xff1B940A);
164 initFragment(0);
165 break;
166 case R.id.ll_address:
167 iv_address.setImageResource(R.drawable.tab_address_pressed);
168 tv_address.setTextColor(0xff1B940A);
169 initFragment(1);
170 break;
171 case R.id.ll_friend:
172 iv_friend.setImageResource(R.drawable.tab_find_frd_pressed);
173 tv_friend.setTextColor(0xff1B940A);
174 initFragment(2);
175 break;
176 case R.id.ll_setting:
177 iv_setting.setImageResource(R.drawable.tab_find_frd_pressed);
178 tv_setting.setTextColor(0xff1B940A);
179 initFragment(3);
180 break;
181
182 default:
183 break;
184 }
185
186 }
187
188 private void restartBotton() {
189 // ImageView置为灰色
190 iv_home.setImageResource(R.drawable.tab_weixin_normal);
191 iv_address.setImageResource(R.drawable.tab_address_normal);
192 iv_friend.setImageResource(R.drawable.tab_find_frd_normal);
193 iv_setting.setImageResource(R.drawable.tab_settings_normal);
194 // TextView置为白色
195 tv_home.setTextColor(0xffffffff);
196 tv_address.setTextColor(0xffffffff);
197 tv_friend.setTextColor(0xffffffff);
198 tv_setting.setTextColor(0xffffffff);
199 }
200
201 }

到这里界面效果就基本实现了,就算是旋转屏幕也能够很好的达到适配效果,最后我们还需要做的2点,可能有些朋友已经发现了,在我们旋转屏幕的时候,Fragment会重新调用onCreate方法,导致成员变量重新初始化了一次,Fragment对象也重置为空,然后就调用不到hide方法,从而出现了界面重复叠加的情况。
下面提供解决的方法,其实很简单,只需要在AndroidManifest.xml里面对应的activity里添设置改换屏幕方向等操作时不触发oncreate事件就可以。
1 android:configChanges="orientation|keyboardHidden|screenSize"
最后我们隐藏下标题栏,在application里添加上:
1 android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"
这样就大功告成了!
总结:
基于ViewPager实现的内容:
优点:
1、界面可以滑动,美观,流畅。
缺点:
1、当界面里有一些需要用手势来实现的内容会起冲突,比如我们ListView里的侧滑删除。
2、由于采用的是ViewPager,所以页面内容实现代码会严重依赖于MainActivity,代码太过冗余,不便于后期维护。
基于Fragment实现的内容:
优点:
1、Fragment文件单独存在,各自页面的内容各自去实现完成,有自己的生命周期,便于后期维护。
2、对于需要手势操作的一些内容不会起冲突。
缺点:
1、界面不可滑动,比较死板。
补充:如果既想有ViewPager的滑动效果,又想ViewPager的页卡里嵌套Fragment,可以使用FragmentPagerAdapter作为适配器,但需要注意Fragment生命周期的管理。
作者:Balla_兔子
出处:http://www.cnblogs.com/lichenwei/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。
正在看本人博客的这位童鞋,我看你气度不凡,谈吐间隐隐有王者之气,日后必有一番作为!旁边有“推荐”二字,你就顺手把它点了吧,相得准,我分文不收;相不准,你也好回来找我!