前言:项目第二版刚上线没多久,产品又对需求进行了大改动,以前用的是左滑菜单,现在又要换成底部导航栏,于是今天又苦逼加班了.花了几个小时实现了一个底部导航栏的demo,然后总结一下.写一篇博客.供自己以后参考.也可以给没有做过的朋友进行参考.以后大家有类似的功能就可以在我的demo上就行修改.
一.先上效果图: 本来是打算用FragmentTabHost实现的,但是中间那个按钮有点麻烦,想到我们项目好几个产品经理,并且经常改需求,于是最后决定 用 TextView+Fragment去实现.
二.查看代码实现.代码是我们最好的老师.
主界面布局文件 activity_main.xml 外层一个FrameLayout+ImageView+LinearLayout TextView选中跟未选中时 图片和颜色 切换都用布局去实现.
1).FrameLayout 用于显示fragment
2).ImageView 显示底部最中间那个图标
3).LinearLayout 显示底部四个图标 我这里用weight分成了5份,第三个控件啥都没有就用View控件占了一个位置
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" > <FrameLayout android:id="@+id/main_container" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_above="@+id/view_line"/> <View android:id="@+id/view_line" android:layout_height="1dp" android:layout_width="match_parent" android:background="#DCDBDB" android:layout_above="@+id/rl_bottom"/> <LinearLayout android:id="@+id/rl_bottom" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:paddingTop="5dp" android:paddingBottom="5dp" android:background="#F2F2F2" android:orientation="horizontal" > <TextView android:id="@+id/tv_main" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_weight="1" android:drawableTop="@drawable/tab_item_main_img_selector" android:drawablePadding="@dimen/main_tab_item_image_and_text" android:focusable="true" android:gravity="center" android:text="@string/main" android:textColor="@drawable/tabitem_txt_sel" /> <TextView android:id="@+id/tv_dynamic" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_weight="1" android:drawableTop="@drawable/tab_item_dynamic_img_selector" android:drawablePadding="@dimen/main_tab_item_image_and_text" android:focusable="true" android:gravity="center" android:text="@string/dynamic" android:textColor="@drawable/tabitem_txt_sel" /> <View android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" /> <TextView android:id="@+id/tv_message" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_weight="1" android:drawableTop="@drawable/tab_item_message_img_selector" android:drawablePadding="@dimen/main_tab_item_image_and_text" android:focusable="true" android:gravity="center" android:text="@string/message" android:textColor="@drawable/tabitem_txt_sel" /> <TextView android:id="@+id/tv_person" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_weight="1" android:drawableTop="@drawable/tab_item_person_img_selector" android:drawablePadding="@dimen/main_tab_item_image_and_text" android:focusable="true" android:gravity="center" android:text="@string/person" android:textColor="@drawable/tabitem_txt_sel"/> </LinearLayout> <ImageView android:id="@+id/iv_make" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:paddingBottom="10dp" android:src="@drawable/icon_tab_make_select"/> </RelativeLayout>
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <!-- Non focused states --> <item android:drawable="@drawable/icon_tab_main_normal" android:state_focused="false" android:state_pressed="false" android:state_selected="false"/> <item android:drawable="@drawable/icon_tab_main_select" android:state_focused="false" android:state_pressed="false" android:state_selected="true"/> <!-- Focused states --> <item android:drawable="@drawable/icon_tab_main_select" android:state_focused="true" android:state_pressed="false" android:state_selected="false"/> <item android:drawable="@drawable/icon_tab_main_select" android:state_focused="true" android:state_pressed="false" android:state_selected="true"/> <!-- Pressed --> <item android:drawable="@drawable/icon_tab_main_select" android:state_pressed="true" android:state_selected="true"/> <item android:drawable="@drawable/icon_tab_main_select" android:state_pressed="true"/> </selector>
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <!-- Non focused states --> <item android:state_focused="false" android:state_pressed="false" android:state_selected="false" android:color="@color/main_tab_item_text_normal"/> <item android:state_focused="false" android:state_pressed="false" android:state_selected="true" android:color="@color/main_tab_item_text_select"/> <!-- Focused states --> <item android:state_focused="true" android:state_pressed="false" android:state_selected="false" android:color="@color/main_tab_item_text_select"/> <item android:state_focused="true" android:state_pressed="false" android:state_selected="true" android:color="@color/main_tab_item_text_select"/> <!-- Pressed --> <item android:state_pressed="true" android:state_selected="true" android:color="@color/main_tab_item_text_select"/> <item android:state_pressed="true" android:color="@color/main_tab_item_text_select"/> </selector>
/** * 对fragment的切换,底部图标颜色的切换 * @author ansen * @create time 2015-09-08 */ public class MainActivity extends FragmentActivity { //要切换显示的四个Fragment private MainFragment mainFragment; private DynamicFragment dynamicFragment; private MessageFragment messageFragment; private PersonFragment personFragment; private int currentId = R.id.tv_main;// 当前选中id,默认是主页 private TextView tvMain, tvDynamic, tvMessage, tvPerson;//底部四个TextView @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tvMain = (TextView) findViewById(R.id.tv_main); tvMain.setSelected(true);//首页默认选中 tvDynamic = (TextView) findViewById(R.id.tv_dynamic); tvMessage = (TextView) findViewById(R.id.tv_message); tvPerson = (TextView) findViewById(R.id.tv_person); /** * 默认加载首页 */ mainFragment = new MainFragment(); getSupportFragmentManager().beginTransaction().add(R.id.main_container, mainFragment).commit(); tvMain.setOnClickListener(tabClickListener); tvDynamic.setOnClickListener(tabClickListener); tvMessage.setOnClickListener(tabClickListener); tvPerson.setOnClickListener(tabClickListener); findViewById(R.id.iv_make).setOnClickListener(onClickListener); } private OnClickListener onClickListener=new OnClickListener() { @Override public void onClick(View v) { switch (v.getId()) { case R.id.iv_make: Intent intent=new Intent(MainActivity.this, MakeActivity.class); startActivity(intent); break; } } }; private OnClickListener tabClickListener = new OnClickListener() { @Override public void onClick(View v) { if (v.getId() != currentId) {//如果当前选中跟上次选中的一样,不需要处理 changeSelect(v.getId());//改变图标跟文字颜色的选中 changeFragment(v.getId());//fragment的切换 currentId = v.getId();//设置选中id } } }; /** * 改变fragment的显示 * * @param resId */ private void changeFragment(int resId) { FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();//开启一个Fragment事务 hideFragments(transaction);//隐藏所有fragment if(resId==R.id.tv_main){//主页 if(mainFragment==null){//如果为空先添加进来.不为空直接显示 mainFragment = new MainFragment(); transaction.add(R.id.main_container,mainFragment); }else { transaction.show(mainFragment); } }else if(resId==R.id.tv_dynamic){//动态 if(dynamicFragment==null){ dynamicFragment = new DynamicFragment(); transaction.add(R.id.main_container,dynamicFragment); }else { transaction.show(dynamicFragment); } }else if(resId==R.id.tv_message){//消息中心 if(messageFragment==null){ messageFragment = new MessageFragment(); transaction.add(R.id.main_container,messageFragment); }else { transaction.show(messageFragment); } }else if(resId==R.id.tv_person){//我 if(personFragment==null){ personFragment = new PersonFragment(); transaction.add(R.id.main_container,personFragment); }else { transaction.show(personFragment); } } transaction.commit();//一定要记得提交事务 } /** * 显示之前隐藏所有fragment * @param transaction */ private void hideFragments(FragmentTransaction transaction){ if (mainFragment != null)//不为空才隐藏,如果不判断第一次会有空指针异常 transaction.hide(mainFragment); if (dynamicFragment != null) transaction.hide(dynamicFragment); if (messageFragment != null) transaction.hide(messageFragment); if (personFragment != null) transaction.hide(personFragment); } /** * 改变TextView选中颜色 * @param resId */ private void changeSelect(int resId) { tvMain.setSelected(false); tvDynamic.setSelected(false); tvMessage.setSelected(false); tvPerson.setSelected(false); switch (resId) { case R.id.tv_main: tvMain.setSelected(true); break; case R.id.tv_dynamic: tvDynamic.setSelected(true); break; case R.id.tv_message: tvMessage.setSelected(true); break; case R.id.tv_person: tvPerson.setSelected(true); break; } } }
MainFragment.java 首页有三个页面(关注,推荐,动态),我用到了ViewPager滑动,增加了滑动指示状态.并且给标题栏的三个TextView设置了点击效果.
/** * 首页 * @author Ansen * @create time 2015-09-08 */ public class MainFragment extends Fragment { private ViewPager vPager; private List<Fragment> list = new ArrayList<Fragment>(); private MessageGroupFragmentAdapter adapter; private ImageView ivShapeCircle; private TextView tvFollow,tvRecommend,tvLocation; private int offset=0;//偏移量216 我这边只是举例说明,不同手机值不一样 private int currentIndex=1; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_main, null); /** * 初始化三个Fragment 并且填充到ViewPager */ vPager = (ViewPager) rootView.findViewById(R.id.viewpager_home); DynamicFragment dynamicFragment = new DynamicFragment(); MessageFragment messageFragment = new MessageFragment(); PersonFragment personFragment = new PersonFragment(); list.add(dynamicFragment); list.add(messageFragment); list.add(personFragment); adapter = new MessageGroupFragmentAdapter(getActivity().getSupportFragmentManager(), list); vPager.setAdapter(adapter); vPager.setOffscreenPageLimit(2); vPager.setCurrentItem(1); vPager.setOnPageChangeListener(pageChangeListener); ivShapeCircle = (ImageView) rootView.findViewById(R.id.iv_shape_circle); tvFollow=(TextView) rootView.findViewById(R.id.tv_follow); tvRecommend=(TextView) rootView.findViewById(R.id.tv_recommend); tvRecommend.setSelected(true);//推荐默认选中 tvLocation=(TextView) rootView.findViewById(R.id.tv_location); /** * 标题栏三个按钮设置点击效果 */ tvFollow.setOnClickListener(clickListener); tvRecommend.setOnClickListener(clickListener); tvLocation.setOnClickListener(clickListener); initCursorPosition(); return rootView; } private OnClickListener clickListener=new OnClickListener() { @Override public void onClick(View v) { switch (v.getId()) { case R.id.tv_follow: //当我们设置setCurrentItem的时候就会触发viewpager的OnPageChangeListener借口, //所以我们不需要去改变标题栏字体啥的 vPager.setCurrentItem(0); break; case R.id.tv_recommend: vPager.setCurrentItem(1); break; case R.id.tv_location: vPager.setCurrentItem(2); break; } } }; private void initCursorPosition() { DisplayMetrics metric = new DisplayMetrics(); getActivity().getWindowManager().getDefaultDisplay().getMetrics(metric); int width = metric.widthPixels; Matrix matrix = new Matrix(); //标题栏我用weight设置权重 分成5份 //(width / 5) * 2 这里表示标题栏两个控件的宽度 //(width / 10) 标题栏一个控件的2分之一 //7 约等于原点宽度的一半 matrix.postTranslate((width / 5) * 2 + (width / 10)-7,0);//图片平移 ivShapeCircle.setImageMatrix(matrix); //一个控件的宽度 我的手机宽度是1080/5=216 不同的手机宽度会不一样哦 offset=(width / 5); } /** * ViewPager滑动监听,用位移动画实现指示器效果 * * TranslateAnimation 强调一个地方,无论你移动了多少次,现在停留在哪里,你的起始位置从未变化过. * 例如:我这个demo里面 推荐移动到了同城,指示器也停留到了同城下面,但是指示器在屏幕上的位置还是推荐下面. */ private OnPageChangeListener pageChangeListener = new OnPageChangeListener() { @Override public void onPageSelected(int index) { changeTextColor(index); translateAnimation(index); } @Override public void onPageScrolled(int arg0, float arg1, int arg2) { } @Override public void onPageScrollStateChanged(int arg0) { } }; /** * 改变标题栏字体颜色 * @param index */ private void changeTextColor(int index){ tvFollow.setSelected(false); tvRecommend.setSelected(false); tvLocation.setSelected(false); switch (index) { case 0: tvFollow.setSelected(true); break; case 1: tvRecommend.setSelected(true); break; case 2: tvLocation.setSelected(true); break; } } /** * 移动标题栏点点点... * @param index */ private void translateAnimation(int index){ TranslateAnimation animation = null; switch(index){ case 0: if(currentIndex==1){//从推荐移动到关注 X坐标向左移动216 animation=new TranslateAnimation(0,-offset,0,0); }else if (currentIndex == 2) {//从同城移动到关注 X坐标向左移动216*2 记住起始x坐标是同城那里 animation = new TranslateAnimation(offset, -offset, 0, 0); } break; case 1: if(currentIndex==0){//从关注移动到推荐 X坐标向右移动216 animation=new TranslateAnimation(-offset,0,0,0); }else if(currentIndex==2){//从同城移动到推荐 X坐标向左移动216 animation=new TranslateAnimation(offset, 0,0,0); } break; case 2: if (currentIndex == 0) {//从关注移动到同城 X坐标向右移动216*2 记住起始x坐标是关注那里 animation = new TranslateAnimation(-offset, offset, 0, 0); } else if(currentIndex==1){//从推荐移动到同城 X坐标向右移动216 animation=new TranslateAnimation(0,offset,0,0); } break; } animation.setFillAfter(true); animation.setDuration(300); ivShapeCircle.startAnimation(animation); currentIndex=index; } }
首页显示的MainFragment.java的布局文件 fragment_main.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <LinearLayout android:id="@+id/ll_title" android:layout_width="match_parent" android:layout_height="44dp" android:background="#00ceaa" android:orientation="vertical" > <LinearLayout android:layout_width="match_parent" android:layout_height="30dp" android:orientation="horizontal" > <View android:id="@+id/view_empty" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" /> <TextView android:id="@+id/tv_follow" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:paddingTop="5dp" android:text="关注" android:gravity="center_horizontal" android:textColor="@drawable/main_title_txt_sel" android:textSize="20sp" /> <TextView android:id="@+id/tv_recommend" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:paddingTop="5dp" android:text="推荐" android:gravity="center_horizontal" android:textColor="@drawable/main_title_txt_sel" android:textSize="20sp" /> <TextView android:id="@+id/tv_location" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:paddingTop="5dp" android:text="同城" android:gravity="center_horizontal" android:textColor="@drawable/main_title_txt_sel" android:textSize="20sp" /> <View android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" /> </LinearLayout> <ImageView android:id="@+id/iv_shape_circle" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginTop="2dp" android:scaleType="matrix" android:src="@drawable/shape_circle" /> </LinearLayout> <android.support.v4.view.ViewPager android:id="@+id/viewpager_home" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@+id/ll_title" /> </RelativeLayout>
MessageGroupFragmentAdapter.java ViewPager的适配器.
public class MessageGroupFragmentAdapter extends FragmentStatePagerAdapter { private List<Fragment>list; public MessageGroupFragmentAdapter(FragmentManager fm, List<Fragment> list) { super(fm); this.list = list; } public MessageGroupFragmentAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int arg0) { return list.get(arg0); } @Override public int getCount() { return list.size(); } }
这个demo的核心代码就在这里了,其他几个Fragment的代码跟布局文件我就不贴出来了....有需要的可以去下载我的源码.....又到了10点半了....回家.....
点击下载源码
相关文章:EventBus实现activity跟fragment交互数据
后记:如果你运行之后首页会出现空白的情况,viewpager滑动也会出现问题了,那是MainFragment类初始化viewpager的adpater有问题.
修改后代码如下,大概在MainFragment中125行:
adapter = new MessageGroupFragmentAdapter(getChildFragmentManager(), list);