关于底部菜单是什么,我想没必要介绍了,在市场上的APP里太常见了,这里提供两种方式来实现。
记得之前写过几篇关于底部菜单实现的方法,有兴趣的朋友可以看看:
1、《安卓开发复习笔记——TabHost组件(一)(实现底部菜单导航)》
2、《安卓开发复习笔记——TabHost组件(二)(实现底部菜单导航)》
3、《安卓开发笔记——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>
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>
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.ViewPager10 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>
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>
具体实现代码:
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 @Override22 public int getCount() {23 return views.size();24 }25 26 @Override27 public boolean isViewFromObject(View arg0, Object arg1) {28 return arg0 == arg1;29 }30 31 @Override32 public Object instantiateItem(ViewGroup container, int position) {33 View view = views.get(position);34 container.addView(view);35 return view;36 }37 38 @Override39 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 @Override108 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 @Override154 public void onPageScrollStateChanged(int arg0) {155 156 }157 158 @Override159 public void onPageScrolled(int arg0, float arg1, int arg2) {160 161 }162 163 @Override164 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 <FrameLayout10 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>
具体实现代码:
由于这次的内容是基于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 @Override12 public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {13 return inflater.inflate(R.layout.page_01, container, false);14 }15 16 }
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 //隐藏Fragment107 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个Linearlayout135 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个ImageView141 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 @Override155 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、界面不可滑动,比较死板。
作者:Balla_兔子
出处:http://www.cnblogs.com/lichenwei/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。
正在看本人博客的这位童鞋,我看你气度不凡,谈吐间隐隐有王者之气,日后必有一番作为!旁边有“推荐”二字,你就顺手把它点了吧,相得准,我分文不收;相不准,你也好回来找我!
- 1楼zdd
- 不错。