安卓 底部导航栏的封装,支持show or hidden 或者 replace 方式

一直以来都想自己封装一个底部导航栏,随便复习一下,fragment的学习和使用这块。
那我们先来复习一下fragment的基础方法。

fragment的生命周期:
这里写图片描述


  • onAttach(): fragment与Activity进行绑定,可以进行mActivity = activity的操作

  • onCreate():初始化Fragment。可通过参数savedInstanceState获取之前保存的值。

  • onCreateView():初始化Fragment的布局。加载布局和findViewById的操作通常在此函数内完成,但是不建议执行耗时的操作,比如读取数据库数据列表。

  • onActivityCreated():执行该方法时,与Fragment绑定的Activity的onCreate方法已经执行完成并返回,在该方法内可以进行与Activity交互的UI操作,所以在该方法之前Activity的onCreate方法并未执行完成,如果提前进行交互操作,会引发空指针异常。

  • onStart():执行该方法时,Fragment由不可见变为可见状态。

  • onResume():执行该方法时,Fragment处于活动状态,用户可与之交互。

  • onPause():执行该方法时,Fragment处于暂停状态,但依然可见,用户不能与之交互。

  • onStop():执行该方法时,Fragment完全不可见。

  • onDestroyView():销毁与Fragment有关的视图,但未与Activity解除绑定,依然可以通过 onCreateView方法重新创建视图。通常在ViewPager+Fragment的方式下会调用此方法。

  • onDestroy():销毁Fragment。通常按Back键退出或者Fragment被回收时调用此方法。

  • onDetach():解除与Activity的绑定。在onDestroy方法之后调用。


Fragment创建:setUserVisibleHint()->onAttach()->onCreate()->onCreateView()->onActivityCreated()->onStart()->onResume();

退出应用:onPause()->onStop()->onDestroyView()->onDestroy()->onDetach()(注意退出不会调用onSaveInstanceState方法,因为是人为退出,没有必要再保存数据);

以上是fragment的生命周期。


要进行再Activity中进行绑定fragment 与切换,我们还需要学习其他知识点。

Fragment常用API.

Fragment常用的三个类:

android.app.Fragment 主要用于定义Fragment

android.app.FragmentManager Fragment的管理类,主要是来操作Fragment,比如show,hidden,或者 add

android.app.FragmentTransaction 我们要操作fragment,的必须要获取一个事务,然后提交事务, 也就是说要操作fragment,需要提交一个事务操作。很难理解,在实际操作中就懂了。

获取FragmentManage的方式
getFragmentManager() // v4中,getSupportFragmentManager,自带的Fragment 管理类通过getFragmentManager()获得, v4包中getSupportFragmentManager()。在常用开中我们用的是getSupportFragmentManager(),对应的 操作Fragment 也是v4包里面的

主要的操作都是FragmentTransaction的方法
FragmentTransaction transaction = fm.benginTransatcion();//开启一个事务

  • transaction.add()

往Activity中添加一个Fragment

  • transaction.remove()

    从Activity中移除一个Fragment,如果被移除的Fragment没有添加到回退栈(回退栈后面会 详细说),这个Fragment实例将会被销毁。

  • transaction.replace()

    使用另一个Fragment替换当前的,实际上就是remove()然后add()的合体~

  • transaction.hide()

    隐藏当前的Fragment,仅仅是设为不可见,并不会销毁

  • transaction.show()

    显示之前隐藏的Fragment

  • detach()

    会将view从UI中移除,和remove()不同,此时fragment的状态依然由FragmentManager维护。

  • attach()

    重建view视图,附加到UI上并显示。

  • transatcion.commit()//提交一个事务


基本知识储备已经够了,现在我们开始撸代码吧。

效果:
这里写图片描述


我这个demo 实现的功能有:

1.支持 两种切换模式 ,


    //指定fragment 切换模式  替换模式
    public    final  static  int   CHANGE_FRAGMENT_REPLACE_MODE=0;

    //低耗模式  show 或者 hidden 模式
    public    final  static  int   CHANGE_FRAGMENT_REPLACE_SHOW_HIDDN=1;

2.Fragment 第一次切换进入的时候有淡入效果

3.购物车 增加显示数量标记效果,提示购物车有多少个数目,这个提示圆是可以拖动的,贝塞尔效果。


