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