简介:
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'
单Activity需要继承自SupportActivity,多Fragment都继承自SupportFragment。可以在AndroidStudio中使用Ctrl+H快捷键查看类的继承结构,如下所示,SupportActivity继承自AppCompatActivity,爷爷正好是FragmentActivity,SupportFragment继承自V4包下的Fragment,所以基本不影响我们使用:
一些API的使用(对照流式交互Demo)
先看一下demo的整体效果
这里先贴出单Activity的代码,代码内我添加了一些注释,基本可以了解清楚MainActivity 中做了哪些事情。这里可以列出比较重要的Api:
附: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<Article> 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); } }
装载根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()); } });
在Activity中重写onCreateFragmentAnimator()方法来设置所有Fragment的出场消失动画,如果在某个单独的Fragment中复写该方法,则只单独对该Fragment有效,这里可以详细的看一下wiki:使用场景- 转场动画;
通过重写父类SupportActivity的onBackPressedSupport()方法,可以轻松的实现按下手机返回键要实现的功能,在下面的demo源码中给出了详细的注释,这里也可以详细的看一下wiki:使用场景- Back键的事件传递机制;
启动跳转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(); } }
共同学习,写下你的评论
评论加载中...
作者其他优质文章