package com.dk.basepack.bseaapplication.weight;

import android.content.Context;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.RadioButton;
import android.widget.RadioGroup;

import com.dk.basepack.bseaapplication.R;
import com.dk.basepack.bseaapplication.mode.FragmentBean;

import java.util.ArrayList;
import java.util.List;

/**
 * Description : 底部导航栏组件
 * Created by Administrator on 2017/9/14.
 */

public class CustomizedBottomNavigationBar extends LinearLayout implements RadioGroup.OnCheckedChangeListener {
    private final  String  tag="CustomizedBottomNavigationBar";
    private Context context;
    private static List<FragmentBean>  fragments;
    private FragmentManager fm;
    private FragmentTransaction transaction;
    private int replace_layout;
    private RadioGroup radioGroup;
    private   boolean  add_Tag=false;

    //第一次可见fragment  标记
    private List<String>  firstVisibles=new ArrayList<>();

    //指定fragment 切换模式  替换模式
    public    final  static  int   CHANGE_FRAGMENT_REPLACE_MODE=0;

    //低耗模式  show 或者 hidden 模式
    public    final  static  int   CHANGE_FRAGMENT_REPLACE_SHOW_HIDDN=1;
    private int mode=-1;
    private boolean isShowFade=true;
    private DragBubbleView bubbleView_tab2;
    public CustomizedBottomNavigationBar(Context context) {
        this(context,null);
    }

    public CustomizedBottomNavigationBar(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

    public CustomizedBottomNavigationBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.context= context;

        //这样写就是把布局贴给了CustomizedBottomNavigationBar
        View view = View.inflate(context, R.layout.customized_bottom_navigation_bar, this);

        findViewById();
    }

    /**
     * 初始化控件
     *
     */
    private void findViewById() {
        radioGroup = (RadioGroup) findViewById(R.id.bottomnavigationbar_radiogroup);
        radioGroup.setOnCheckedChangeListener(this);

        bubbleView_tab2 = (DragBubbleView) findViewById(R.id.dragBubbleView_tab2);
        bubbleView_tab2.setOnBubbleStateListener(bubbleStateListener_tab2);
    }




    DragBubbleView.OnBubbleStateListener  bubbleStateListener_tab2= new DragBubbleView.OnBubbleStateListener() {
        @Override
        public void onDrag() {
//            Log.e("---> ", "拖拽气泡");
        }

        @Override
        public void onMove() {
//            Log.e("---> ", "移动气泡");
        }

        @Override
        public void onRestore() {
//            Log.e("---> ", "气泡恢复原来位置");
        }

        @Override
        public void onDismiss() {
//            Log.e("---> ", "气泡消失");
            bubbleView_tab2.postDelayed(new Runnable() {
                @Override
                public void run() {
                    bubbleView_tab2.reCreate();
                }
            },700);

        }
    };

    /**
     *
     * @param fragments  要跳转的fragment集合
     * @param manager       FragmentManager
     * @param replace_layout         替换布局
     */
    public   void  setChangedFragmentLists(List<FragmentBean> fragments, FragmentManager manager, int replace_layout)
    {
        //开启事物为替换做准备
        this.fm=manager;
        this.replace_layout=replace_layout;

        this.fragments=fragments;

        //设置默认选择
        seChecked(0);
    }

    /**
     *
     * @param fragments  要跳转的fragment集合
     * @param manager       FragmentManager
     */
    public   void  setChangedFragmentLists(List<FragmentBean> fragments, FragmentManager manager)
    {
        setChangedFragmentLists(fragments,manager,0);
    }


    @Override
    public void onCheckedChanged(RadioGroup group, int checkedId) {
        bubbleView_tab2.setIsMoveFalg(true);
        if (mode==-1)
        {
            throw  new NullPointerException("onCheckedChanged"+":"+"切换 模式的缺省值 mode  等于  ---->"+mode+"无效");
        }
        if (fragments==null) return;

        RadioButton radioButton= (RadioButton) group.findViewById(checkedId);
        FragmentBean fragment=getFragmentBean((String) radioButton.getTag());
        Fragment fg =  fragment.getFragment();
        if ( fragment.getFragment()==null)
        {
            throw   new NullPointerException(fragment.getFragmentTag()+":"+"切换 fragment  等于  null ---->"+tag);
        }


        switch (mode)
        {
            case  CHANGE_FRAGMENT_REPLACE_MODE:
                changedReplaceFragmentMode(fg,fragment);
                break;
            case CHANGE_FRAGMENT_REPLACE_SHOW_HIDDN:
                changedFragmentHiddeORShowMode(fg,fragment);
                break;
            default:
                break;
        }
    }


