安卓开发笔记——多种方式实现底部菜单栏(仿微信界面)
关于底部菜单是什么,我想没必要介绍了,在市场上的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.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>
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 @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>
具体实现代码:
由于这次的内容是基于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 }
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/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。
正在看本人博客的这位童鞋,我看你气度不凡,谈吐间隐隐有王者之气,日后必有一番作为!旁边有“推荐”二字,你就顺手把它点了吧,相得准,我分文不收;相不准,你也好回来找我!