Android框架之路——Fragmentation的使用(流式交互Demo)

简介:

YoKey大神的Fragment库Fragmentation,主要用于现在App经常需要实现的单Activity+多Fragment以及多Activity+多Fragment的形式架构。同时最最重要的是,它帮助我们封装了很多好用的方法,解决了一些官方Fragment库中存在的一些Bug。
我在学习做一款有关Ble蓝牙防丢器的App时想要尝试以单Activity+多Fragment的架构去实现,恰好可以使用这个库,也就皮毛的研究了一下。之前我是通过ViewPager去管理多个Fragment的进出,后来还是抛弃这种方式,因为确实不太合理。所以,用了Fragmentation这个库,还是非常不错的。

大神的话:

  • Fragment之我的解决方案:Fragmentation
  • Github:Fragmentation
  • Github:wiki 使用指南

使用教程:

  1. 添加依赖

    compile 'me.yokeyword:fragmentation:最新版'
    

    我的是

    compile 'me.yokeyword:fragmentation:0.10.3'
    
  2. 单Activity需要继承自SupportActivity,多Fragment都继承自SupportFragment。可以在AndroidStudio中使用Ctrl+H快捷键查看类的继承结构,如下所示,SupportActivity继承自AppCompatActivity,爷爷正好是FragmentActivity,SupportFragment继承自V4包下的Fragment,所以基本不影响我们使用:
                Android框架之路——Fragmentation的使用(流式交互Demo)_第1张图片

                enter image description here
  3. 一些API的使用(对照流式交互Demo)

    • 先看一下demo的整体效果
                  Android框架之路——Fragmentation的使用(流式交互Demo)_第2张图片      
    • 这里先贴出单Activity的代码,代码内我添加了一些注释,基本可以了解清楚MainActivity 中做了哪些事情。这里可以列出比较重要的Api:

      1. 装载根Fragment,一般在saveInstanceState==null时load;

        loadRootFragment(R.id.fl_container, HomeFragment.newInstance()); //activity初始加载HomeFragment
        
      2. 在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());
                }
            });
        
      3. 在Activity中重写onCreateFragmentAnimator()方法来设置所有Fragment的出场消失动画,如果在某个单独的Fragment中复写该方法,则只单独对该Fragment有效,这里可以详细的看一下wiki:使用场景- 转场动画;
      4. 通过重写父类SupportActivity的onBackPressedSupport()方法,可以轻松的实现按下手机返回键要实现的功能,在下面的demo源码中给出了详细的注释,这里也可以详细的看一下wiki:使用场景- Back键的事件传递机制;
      5. 启动跳转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)
        
      6. 出栈,移出某个Fragment

        // 当前Fragment出栈(在当前Fragment所在栈内pop)
        pop();
        
        // 出栈某一个Fragment栈内之上的所有Fragment
        popTo(Class fragmentClass/String tag, boolean includeSelf);
        
        // 出栈某一个Fragment栈内之上的所有Fragment。如果想出栈后,紧接着.beginTransaction()开始一个新事务,
        //请使用下面的方法, 防止多事务连续执行的异常
        popTo(Class fragmentClass, boolean includeSelf, Runnable afterTransaction);
        
      7. 查找获取某一Fragment

        // 获取所在栈内的栈顶Fragment
        getTopFragment();
        
        // 获取当前Fragment所在栈内的前一个Fragment
        getPreFragment();
        
        // 获取所在栈内的某个Fragment,可以是xxxFragment.Class,也可以是tag
        findFragment(Class fragmentClass/String tag);
        
      8. 防止动画卡顿,可以先让其加载完Fragment的转场动画,然后继续实现一些繁琐的业务逻辑。在Fragment中重写onEnterAnimationEnd(Bundle saveInstanceState)方法,在这个方法里继续执行一些繁琐操作。通常可以采取这样的一种模式:

        public View onCreateView(...) {
            ...
            // 这里仅给一些findViewById等轻量UI的操作
            initView();
            return view;
        }
        
        @Override
        protected void onEnterAnimationEnd(Bundle saveInstanceState) {
             // 这里设置Listener、各种Adapter、请求数据等等
            initLazyView();
        }
        
      9. 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);
            }
        }
        
      10. 以某种启动模式启动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); } }



个人公众号:每日推荐一篇技术博客,坚持每日进步一丢丢…欢迎关注,想建个微信群,主要讨论安卓和Java语言,一起打基础、用框架、学设计模式,菜鸡变菜鸟,菜鸟再起飞,愿意一起努力的话可以公众号留言,谢谢…

Android框架之路——Fragmentation的使用(流式交互Demo)_第3张图片

你可能感兴趣的:(Android,Android框架之路)