您的位置:首页 > 其它

快速实现Fragment切换功能

2016-11-05 16:47 274 查看

1. 前言

一个app首页通常是使用
Activity + Fragment
的形式展示,控制Fragment的显示和隐藏基本有两种实现方法

ViewPager , 比如微信 , 优势是手势操作更加方便,官方提供了FragmentPagerAdapter可以很方便帮助我们实现数据加载(Fragment要使用懒加载的方式,避免浪费资源),劣势就是当你的第一个Fragment中已经使用了ViewPager,两层套一起事件会冲突,而且操作也不友好啦。

FragmentManager , 比如头条,针对使用ViewPager组合Fragment的问题,使用FragmentManager控制Fragment的显示和隐藏,不需要考虑懒加载的问题,不过不能支持滑动啦。

既然是经常使用的功能,自然要分离出来,避免下次做重复的工作,本文主要介绍这个功能的实现。

2. 最初的方案

刚开始的方案是定义了一个Activity的基类MultiFragmentActivity,在基类中实现切换Fragment的逻辑,想要实现该功能的Activity继承基类就可以实现切换功能。MultiFragmentActivity.java源代码,这种实现方式使用起来要更加简单,但是有个问题,整个项目通常会实现同一个基类来完成类似或者相同的功能,当然我们可以将MultiFragmentActivity继承自项目的基类,然后再继承MultiFragmentActivity,但是我的想法是能把这个功能放到Library中,方便以后使用,那就面临一个问题就是不能采用继承的方式。

少用继承,多用组合,继承造成的问题就是耦合性太高,不方便分离功能,所以决定使用组合的方法重新设计,定义帮助类FragmentHelper,Activity可以持有FragmentHelper来完成Fragment的切换,具体的切换逻辑由FragmentHelper来处理。

3. 定义接口

首先定义FragmentOperator接口,借助该接口FragmentHelper连接持有它的Activity

interface FragmentOperator {
/**
* 获取放置fragment的控件id
*
* @return id
*/
int getFragmentContainerId();

/**
* 构建fragment
*
* @param showItem 将要展示的fragment pos
* @return fragment
*/
Fragment makeFragment(int showItem);

/**
* 进行转换之前做操作,动画之类的
*
* @return FragmentTransaction
*/
void beginTransaction(FragmentTransaction transaction);

/**
* 同步选中之后的显示状态
*
* @param selectImage 被选中的item
*/
void syncSelectState(int selectImage);

/**
* 当点击显示同一个
*
* @param showItem 显示的item
* @return 返回false表示忽略此次点击的切换
*/
boolean whenShowSameFragment(int showItem);

/**
* 当点击显示的不是同一个
*
* @param showItem 显示的item
* @return 返回false表示忽略此次点击的切换
*/
boolean whenShowNotSameFragment(int showItem);
}

// 一个简单封装
public static abstract class SimpleFragmentOperator implements FragmentOperator {
@Override
public boolean whenShowNotSameFragment(int showItem) {
return true;
}

@Override
public boolean whenShowSameFragment(int showItem) {
return false;
}

@Override
public void syncSelectState(int selectImage) {

}

@Override
public void beginTransaction(FragmentTransaction transaction) {

}
}


4. 核心方法

使用tag作为标记添加fragment,避免重复创建

private static final String FRAGMENT_ATG = "FragmentHelper";
private static final String ITEM_HIDE = "mHideItem";
private static final String ITEM_SHOW = "mShowItem";

private FragmentOperator operator;
private Fragment mCurrentFragment;
private FragmentManager mFragmentManager;
private int mShowItem, mHideItem;
private int mExactlyItem = 0;

/**
* 隐藏当前显示的fragment,显示将要显示的fragment
*
* @param hideItem   需要隐藏的fragment
* @param showItem   需要显示的fragment
* @param isOnCreate 是否是第一次从OnCreate中启动,点击都是false
*/
private void performSelectItem(int hideItem, int showItem, boolean isOnCreate) {
// 获得将要显示页的tag
String currentTag = getFragmentTag(hideItem);
// 隐藏当前的的fragment
FragmentTransaction transaction = mFragmentManager.beginTransaction();
operator.beginTransaction(transaction);

// 第一次创建,一个都没有,不需要隐藏,直接显示
if (mFragmentManager.getFragments() == null) {
mShowItem = showItem;
mExactlyItem = showItem;
mCurrentFragment = operator.makeFragment(showItem);
transaction.add(operator.getFragmentContainerId(), mCurrentFragment, getFragmentTag(showItem))
.show(mCurrentFragment);
} else {
// 优化,如果被杀后再进来,全部的fragment都会被呈现显示状态,所以都隐藏一遍
if (isOnCreate && mFragmentManager.getFragments() != null) {
for (Fragment fragment : mFragmentManager.getFragments()) {
transaction.hide(fragment);
}
} else {
// 正常按钮点击进入,隐藏上一个即可
Fragment lastFragment = mFragmentManager.findFragmentByTag(currentTag);
if (lastFragment != null) {
transaction.hide(lastFragment);
}
}

// 获得将要显示页的tag
String toTag = getFragmentTag(showItem);
// find要显示的Fragment
mCurrentFragment = mFragmentManager.findFragmentByTag(toTag);
if (mCurrentFragment != null) {
// 已经存在则显示
transaction.show(mCurrentFragment);
} else {
// 不存在则添加新的fragment
mCurrentFragment = operator.makeFragment(showItem);
if (mCurrentFragment != null) {
transaction.add(operator.getFragmentContainerId(), mCurrentFragment, toTag);
}
}
}
// 同步状态
operator.syncSelectState(showItem);
// 保存当前显示fragment的item
mHideItem = hideItem;
mShowItem = showItem;
transaction.commitAllowingStateLoss();
}