    /**
     * 第一次显示fragment 的时候  显示淡入淡出效果
     * @param fragment
     */
    private void    firstVisibleFragment(FragmentBean fragment)
    {
        if (!isShowFade) return;

        if (!firstVisibles.contains(fragment.getFragmentTag()))
        {
            transaction.setCustomAnimations(android.R.anim.fade_in,android.R.anim.fade_out);
            firstVisibles.add(fragment.getFragmentTag());
        }
    }


    /**
     * 获取当前将要切换fragment信息
     * @param tag
     * @return
     */
    private FragmentBean getFragmentBean(String  tag){
        for (FragmentBean fragmentBean : fragments) {
            if (tag.equals(fragmentBean.getFragmentTag()))
            {
                return fragmentBean;
            }
        }
        return null;
    }


    /**
     * 是否要关闭掉第一次显示fragment的时候的 淡入淡出效果
     * @param isShowFade   默认开启状态
     */
    public  void  firstFragmentShowFade(boolean  isShowFade)
    {
        this.isShowFade=isShowFade;
    }


    /**
     * 选中那个
     * @param index
     */
    public  void  seChecked(int  index)
    {
        ((RadioButton)radioGroup.getChildAt(index)).setChecked(true);
    }


    /**
     * fragment 的切换模式,目前有两种
     * 替换模式  or    show  hidden 模式
     *
     * 必须在  setChangedFragmentLists()  之前调用
     * @param mode
     */
    public   void  setFragmentChangeMode(int  mode)
    {
        this.mode=mode;
    }


    /**________________________________________________________________________________________*/

    /**
     * 切换fragment  替换模式 replace方法替换
     * @param fg
     * @param fragment
     */
    private void   changedReplaceFragmentMode(Fragment fg, FragmentBean fragment)
    {
        transaction = fm.beginTransaction();

        firstVisibleFragment(fragment);

        transaction.replace(replace_layout,fg);
        transaction.commit();

    }

    /**________________________________________________________________________________________*/





    /*************************************************************************************************/

    /**
     *   show   或者 hidden  模式
     * @param fg
     * @param fragment
     */
    private void changedFragmentHiddeORShowMode(Fragment fg, FragmentBean fragment) {

        //每次要创建一个新的事务
        transaction = fm.beginTransaction();

        //将每一个fragment添加到  事务中
        if (!add_Tag)  addFragments();

        //隐藏没有隐藏的fragment
        hideFragments(transaction);

        firstVisibleFragment(fragment);
        //show   fragment
        transaction.show(fg);

        //提交事务
        transaction.commitAllowingStateLoss();
    }


    /**
     * 循环隐藏所有的fragment
     * @param transaction
     */
    private void hideFragments(FragmentTransaction transaction) {
        for (FragmentBean fragmentBean : fragments) {
            if (fragmentBean!=null&&fragmentBean.getFragment()!=null)
            {
                boolean ishidden= fragmentBean.getFragment().isHidden();

                if (!ishidden) transaction.hide(fragmentBean.getFragment());
            }
        }
    }


    /**
     * 添加所有的fragment
     */
    private void addFragments() {
        for (FragmentBean fragmentBean : fragments) {
            if (fragmentBean!=null&&fragmentBean.getFragment()!=null)
            {
                //setRetainInstance(true),这样旋转时 Fragment 不需要重新创建,还是之前的fragment
                fragmentBean.getFragment().setRetainInstance(true);
                transaction.add(replace_layout, fragmentBean.getFragment());
            }
        }

        add_Tag=true;
    }
    /*************************************************************************************************/
}

customized_bottom_navigation_bar.xml

