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

来源:互联网 发布:淘宝的pc端是什么意思 编辑:程序博客网 时间:2024/06/05 02:59

简介:

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

      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)// 以某种启动模式,启动新的Fragmentstart(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栈内之上的所有FragmentpopTo(Class fragmentClass/String tag, boolean includeSelf);// 出栈某一个Fragment栈内之上的所有Fragment。如果想出栈后,紧接着.beginTransaction()开始一个新事务,//请使用下面的方法, 防止多事务连续执行的异常popTo(Class fragmentClass, boolean includeSelf, Runnable afterTransaction);
      7. 查找获取某一Fragment

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

        public View onCreateView(...) {    ...    // 这里仅给一些findViewById等轻量UI的操作    initView();    return view;}@Overrideprotected 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<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);        }    }



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

1 0