为了账号安全,请及时绑定邮箱和手机立即绑定

Android框架之路——Fragmentation的使用

标签:
Android

简介:

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

大神的话:

使用教程:

  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,所以基本不影响我们使用: 
                enter image description here

                enter image description here

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

  • 先看一下demo的整体效果 
          enter image description here      enter image description here      enter image description here

  • 这里先贴出单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);
            }
        }
  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();
        }
    }

原文链接:http://www.apkbus.com/blog-902180-63547.html

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 1
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消