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

来源:互联网 发布:淘宝晚礼服推荐 编辑:程序博客网 时间:2024/05/16 04:21

一直以来都想自己封装一个底部导航栏,随便复习一下,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