<?xml version="1.0" encoding="utf-8"?>

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:monkey="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="47dip"
    android:clipChildren="false">


    <RadioGroup
        android:id="@+id/bottomnavigationbar.radiogroup"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal">

        <RadioButton
            android:id="@+id/bottomnavigationbar.tab0"
            style="@style/bottomnavigationbar_style"
            android:drawableTop="@drawable/bottomnavigationbar_logo0"
            android:tag="tab0"
            android:text="@string/bottomnavigationbar_title0" />

        <RadioButton
            android:id="@+id/bottomnavigationbar.tab1"
            style="@style/bottomnavigationbar_style"
            android:drawableTop="@drawable/bottomnavigationbar_logo1"
            android:tag="tab1"
            android:text="@string/bottomnavigationbar_title1" />

        <RadioButton
            android:id="@+id/bottomnavigationbar.tab2"
            style="@style/bottomnavigationbar_style"
            android:drawableTop="@drawable/bottomnavigationbar_logo2"
            android:tag="tab2"
            android:text="@string/bottomnavigationbar_title2" />

        <RadioButton
            android:id="@+id/bottomnavigationbar.tab3"
            style="@style/bottomnavigationbar_style"
            android:drawableTop="@drawable/bottomnavigationbar_logo3"
            android:tag="tab3"
            android:text="@string/bottomnavigationbar_title3" />

        <RadioButton
            android:id="@+id/bottomnavigationbar.tab4"
            style="@style/bottomnavigationbar_style"
            android:drawableTop="@drawable/bottomnavigationbar_logo4"
            android:tag="tab4"
            android:text="@string/bottomnavigationbar_title4" />
    </RadioGroup>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clipChildren="false">

        <View
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1" />

        <View
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1" />
        <RelativeLayout
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:clipChildren="false">

            <com.dk.basepack.bseaapplication.weight.DragBubbleView
                android:id="@+id/dragBubbleView_tab2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentRight="true"
                android:layout_marginRight="7dp"
                android:layout_marginTop="1dp"
                monkey:bubbleColor="#ff0000"
                monkey:bubbleRadius="11dp"
                monkey:text="99+"
                monkey:textColor="#ffffff"
                monkey:textSize="10sp" />
        </RelativeLayout>
        <View
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1" />



        <View
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1" />
    </LinearLayout>
</FrameLayout>

a.看到布局可以看到我是用RadioGroup+RadioButton 来导航栏切换的。

b.DragBubbleView 一个具有贝塞尔曲线运动的小点点

Fragment 切换主要逻辑实在RadioGroup 的onCheckedChanged()方法中处理

 @Override
    public void onCheckedChanged(RadioGroup group, int checkedId) {
        bubbleView_tab2.setIsMoveFalg(true);
        if (mode==-1)
        {
            throw  new NullPointerException("onCheckedChanged"+":"+"切换 模式的缺省值 mode  等于  ---->"+mode+"无效");
        }
        if (fragments==null) return;

        RadioButton radioButton= (RadioButton) group.findViewById(checkedId);
        FragmentBean fragment=getFragmentBean((String) radioButton.getTag());
        Fragment fg =  fragment.getFragment();
        if ( fragment.getFragment()==null)
        {
            throw   new NullPointerException(fragment.getFragmentTag()+":"+"切换 fragment  等于  null ---->"+tag);
        }


        switch (mode)
        {
            case  CHANGE_FRAGMENT_REPLACE_MODE:
                changedReplaceFragmentMode(fg,fragment);
                break;
            case CHANGE_FRAGMENT_REPLACE_SHOW_HIDDN:
                changedFragmentHiddeORShowMode(fg,fragment);
                break;
            default:
                break;
        }
    }

