安卓 底部导航栏的封装,支持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
- 安卓 底部导航栏的封装,支持show or hidden 或者 replace 方式
- 安卓底部导航栏
- 底部导航栏封装
- 安卓底部导航
- 封装通用的底部导航栏
- 安卓精美底部导航栏的实现TabHost
- 探索安卓中的底部导航栏
- 安卓UI-底部导航栏
- 底部导航栏的实现方式
- observe keyboard show or hidden
- 安卓商城学习笔记--01-底部导航栏
- 安卓底部导航栏点击变色切换不同Fragment
- 安卓底部导航栏菜单弹出属性动画
- 安卓 AppCompat 隐藏标题栏 保留底部导航栏
- 安卓 BottomNavigationBar 底部导航栏 最简单用法详解
- 安卓UI--底部导航栏--TabHost、TabActivity
- 安卓中用show()和hide()方法来代替转跳fragment的replace()方法
- H5应用 安卓输入法弹出撑开底部导航栏的问题解决
- 刷题——POJ 2395 Out of Hay
- CDH元数据的主从备份
- mysql查询多个字段同时满足多个条件,取并集
- DataBinding基本使用(2)
- PHP 使用SOAP调用接口开发,偶尔会报错
- 安卓 底部导航栏的封装,支持show or hidden 或者 replace 方式
- Android开发入门基础
- 分布式工程中,各工程的配置问题
- 关于安卓setBackgroundColor中Drawable复用导致背景重复改变
- 在 webpack 中使用 ECharts
- 自定义虚线格式的EditText输入框
- Leetcode算法学习日志-680 valid Palindrome II
- scrapy 模拟登录
- BZOJ 2138: stone Hall定理 线段树