5. 在Activty显示某个Fragment

在Activity中使用时只需要调用showFragment方法即可

/**
* 显示某个fragment
*
* @param showItem 显示的item
*/
public void showFragment(int showItem) {
showFragment(showItem, false);
}

/**
* 选中某一个fragment
*
* @param showItem   显示的item
* @param isOnCreate 是否是第一次创
*/
private void showFragment(int showItem, boolean isOnCreate) {
if (showItem == mShowItem) {
if (operator.whenShowSameFragment(showItem)) {
performSelectItem(mExactlyItem, showItem, isOnCreate);
mExactlyItem = showItem;
}
} else {
performSelectItem(mExactlyItem, showItem, isOnCreate);
mExactlyItem = showItem;
}
}


6. 优化

当Activity被回收时,记录上次的状态

public void restoreFragmentHelper(Bundle save) {
if (save != null) {
mHideItem = save.getInt(ITEM_HIDE, 0);
mShowItem = save.getInt(ITEM_SHOW, 0);
}
performSelectItem(mHideItem, mShowItem, true);
}

public void onSaveInstanceState(Bundle outState) {
outState.putInt(ITEM_HIDE, mHideItem);
outState.putInt(ITEM_SHOW, mShowItem);
}


7. 使用

public class HomePageActivity extends BaseReaperActivity {

private FragmentHelper fragmentHelper;

private FragmentHelper.SimpleFragmentOperator operator = new FragmentHelper.SimpleFragmentOperator() {
@Override
public int getFragmentContainerId() {
return R.id.home_container;
}

@Override
public Fragment makeFragment(int showItem) {
Fragment fragment = null;
switch (showItem) {
case 0:
fragment = HomeVideoFunFragment.newInst();
break;
case 1:
fragment = HomeFunnyFragment.newInst();
break;
case 2:
fragment = HomeBeautyFragment.newInst();
break;
case 3:
fragment = HomeMineFragment.newInst();
break;
}
return fragment;
}

@Override
public void beginTransaction(FragmentTransaction transaction) {
super.beginTransaction(transaction);
Logger.e("beginTransaction");
}

@Override
public void syncSelectState(int selectImage) {
for (int i = 0; i < mBotTabsTv.size(); i++) {
mBotTabsTv.get(i).setSelected(selectImage == i);
}
}

@Override
public boolean whenShowNotSameFragment(int showItem) {
JCVideoPlayer.releaseAllVideos();
return super.whenShowNotSameFragment(showItem);
}
};

@Override
public void onInitViews(View view, Bundle saveData) {
super.onInitViews(view, saveData);
fragmentHelper = new FragmentHelper(getSupportFragmentManager(), operator);
fragmentHelper.showFragment(2);
}

@OnClick({R.id.home_recommend, R.id.home_album, R.id.home_search, R.id.home_mine})
public void click(View v) {
int tag = Integer.parseInt(v.getTag().toString());
fragmentHelper.showFragment(tag);
}

@Override
public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
super.onSaveInstanceState(outState, outPersistentState);
fragmentHelper.onSaveInstanceState(outState);
}

@Override
public void onInitViews(View view, Bundle saveData) {
super.onInitViews(view, saveData);
fragmentHelper = new FragmentHelper(getSupportFragmentManager(), operator);
fragmentHelper.showFragment(2);
}
}


8. 源代码