changedReplaceFragmentMode(fg,fragment); 和 changedFragmentHiddeORShowMode(fg,fragment); 是两种不同的切换模式

  • changedReplaceFragmentMode(),replace 直接替换原有的fragment,
    transaction.replace(replace_layout,fg); 第一个参数是要替换的布局,第二个参数是 替换的fragment。 有一个地方要注意的是在 替换之前 transaction = fm.beginTransaction(); 都要重新获取一次,执行replace 操作完毕后 执行commit() 。

    /**________________________________________________________________________________________*/

    /**
     * 切换fragment  替换模式 replace方法替换
     * @param fg
     * @param fragment
     */
    private void   changedReplaceFragmentMode(Fragment fg, FragmentBean fragment)
    {
        transaction = fm.beginTransaction();

        firstVisibleFragment(fragment);

        transaction.replace(replace_layout,fg);
        transaction.commit();

    }

    /**________________________________________________________________________________________*/


  • changedFragmentHiddeORShowMode(fg,fragment); 这个方法主要是使用了事务中的,show,hidden 方法,所以不会销毁之前的Fragment,之前用户操作的行为都会保留下来,在这种应用场景使用这种方式是很好的。
 /**
     *   show   或者 hidden  模式
     * @param fg
     * @param fragment
     */
    private void changedFragmentHiddeORShowMode(Fragment fg, FragmentBean fragment) {

        //每次要创建一个新的事务
        transaction = fm.beginTransaction();

        //将每一个fragment添加到  事务中
        if (!add_Tag)  addFragments();

        //隐藏没有隐藏的fragment
        hideFragments(transaction);

        firstVisibleFragment(fragment);
        //show   fragment
        transaction.show(fg);

        //提交事务
        transaction.commitAllowingStateLoss();
    }

把要操作的Fragment 全都add到事务中。


    /**
     * 添加所有的fragment
     */
    private void addFragments() {
        for (FragmentBean fragmentBean : fragments) {
            if (fragmentBean!=null&&fragmentBean.getFragment()!=null)
            {
                //setRetainInstance(true),这样旋转时 Fragment 不需要重新创建,还是之前的fragment
                fragmentBean.getFragment().setRetainInstance(true);
                transaction.add(replace_layout, fragmentBean.getFragment());
            }
        }

        add_Tag=true;
    }
    /*************************************************************************************************/
}

hideFragments(transaction); 隐藏上一个选中的Fragment。

 /**
     * 循环隐藏所有的fragment
     * @param transaction
     */
    private void hideFragments(FragmentTransaction transaction) {
        for (FragmentBean fragmentBean : fragments) {
            if (fragmentBean!=null&&fragmentBean.getFragment()!=null)
            {
                boolean ishidden= fragmentBean.getFragment().isHidden();

                if (!ishidden) transaction.hide(fragmentBean.getFragment());
            }
        }
    }

    /**
     * 第一次显示fragment 的时候  显示淡入淡出效果
     * @param fragment
     */
    private void    firstVisibleFragment(FragmentBean fragment)
    {
        if (!isShowFade) return;

        if (!firstVisibles.contains(fragment.getFragmentTag()))
        {
            transaction.setCustomAnimations(android.R.anim.fade_in,android.R.anim.fade_out);
            firstVisibles.add(fragment.getFragmentTag());
        }
    }

最后就是显示选中的Fragment

    //show   fragment
        transaction.show(fg);

最后看如何在Activity中使用

package com.dk.basepack.bseaapplication;

import android.os.Bundle;
import android.support.v4.app.FragmentActivity;

import com.dk.basepack.bseaapplication.fragment.HomePageFragment;
import com.dk.basepack.bseaapplication.mode.FragmentBean;
import com.dk.basepack.bseaapplication.weight.CustomizedBottomNavigationBar;

import java.util.ArrayList;
import java.util.List;


public class MainActivity extends FragmentActivity {

    private List<FragmentBean> fragments;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initFragments();

        CustomizedBottomNavigationBar navigationBar= (CustomizedBottomNavigationBar) findViewById(R.id.navigation_bar);
        //确定那种Fragment的切换模式
        navigationBar.setFragmentChangeMode(CustomizedBottomNavigationBar.CHANGE_FRAGMENT_REPLACE_MODE);
        //设置操作Fragment 数据集合
        navigationBar.setChangedFragmentLists(fragments,getSupportFragmentManager(),R.id.fragment_container);

    }

    private void initFragments() {//初始化操作的Fragment  
        fragments = new ArrayList<>();
        fragments.add(new FragmentBean(new HomePageFragment(),"tab0"));
        fragments.add(new FragmentBean(new  ClassifyFragment(),"tab1"));
        fragments.add(new FragmentBean(new  ShoppingCartFragment(),"tab2"));
        fragments.add(new FragmentBean(new  InformationFragment(),"tab3"));
        fragments.add(new FragmentBean(new  PersonalCenterFragment(),"tab4"));
    }
}

项目: https://github.com/Followk/bseaapplication


参考大神:
http://blog.csdn.net/lmj623565791/article/details/37970961/

http://blog.csdn.net/qq_31715429/article/details/54386934

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值