YoKey大神的Fragment库Fragmentation,主要用于现在App经常需要实现的单Activity+多Fragment以及多Activity+多Fragment的形式架构。同时最最重要的是,它帮助我们封装了很多好用的方法,解决了一些官方Fragment库中存在的一些Bug。
我在学习做一款有关Ble蓝牙防丢器的App时想要尝试以单Activity+多Fragment的架构去实现,恰好可以使用这个库,也就皮毛的研究了一下。之前我是通过ViewPager去管理多个Fragment的进出,后来还是抛弃这种方式,因为确实不太合理。所以,用了Fragmentation这个库,还是非常不错的。
添加依赖
compile 'me.yokeyword:fragmentation:最新版'
我的是
compile 'me.yokeyword:fragmentation:0.10.3'
一些API的使用(对照流式交互Demo)
这里先贴出单Activity的代码,代码内我添加了一些注释,基本可以了解清楚MainActivity 中做了哪些事情。这里可以列出比较重要的Api:
装载根Fragment,一般在saveInstanceState==null时load;
loadRootFragment(R.id.fl_container, HomeFragment.newInstance()); //activity初始加载HomeFragment
在Activity中注册所有Fragment生命周期的回调函数,可以监听该Activity下的所有Fragment的18个 生命周期方法,这里监听的是Fragment的创建:
registerFragmentLifecycleCallbacks(new FragmentLifecycleCallbacks() {
// 当有Fragment Create时回调,打印log
@Override
public void onFragmentCreated(SupportFragment fragment, Bundle savedInstanceState) {
Log.i("MainActivity", "onFragmentCreated--->" + fragment.getClass().getSimpleName());
}
});
启动跳转Fragment,
// 启动新的Fragment,启动者和被启动者是在同一个栈的
start(SupportFragment fragment)
// 以某种启动模式,启动新的Fragment
start(SupportFragment fragment, int launchMode)
// 启动新的Fragment,并能接收到新Fragment的数据返回
startForResult(SupportFragment fragment,int requestCode)
// 启动目标Fragment,并关闭当前Fragment;不要尝试pop()+start(),动画会有问题
startWithPop(SupportFragment fragment)
出栈,移出某个Fragment
// 当前Fragment出栈(在当前Fragment所在栈内pop)
pop();
// 出栈某一个Fragment栈内之上的所有Fragment
popTo(Class fragmentClass/String tag, boolean includeSelf);
// 出栈某一个Fragment栈内之上的所有Fragment。如果想出栈后,紧接着.beginTransaction()开始一个新事务,
//请使用下面的方法, 防止多事务连续执行的异常
popTo(Class fragmentClass, boolean includeSelf, Runnable afterTransaction);
查找获取某一Fragment
// 获取所在栈内的栈顶Fragment
getTopFragment();
// 获取当前Fragment所在栈内的前一个Fragment
getPreFragment();
// 获取所在栈内的某个Fragment,可以是xxxFragment.Class,也可以是tag
findFragment(Class fragmentClass/String tag);
防止动画卡顿,可以先让其加载完Fragment的转场动画,然后继续实现一些繁琐的业务逻辑。在Fragment中重写onEnterAnimationEnd(Bundle saveInstanceState)方法,在这个方法里继续执行一些繁琐操作。通常可以采取这样的一种模式:
public View onCreateView(...) {
...
// 这里仅给一些findViewById等轻量UI的操作
initView();
return view;
}
@Override
protected void onEnterAnimationEnd(Bundle saveInstanceState) {
// 这里设置Listener、各种Adapter、请求数据等等
initLazyView();
}
Fragment实例调用startForResult(SupportFragment fragment,int requestCode)用来Fragment之间返回数据,类似于Activity的startActivityForResult()。大致用法如下;
public class DetailFragment extends SupportFragment{
private void goDetail(){
// 启动ModifyDetailFragment
startForResult(ModifyDetailFragment.newInstance(mTitle), REQ_CODE);
}
// ModifyDetailFragment调用setFragmentResult()后,在其出栈时,会回调该方法
@Override
public void onFragmentResult(int requestCode, int resultCode, Bundle data) {
super.onFragmentResult(requestCode, resultCode, data);
if (requestCode == REQ_CODE && resultCode == RESULT_OK ) {
// 在此通过Bundle data 获取返回的数据
}
}
}
public class ModifyTitleFragment extends SupportFragment{
// 设置传给上个Fragment的bundle数据
private void setResult(){
Bundle bundle = new Bundle();
bundle.putString("title", "xxxx");
setFramgentResult(RESULT_OK, bundle);
}
}
以某种启动模式启动Fragment:start(SupportFragment fragment, int launchMode)。下面是以SingleTask模式重新启动一个已存在的Fragment的标准代码: 比如:HomeFragment->B Fragment->C Fragment,C Fragment以SingleTask模式重新启动HomeFragment。在被以SingleTask模式启动的Fragment中重写onNewBundle()方法,可以接收到SINGLETASK/SINGTOP启动模式传递的数据。类似于Activity中的onNewIntent()。
// 任意同栈内的Fragment中:
HomeFragment fragment = findFragment(HomeFragment.class);
Bundle newBundle = new Bundle();
newBundle.putString("from", "主页-->来自:" + topFragment.getClass().getSimpleName());
fragment.putNewBundle(newBundle);
// 在栈内的HomeFragment以SingleTask模式启动(即在其之上的Fragment会出栈)
start(fragment, SupportFragment.SINGLETASK);
public class HomeFragment extends SupportFragment{
@Override
protected void onNewBundle(Bundle newBundle){
// 在此可以接收到SINGLETASK/SINGTOP启动模式传递的数据 类似Activity中的onNewIntent()
Toast.makeText(_mActivity, args.getString("from"), Toast.LENGTH_SHORT).show();
}
}
附:MainActivity .java
/**
* 流程式demo tip: 多使用右上角的"查看栈视图"
* Created by YoKeyword on 16/1/29.
*/
public class MainActivity extends SupportActivity
implements NavigationView.OnNavigationItemSelectedListener, BaseMainFragment.OnFragmentOpenDrawerListener
, LoginFragment.OnLoginSuccessListener, SwipeBackSampleFragment.OnLockDrawLayoutListener {
public static final String TAG = MainActivity.class.getSimpleName();
// 再点一次退出程序时间设置
private static final long WAIT_TIME = 2000L;
private long TOUCH_TIME = 0; //点击返回键时间
private DrawerLayout mDrawer;
private NavigationView mNavigationView;
private TextView mTvName; // NavigationView上的名字
private ImageView mImgNav; // NavigationView上的头像
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
loadRootFragment(R.id.fl_container, HomeFragment.newInstance()); //activity初始加载HomeFragment
}
initView();
//在Activity中注册所有Fragment生命周期的回调函数,可以监听该Activity下的所有Fragment的18个 生命周期方法
registerFragmentLifecycleCallbacks(new FragmentLifecycleCallbacks() {
// 当有Fragment Create时回调,打印log
@Override
public void onFragmentCreated(SupportFragment fragment, Bundle savedInstanceState) {
Log.i("MainActivity", "onFragmentCreated--->" + fragment.getClass().getSimpleName());
}
});
}
//设置所有Fragment的转场动画
@Override
public FragmentAnimator onCreateFragmentAnimator() {
// 设置默认Fragment动画 默认竖向(和安卓5.0以上的动画相同)
return super.onCreateFragmentAnimator();
// 设置横向(和安卓4.x动画相同)
// return new DefaultHorizontalAnimator();
// 设置自定义动画
// return new FragmentAnimator(enter,exit,popEnter,popExit);
}
private void initView() {
mDrawer = (DrawerLayout) findViewById(R.id.drawer_layout);
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
this, mDrawer, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
// mDrawer.setDrawerListener(toggle);
toggle.syncState();
mNavigationView = (NavigationView) findViewById(R.id.nav_view);
mNavigationView.setNavigationItemSelectedListener(this); //设置NavigationItem的点击事件
mNavigationView.setCheckedItem(R.id.nav_home); //默认初始设置首页被选中
//绑定NavigationView的headview里的控件
//设置点击事件登录(延时250ms跳转LoginFragment)
LinearLayout llNavHeader = (LinearLayout) mNavigationView.getHeaderView(0);
mTvName = (TextView) llNavHeader.findViewById(R.id.tv_name);
mImgNav = (ImageView) llNavHeader.findViewById(R.id.img_nav);
llNavHeader.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mDrawer.closeDrawer(GravityCompat.START);
mDrawer.postDelayed(new Runnable() {
@Override
public void run() {
goLogin();
}
}, 250);
}
});
}
//设置手机返回键事件
//如果侧边栏打开则关闭,否则: 如果栈顶的Fragment是BaseMainFragment的实例,那么先设置mNavigationView的nav_home被选中
// 同时如果栈中不止一个Fragment,就出栈一个,否则提示是否要再按一次退出。在WAIT_TIME时间内再按则退出。
@Override
public void onBackPressedSupport() {
if (mDrawer.isDrawerOpen(GravityCompat.START)) {
mDrawer.closeDrawer(GravityCompat.START);
} else {
Fragment topFragment = getTopFragment();
// 主页的Fragment
if (topFragment instanceof BaseMainFragment) {
mNavigationView.setCheckedItem(R.id.nav_home);
}
if (getSupportFragmentManager().getBackStackEntryCount() > 1) {
pop();
} else {
if (System.currentTimeMillis() - TOUCH_TIME < WAIT_TIME) {
finish();
} else {
TOUCH_TIME = System.currentTimeMillis();
Toast.makeText(this, R.string.press_again_exit, Toast.LENGTH_SHORT).show();
}
}
}
}
/**
* 打开抽屉
*/
@Override
public void onOpenDrawer() {
if (!mDrawer.isDrawerOpen(GravityCompat.START)) {
mDrawer.openDrawer(GravityCompat.START);
}
}
@Override
public boolean onNavigationItemSelected(final MenuItem item) {
mDrawer.closeDrawer(GravityCompat.START);
mDrawer.postDelayed(new Runnable() {
@Override
public void run() {
int id = item.getItemId();
//获取栈顶的Fragment
final SupportFragment topFragment = getTopFragment();
if (id == R.id.nav_home) {
HomeFragment fragment = findFragment(HomeFragment.class); //根据Fragment类名查找对应的Fragment
//fragment再次启动时,fragment类中通过重写onNewBundle方法取出数据
Bundle newBundle = new Bundle();
newBundle.putString("from", "主页-->来自:" + topFragment.getClass().getSimpleName());
fragment.putNewBundle(newBundle);
start(fragment, SupportFragment.SINGLETASK); //跳转HomeFragment
} else if (id == R.id.nav_discover) {
DiscoverFragment fragment = findFragment(DiscoverFragment.class);
if (fragment == null) {
//出栈某一个Fragment之上的所有Fragment,并执行一个新事务
//这里是将所有HomeFragment之上的Fragment出栈,并立即start DiscoverFragment
popTo(HomeFragment.class, false, new Runnable() {
@Override
public void run() {
start(DiscoverFragment.newInstance());
}
});
} else {
// 如果已经在栈内,则以SingleTask模式start
start(fragment, SupportFragment.SINGLETASK);
}
} else if (id == R.id.nav_msg) {
ShopFragment fragment = findFragment(ShopFragment.class);
if (fragment == null) {
popTo(HomeFragment.class, false, new Runnable() {
@Override
public void run() {
start(ShopFragment.newInstance());
}
});
} else {
// 如果已经在栈内,则以SingleTask模式start,也可以用popTo
// start(fragment, SupportFragment.SINGLETASK);
popTo(ShopFragment.class, false);
}
} else if (id == R.id.nav_login) {
goLogin();
} else if (id == R.id.nav_swipe_back) {
startActivity(new Intent(MainActivity.this, SwipeBackSampleActivity.class));
} else if (id == R.id.nav_swipe_back_f) {
start(SwipeBackSampleFragment.newInstance());
}
}
}, 250);
return true;
}
private void goLogin() {
//启动目标Fragment
start(LoginFragment.newInstance());
}
@Override
public void onLoginSuccess(String account) {
mTvName.setText(account);
mImgNav.setImageResource(R.drawable.ic_account_circle_white_48dp);
Toast.makeText(this, "登录成功,NavigationView的用户名已经更改!", Toast.LENGTH_SHORT).show();
}
@Override
public void onLockDrawLayout(boolean lock) {
if (lock) {
mDrawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
} else {
mDrawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
}
}
}
HomeFragment.java
package me.yokeyword.sample.demo_flow.ui.fragment.home;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v4.view.GravityCompat;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.PopupMenu;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
import me.yokeyword.fragmentation.anim.DefaultHorizontalAnimator;
import me.yokeyword.fragmentation.anim.DefaultNoAnimator;
import me.yokeyword.fragmentation.anim.DefaultVerticalAnimator;
import me.yokeyword.fragmentation.anim.FragmentAnimator;
import me.yokeyword.sample.R;
import me.yokeyword.sample.demo_flow.adapter.HomeAdapter;
import me.yokeyword.sample.demo_flow.listener.OnItemClickListener;
import me.yokeyword.sample.demo_flow.entity.Article;
import me.yokeyword.sample.demo_flow.base.BaseMainFragment;
public class HomeFragment extends BaseMainFragment implements Toolbar.OnMenuItemClickListener {
private static final String TAG = "Fragmentation";
private String[] mTitles = new String[]{
"航拍“摩托大军”返乡高峰 如蚂蚁搬家(组图)",
"苹果因漏电召回部分电源插头",
"IS宣称对叙利亚爆炸案负责"
};
private String[] mContents = new String[]{
"1月30日,距离春节还有不到十天,“摩托大军”返乡高峰到来。航拍广西梧州市东出口服务站附近的骑行返乡人员,如同蚂蚁搬家一般。",
"昨天记者了解到,苹果公司在其官网发出交流电源插头转换器更换计划,召回部分可能存在漏电风险的电源插头。",
"极端组织“伊斯兰国”31日在社交媒体上宣称,该组织制造了当天在叙利亚首都大马士革发生的连环爆炸案。"
};
private Toolbar mToolbar;
private FloatingActionButton mFab;
private RecyclerView mRecy;
private HomeAdapter mAdapter;
public static HomeFragment newInstance() {
return new HomeFragment();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_home, container, false);
initView(view);
return view;
}
@Override
protected FragmentAnimator onCreateFragmentAnimator() {
// 默认不改变
// return super.onCreateFragmentAnimation();
// 在进入和离开时 设定无动画
return new DefaultNoAnimator();
}
private void initView(View view) {
mToolbar = (Toolbar) view.findViewById(R.id.toolbar);
mFab = (FloatingActionButton) view.findViewById(R.id.fab);
mRecy = (RecyclerView) view.findViewById(R.id.recy);
mToolbar.setTitle(R.string.home);
initToolbarNav(mToolbar, true);
mToolbar.inflateMenu(R.menu.home);
mToolbar.setOnMenuItemClickListener(this);
//_mActivity是SupportFragment中成员变量,在onAttach方法中初始化
mAdapter = new HomeAdapter(_mActivity);
//设置RecyclerView分界线和适配器
LinearLayoutManager manager = new LinearLayoutManager(_mActivity);
mRecy.setLayoutManager(manager);
mRecy.setAdapter(mAdapter);
//设置RecyclerView上滑时FAB隐藏,下滑显示
mRecy.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (dy > 5) {
mFab.hide();
} else if (dy < -5) {
mFab.show();
}
}
});
//设置RecyclerView的item点击事件,启动DetailFragment
mAdapter.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(int position, View view) {
start(DetailFragment.newInstance(mAdapter.getItem(position).getTitle()));
}
});
// Init Datas
List articleList = new ArrayList<>();
for (int i = 0; i < 15; i++) {
int index = (int) (Math.random() * 3);
Article article = new Article(mTitles[index], mContents[index]);
articleList.add(article);
}
mAdapter.setDatas(articleList);
mFab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
}
/**
* 类似于 Activity的 onNewIntent()
*/
@Override
protected void onNewBundle(Bundle args) {
super.onNewBundle(args);
Toast.makeText(_mActivity, args.getString("from"), Toast.LENGTH_SHORT).show();
}
//设置点击动画弹出popMenu,设置动画模式
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_anim:
final PopupMenu popupMenu = new PopupMenu(_mActivity, mToolbar, GravityCompat.END);
popupMenu.inflate(R.menu.home_pop);
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_anim_veritical:
_mActivity.setFragmentAnimator(new DefaultVerticalAnimator());
Toast.makeText(_mActivity, "设置全局动画成功! 竖向", Toast.LENGTH_SHORT).show();
break;
case R.id.action_anim_horizontal:
_mActivity.setFragmentAnimator(new DefaultHorizontalAnimator());
Toast.makeText(_mActivity, "设置全局动画成功! 横向", Toast.LENGTH_SHORT).show();
break;
case R.id.action_anim_none:
_mActivity.setFragmentAnimator(new DefaultNoAnimator());
Toast.makeText(_mActivity, "设置全局动画成功! 无", Toast.LENGTH_SHORT).show();
break;
}
popupMenu.dismiss();
return true;
}
});
popupMenu.show();
break;
}
return true;
}
@Override
public void onDestroyView() {
super.onDestroyView();
mRecy.setAdapter(null);
}
}