/**
* Project  : CommonLib
* Package  : com.march.lib.core.common
* CreateAt : 2016/11/5
* Describe : 实现Fragment切换
*
* @author chendong
*/
public class FragmentHelper {

interface FragmentOperator {
/**
* 获取放置fragment的控件id
*
* @return id
*/
int getFragmentContainerId();

/**
* 构建fragment
*
* @param showItem 将要展示的fragment pos
* @return fragment
*/
Fragment makeFragment(int showItem);

/**
* 进行转换之前做操作,动画之类的
*
* @return FragmentTransaction
*/
void beginTransaction(FragmentTransaction transaction);

/**
* 同步选中之后的显示状态
*
* @param selectImage 被选中的item
*/
void syncSelectState(int selectImage);

/**
* 当点击显示同一个
*
* @param showItem 显示的item
* @return 返回false表示忽略此次点击的切换
*/
boolean whenShowSameFragment(int showItem);

/**
* 当点击显示的不是同一个
*
* @param showItem 显示的item
* @return 返回false表示忽略此次点击的切换
*/
boolean whenShowNotSameFragment(int showItem);
}

public static abstract class SimpleFragmentOperator implements FragmentOperator {
@Override
public boolean whenShowNotSameFragment(int showItem) {
return true;
}

@Override
public boolean whenShowSameFragment(int showItem) {
return false;
}

@Override
public void syncSelectState(int selectImage) {

}

@Override
public void beginTransaction(FragmentTransaction transaction) {

}
}

private static final String FRAGMENT_ATG = "FragmentHelper";
private static final String ITEM_HIDE = "mHideItem";
private static final String ITEM_SHOW = "mShowItem";

private FragmentOperator operator;
private Fragment mCurrentFragment;
private FragmentManager mFragmentManager;
private int mShowItem, mHideItem;
private int mExactlyItem = 0;

public void restoreFragmentHelper(Bundle save) {
if (save != null) {
mHideItem = save.getInt(ITEM_HIDE, 0);
mShowItem = save.getInt(ITEM_SHOW, 0);
}
performSelectItem(mHideItem, mShowItem, true);
}

public void onSaveInstanceState(Bundle outState) {
outState.putInt(ITEM_HIDE, mHideItem);
outState.putInt(ITEM_SHOW, mShowItem);
}

public FragmentHelper(FragmentManager mFragmentManager, FragmentOperator operator) {
this.mFragmentManager = mFragmentManager;
this.mFragmentManager = mFragmentManager;
this.operator = operator;
}

/**
* 显示某个fragment
*
* @param showItem 显示的item
*/
public void showFragment(int showItem) {
showFragment(showItem, false);
}

/**
* 选中某一个fragment,处理重复点击
*
* @param showItem   显示的item
* @param isOnCreate 是否是第一次创
*/
private void showFragment(int showItem, boolean isOnCreate) {
if (showItem == mShowItem) {
if (operator.whenShowSameFragment(showItem)) {
performSelectItem(mExactlyItem, showItem, isOnCreate);
mExactlyItem = showItem;
}
} else {
performSelectItem(mExactlyItem, showItem, isOnCreate);
mExactlyItem = showItem;
}
}

/**
* 获取当前处于活动状态的fragment'
*
* @return fragment
*/
public Fragment getCurrentFragment() {
return mCurrentFragment;
}

/**
* 隐藏当前显示的fragment,显示将要显示的fragment
*
* @param hideItem   需要隐藏的fragment
* @param showItem   需要显示的fragment
* @param isOnCreate 是否是第一次从OnCreate中启动,点击都是false
*/
private void performSelectItem(int hideItem, int showItem, boolean isOnCreate) {
// 获得将要显示页的tag
String currentTag = getFragmentTag(hideItem);
// 隐藏当前的的fragment
FragmentTransaction transaction = mFragmentManager.beginTransaction();
operator.beginTransaction(transaction);

// 第一次创建,一个都没有,不需要隐藏,直接显示
if (mFragmentManager.getFragments() == null) {
mShowItem = showItem;
mExactlyItem = showItem;
mCurrentFragment = operator.makeFragment(showItem);
transaction.add(operator.getFragmentContainerId(), mCurrentFragment, getFragmentTag(showItem))
.show(mCurrentFragment);
} else {
// 优化,如果被杀后再进来,全部的fragment都会被呈现显示状态,所以都隐藏一遍
if (isOnCreate && mFragmentManager.getFragments() != null) {
for (Fragment fragment : mFragmentManager.getFragments()) {
transaction.hide(fragment);
}
} else {
// 正常按钮点击进入,隐藏上一个即可
Fragment lastFragment = mFragmentManager.findFragmentByTag(currentTag);
if (lastFragment != null) {
transaction.hide(lastFragment);
}
}

// 获得将要显示页的tag
String toTag = getFragmentTag(showItem);
// find要显示的Fragment
mCurrentFragment = mFragmentManager.findFragmentByTag(toTag);
if (mCurrentFragment != null) {
// 已经存在则显示
transaction.show(mCurrentFragment);
} else {
// 不存在则添加新的fragment
mCurrentFragment = operator.makeFragment(showItem);
if (mCurrentFragment != null) {
transaction.add(operator.getFragmentContainerId(), mCurrentFragment, toTag);
}
}
}
// 同步状态
operator.syncSelectState(showItem);
// 保存当前显示fragment的item
mHideItem = hideItem;
mShowItem = showItem;
transaction.commitAllowingStateLoss();
}

private String getFragmentTag(int item) {
return FRAGMENT_ATG + item;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  fragment 切换
相关文章推荐