android底部导航框架可以说是目前最常用的框架,看似非常简单的框架如果搭建不好后期会出现各种意想不到问题;
比如我前面两篇写的每次点击底部图标fragment之间会来回替换,这样肯定不实用;这个demo使用add(),hide(),show()代替replace()
再者当打开app之后按home键退出,浏览其他app当内存重启的时候再次打开我们的app会出现四个fragment重现的问题;
经过我们伟大的测试不断的努力下,进入首页立马退出,然后再进入...如此反复操作会出现getActivity()报空指针;
想要更深入的了解fragment推荐给大家一篇文章:http://www.jianshu.com/p/d9143a92ad94
如果大家手里没有成熟的框架,可以直接到文章底部下载源码,可以直接使用;
整体的大布局是一个线性布局,按照权重8:1分配fragment的父布局和下面的底部导航栏
<LinearLayout 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"
android:orientation="vertical"
tools:context="www.relative.com.relativeframe.MainActivity">
<!--存放fragment-->
<FrameLayout
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_above="@+id/tab_layout"
android:layout_weight="8"></FrameLayout>
<!--底部导航-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:gravity="bottom"
android:layout_weight="1">
<include
android:id="@+id/tab_layout"
layout="@layout/view_main_tab" />
</LinearLayout>
</LinearLayout>
底部导航也是一个线性布局,我们重点看下“进货单”,其他三个也一样,只不过没有显示消息的TextView
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="56dp"
android:layout_alignParentBottom="true"
android:orientation="horizontal"
android:background="#27282c">
<!--首页-->
<RelativeLayout/>
<!--商品-->
<RelativeLayout />
<!--进货单-->
<RelativeLayout
android:id="@+id/shopping_cart_page"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginTop="4dp"
android:layout_weight="1">
<RadioButton
android:id="@+id/shopping_cart_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:background="@null"
android:button="@null"
android:clickable="false"
android:drawablePadding="5dp"
android:drawableTop="@drawable/selector_tab_stock_list"
android:gravity="center"
android:text="进货单"
android:textColor="@drawable/tab_text_selector"
android:textSize="10sp" />
<TextView
android:id="@+id/shopping_cart_num"
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_alignRight="@id/shopping_cart_button"
android:layout_alignTop="@id/shopping_cart_button"
android:layout_marginTop="-6dp"
android:layout_gravity="right"
android:background="@drawable/msg_num_shape"
android:clickable="false"
android:gravity="center"
android:text="99+"
android:textColor="@color/white"
android:textSize="8sp" />
</RelativeLayout>
<!--会员-->
<RelativeLayout />
</LinearLayout>
布局写法很多,但是万变不离其宗,几乎都差不多;
其实逻辑也比较简单:
第一:初始化view和fragment(将fragment保存到一个集合)
第二:显示选中的Fragment
第三:处理RadioButton的点击事件,当点击的时候显示对应的Fragment并且改变RadioButton的状态
第四:重写onSaveInstanceState()方法,注销super...
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fragmentManager = getSupportFragmentManager();
initView();
// 显示选中的 Fragment
showFragment();
}
//初始化view和fragment
private void initView() {
fragmentList.add(new HomeFragment());
fragmentList.add(new GoodsFragment());
fragmentList.add(new ShoppingFragment());
fragmentList.add(new UserFragment());
...
}
@Override
//RadioButton点击事件
public void onClick(View v) {
switch (v.getId()) {
case R.id.homepage://首页
currentIndex = 0;
changeState(true, false, false, false);
break;
case R.id.goods_page://商品
currentIndex = 1;
changeState(false, true, false, false);
break;
case R.id.shopping_cart_page://进货单
currentIndex = 2;
changeState(false, false, true, false);
break;
case R.id.user_page://会员
currentIndex = 3;
changeState(false, false, false, true);
break;
}
showFragment();
}
//改变RadioButton的状态
private void changeState(boolean homeB, boolean goodsB, boolean shopB, boolean userB) {
btnHome.setChecked(homeB);
btnGoods.setChecked(goodsB);
btnShopCart.setChecked(shopB);
btnUserVip.setChecked(userB);
}
// 显示选中的 Fragment
private void showFragment() {
Fragment currentFragment = fragmentList.get(currentIndex);
FragmentTransaction ft = fragmentManager.beginTransaction();
//如果当前的fragment没有添加,添加当前的fragment
if(!currentFragment.isAdded()){
/**
* 把fragment添加到MainActivity中
* 第一个参数:fragment添加的位置;(必须是布局容器,一般是一个framelayout)
* 第二个参数:要添加的fragment对象
*/
ft.add(R.id.fragment_container, currentFragment);
}
for(int x=0;x<fragmentList.size();x++){
if(fragmentList.get(x)!=currentFragment){
//隐藏不需要显示的Fragment,仅仅是设为不可见,并不会销毁
ft.hide(fragmentList.get(x));
}else {
ft.show(fragmentList.get(x));
}
}
ft.commit();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
//super.onSaveInstanceState(outState);
}
最后注销的super.onSaveInstanceState()方法是系统的方法,在内存重启的时候会调用,作用就是在内存重启的时候不让系统做任何操作,这样就不会出现重叠现象;
解决getActivity()空指针,需要创建一个Fragment的基类(BaseFragment),然后在BaseFragment中:
public class BaseFragment extends Fragment {
private Activity mActivity;
@Override
/**
* 当Fragment与Activity发生关联时调用
*
* 在Fragment基类里设置一个Activity mActivity的全局变量,在onAttach(Activity activity)里赋值,
* 使用mActivity代替getActivity(),保证Fragment即使在onDetach后,仍持有Activity的引用
*/
public void onAttach(Context context) {
super.onAttach(context);
this.mActivity = (Activity) context;
}
}