关闭

Fragment使用姿势之NoFragment

标签: FragmentNoFragmentFragmen返回栈Fragment参数
9229人阅读 评论(18) 收藏 举报
分类:

版权声明:转载必须注明本文转自严振杰的博客:http://blog.yanzhenjie.com

我还算是个比较传统的人,所以我一直觉得明年是从春节开始,所以这是我今年最后一骗博客了,当然也是我阳历2017年第一骗博客,希望大家可以吸收到对自己有帮助的东西,因此这是一骗承上启下的文章。对了,你没看错,是骗(反正你也不能顺着网线来打我)。

今天会先介绍Fragment通常的四种用法,然后再介绍一个开源项目NoFragment的特点和用法。

对于Fragment大家一定不陌生,记得第一次接触时我还是个大三的学生。是在2013年底参加Google全国大学生Android应用设计大赛时,初赛获得华中五省第二名,有资格参加全国总决赛。总决赛是现场编码,需求大概是这样的:要求使用的技术FragmentSocketNFC二维码识别,远程控制一个装有摄像头的小车沿着规定的路线做task,并且一路拍照并上传到Android端,Android端分析图片颜色,根据指定颜色再进行下一个随机任务,重点是,Android端手动发送一个start命令后,人就不能触碰手机了,剩下的事情全部由我们开发的Android端智能完成。项目和任务都完成了,遗憾的是由于其它人耗时更短,所以只获得了15/34。不过从那个比赛也收获了很多,从此从JavaWeb跳入了Android的坑,但是我一直很庆幸自己在从事Android开发。当时印象最深的是玩FragmentNFC,网上没有任何文章,就只好从官网看文档,后来也养成了自己喜欢看官网的文档的习惯。

回到正题,不少被Fragment的生命周期、相互跳转、参数传递、stack回退等问题困扰。如果是结合ViewPager还好说。然而很多同学的项目结构是一个Activity + 多个Fragment的形式,这样会加快页面之间的跳转,但是也带来不少问题,请往下看。

因此顺理成章的安利一个开源项目NoFragment,代码开源在Github:https://github.com/yanzhenjie/NoFragment

NoFragment 特点

  1. 支持传统Fragment的所有用法。
  2. 支持startFragmentForResult(Fragment)onFragmentResult(int, int, Bundle),原生只有Activity。
  3. 支持同一个Fragment启动多个实例。
  4. 支持自动维护Back Stack,不会错乱。
  5. 支持在Fragment中直接setToolbar()setTitle()displayHomeButton()
  6. 返回键和homeButton自动处理,支持开发者拦截处理。
  7. 支持ActionBar Menu、溢出Menu等。

当然最基本的是一个Activity + 多个Fragment。

依赖方法

  • Gradle一句话远程依赖
compile 'com.yanzhenjie:fragment:1.0.0'
  • Maven:
<dependency>
  <groupId>com.yanzhenjie</groupId>
  <artifactId>fragment</artifactId>
  <version>1.0.0</version>
  <type>pom</type>
</dependency>
  • Eclipse ADT
    请放弃治疗。

通常使用Fragment 4个姿势

一般我们使用Fragment大概有四种方法,第一种:ViewPager + Fragment,第二种:layout.xml{framgent},第三种:FragmetManager#replace(),第四种:FragmentManager#show()/hide(),下面我们一个个来看看用法和他们的优缺点。

一、ViewPager + Fragment

先来看一张图:

ViewPager + Fragment

当然了,代码也是超级无敌的简单(纯手写,小错请忽略):

ViewPager viewPager = findViewById(R.id.view_pager);
List<Fragment> fragmentList = new ArrayList<>();
fragmentList.add(new MyFragment());
...
FragmentManager fManager = getSupportFragmentManager();
FragmentAdapter adapter = new FragmentAdapter(fManager, fragmentList);
viewPager.setAdapter(adapter);

------------------------------------------------------------------------------

public class FragmentAdapter extends FragmentPagerAdapter {

    private List<Fragment> mFragmentList;

    public FragmentAdapter(FragmentManager fm, List<Fragment> fragmentList) {
        super(fm);
        mFragmentList = fragmentList;
    }

    @Override
    public Fragment getItem(int position) {
        return mFragmentList.get(position);
    }

    @Override
    public int getCount() {
        return mFragmentList.size();
    }
}

这种玩法应该是最简单的一种了,ViewPager的Adapter会自动为我们管理show()hide(),生命周期的调用我们也完全不用担心,由于这种方法太简单了,就不说了,对此用法遇到问题的同学也可以在文章下留言,我会一一解答。

二、layout.xml {framgent}

这种玩法也很简单,在布局中写一个framgent,name填我们fragment完整包名就可以了,这种用法我们可以简单粗暴的把fragment理解为一个view:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <fragment
        android:id="@+id/fragment_sign_in"
        android:name="com.yanzhenjie.fragment.SignInFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>

上述代码中的framgent完整包名:android:name="com.yanzhenjie.fragment.SignInFragment",这样布局会自动加载fragment进来,我们在java代码中可以通过如下方法拿到这个fragment

FragmetnManager fManager = getSupportFragmentManager();
SignInFragment signInFragment = (SignInFragment)fManager.findFragmentById(R.id.fragment_sign_in);
// TODO Then, do anything.

这种玩法常用的是大屏手机上,左边一个MenuFragment,右边一个ContentFragment,左边控制右边的内容。当然也有一个Fragment占满全屏的用法,比如高德地图MapView提供的MapFragment

三、FragmetManager#replace()

我最开始玩Fragment就是这么玩的,一般是在布局中提供一个空的FrameLayout,再用FragmetManager#replace()替换要显示的Fragment

// 调用
replaceFragment(new SignInFragment());

--------------------------------------------------------------

public <T extends Fragment> void replaceFragment(T fragment) {
    FragmentManager fManager = getSupportFragmentManager();
    fManager
        .beginTransaction()
        .replace(R.id.fragment_root, fragment)
        .commitAllowingStateLoss();
}

这种用法可以做到项目只有一个Activity,然后多个Fragment做为切换的页面。比较省内存,页面之间的跳转速度比较快,由于使用replace()方法它的缺点是:

  • 所以无法保存fragment的状态。
  • Back Stack错乱,没有ActivityBack Stack的好用。
  • 没有startFragmetnForResult()用法。

四、FragmentManager#show()/hide()

我们先来看看这种方法怎么玩:

/**
 * 显示一个新的fragment,并且隐藏旧的fragment。
 *
 * @param outFragment display fragment。
 * @param inFragment  hide fragment。
 * @param <T>         {@link Fragment}.
 */
public <T extends Fragment> void showHideFragment(T outFragment, T inFragment) {
    FragmentManager fManager = getSupportFragmentManager();
    // 如果要隐藏的fragment非空,隐藏。
    if (outFragment != null) {
        fManager
            .beginTransaction()
            .hide(outFragment)
            .commit();
    }
    // 先从栈中看是否存在要显示的fagment。
    String tag = inFragment.getClass().getClass().getSimpleName();
    Fragment tempFragment = fManager.findFragmentByTag(tag);
    if(tempFragment != null) { // 存在则直接显示。
        fManager
            .beginTransaction()
            .show(inFragment)
            .commitAllowingStateLoss();
    } else { // 不存在就添加并显示。
        fManager
            .beginTransaction()
            .add(R.id.fragment_root, inFragment, tag)
            .addToBackStack(tag)
            .commitAllowingStateLoss();
    }
}

/**
 * Destroy self.
 */
public <T extends Fragment> void finish(T fragment) {
    FragmentManager fManager = getSupportFragmentManager();
    fManager
        .beginTransaction()
        .remove(fragment)
        .commitAllowingStateLoss();
    }

经过上面的封装,其实已经变的很好用了:
Activity中:

showHideFragment(null, new SignInFragment());

Fragment中:

showHideFragment(this, new RegisterFragment());

当需要退出的时候:

finish(this);

结合注释看应该是看得懂的,调用show()hide()方法可以保存fragment的状态,但是逻辑复杂,同样存在以下缺点:

  • 没有startFragmetnForResult()用法。
  • 没有Activitystandard模式,也就是同一个Fragment启动多个实例。

因此我结合support包中的Fragment开发了NoFragment,没错它和NoHttp是同一个系列,就是为了让你忘记Fragment

NoFragment新姿势新玩法

其实Fragment的用法上图是没法很直观的了解的,所以建议大家去下载Demo看看,如果觉得好用的话,顺便点个Star:https://github.com/yanzhenjie/NoFragment

特点

  1. 支持传统Fragment的所有用法。
  2. 支持startFragmentForResult(Fragment)onFragmentResult(int, int, Bundle),原生只有Activity。
  3. 支持同一个Fragment启动多个实例。
  4. 支持自动维护Back Stack,不会错乱。
  5. 支持在Fragment中直接setToolbar()setTitle()displayHomeButton()
  6. 返回键和homeButton自动处理,支持开发者拦截处理。
  7. 支持ActionBar Menu、溢出Menu等。
  8. 开发者不用管跳转逻辑、back键处理、Toolbar加载菜单等。

图示

第一种,结合ToolBar、Menu的演示:

这里写图片描述

第二种,结合Toolbar、Menu + OverFlower的演示:

这里写图片描述

第三种,startFragmentForResult()onFragmentResult()演示:

这里写图片描述

第四种,不保存的在回退栈的演示:

这里写图片描述

用法

你的宿主Activity需要继承CompatActivity,然后启动一个Fragment

public class MainActivity extends CompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        /*
         * 一句话即可,不要怀疑自己的眼睛,这是真的。
         */
        startFragment(MainFragment.class);
    }

    @Override
    protected int fragmentLayoutId() {
        return R.id.fragment_root;
    }

}

之后在Fragment中互相跳转,你可以不同管物理Back键被按下之类的:

一、以standard模式启动一个Fragment

startFragment(MoreMenuFragment.class);

二、以startActivityForResult()方式启动一个Fragment

// 启动,等待回调结果。
startFragmentForResquest(StartResultFragment.class, 100);

// 不论怎样回来都会回调onFragmentResult()。
@Override
public void onFragmentResult(int requestCode, int resultCode, @Nullable Bundle result) {
    switch (requestCode) {
        case 100: {
            if (resultCode == RESULT_OK) {
                // 操作成功:result就是调用的Fragment返回的结果。
            } else if (resultCode == RESULT_CANCELED) {
                // 操作取消。
            }
            break;
        }
    }
}

StartResultFragment中如果要返回结果,那么:

Bundle bundle = new Bundle();
bundle.putString("message", result);
setResult(RESULT_OK, bundle);
finish();

当然你也不设置,那么resultCode的默认值是RESULT_CANCELED

三、跳转时带参数

// 封装参数:
Bundle bundle = new Bundle();
bundle.putString("hehe", "呵呵哒");
bundle.putString("meng", "萌萌哒");
bundle.putString("bang", "棒棒哒");
bundle.putString("meme", "么么哒");

// 第一种:
NoFragment fragment = NoFragment.instantiate(getContext(), ArgumentFragment.class, bundle);

// 第二种:
ArgumentFragment fragment = new ArgumentFragment();
fragment.setArgument(bundle);

// 最后启动:
startFragment(fragment);

四、跳转的Fragment不保存在Back Stack

这种方式显示的fragment中如果调用了其它fragment,从其它fragment中回来时,这个fragment将会跳过,不会显示,也就是说:A-B-C-[back]-A,从A到B,B不加入回退栈,B再到C,C按下返回键,或者调用finish()方法,将会直接回到A。

startFragment(StackFragment.class, false);

五、同一个Fragment,启动多个实例

startFragment(MoreMenuFragment.class);
startFragment(MoreMenuFragment.class);
startFragment(MoreMenuFragment.class);
startFragment(MoreMenuFragment.class);

比如我们这里调用四次,那么回退栈中有四个MoreMenuFragment,按下返回键时将一个个退出。

六、Toolbar菜单的加载和处理

我们知道MD设计中,Toolbar的菜单很好看,而且利用Toolbar也很好加载,那么NoFragment也是完美支持的,当重写了onCreateOptionsMenu()方法后,调用setToolbar(Toolbar)方法时,将会调用onCreateOptionsMenu()方法,此时你就该加载菜单了,当然也只需要一句话。

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    // Load your menu.
    inflater.inflate(R.menu.menu_fragment_main, menu);
}

当用户点击meun的item时将会回调这个方法,和原生Activity是一样的。

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle menu item click.
    int id = item.getItemId();
    switch (id) {
        case R.id.action_settings: {
            Snackbar.make(mToolbar, R.string.action_settings, Snackbar.LENGTH_SHORT).show();
            break;
        }
        case R.id.action_exit: {
            Snackbar.make(mToolbar, R.string.action_exit, Snackbar.LENGTH_SHORT).show();
            break;
        }
    }
    return true;
}

七、Toolbar的返回按钮的处理

在正常开发中给Toolbar设置返回按钮也要好几行代码的,如果使用了NoFragment,那么:

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

    // 首先设置Toolbar:
    setToolbar(mToolbar);

    // 设置标题:
    setTitle(R.string.title_fragment_main);

    // 显示返回按钮,图标开发者指定:
    displayHomeAsUpEnabled(R.drawable.ic_close_white);
}

设置了返回按钮后,用户点击返回按钮将自动杀死当前Fragment,当然你也可以拦截用户的返回行为:

@Override
    public boolean onInterceptToolbarBack() {
        // 返回true将拦截,返回false将不拦截。
        return true;
    }

混淆

-keep public class * extends android.support.v4.app.Fragment

好啦,到这里就完了,希望这个项目可以帮到需要的同学。


版权声明:转载必须注明本文转自严振杰的博客:http://blog.yanzhenjie.com

13
0
查看评论
发表评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场

Android Fragment 真正的完全解析(上)

自从Fragment出现,曾经有段时间,感觉大家谈什么都能跟Fragment谈上关系,做什么都要问下Fragment能实现不~~~哈哈,是不是有点过~~~ 本篇博客力求为大家说明Fragment如何产生,什么是Fragment,Fragment生命周期,如何静态和动态的使用Fragment,Frag...
  • lmj623565791
  • lmj623565791
  • 2014-07-20 11:22
  • 785374

fragment简单使用

Fragment学习——fragment简单使用实现一个简单的例子,流程如下:一、添加两个类继承Fragment,并且为对应的fragment创建对应的xml布局。fragment01代码如下:import android.app.Fragment; import android.os.Bundle...
  • bobo8945510
  • bobo8945510
  • 2016-10-11 17:57
  • 3807

Fragment 本质探究

在 google 设计角度来看,Fragment 是 Activity 的片段,所以 Fragment 和 Activity 一样是 Framework 提供给我们的对象的容器
  • zhaoyw2008
  • zhaoyw2008
  • 2015-05-20 00:29
  • 1019

纯净主框架Fragment 安卓项目搭建框架

  • 2016-03-08 15:06
  • 2.84MB
  • 下载

Fragment详解之三——管理Fragment(1)

前言:follow your heart,be your own king
  • harvic880925
  • harvic880925
  • 2015-04-07 23:00
  • 76391

Fragment的基本应用

转载请注明出处:http://blog.csdn.net/crazy1235/article/details/50771703Fragment还是先来基本介绍。Fragment –> 片段。 在Android3.0的时候被引入,它的出现主要是给大屏幕设备提供更加灵活的UI支持。通过对Acti...
  • crazy1235
  • crazy1235
  • 2016-03-19 23:34
  • 16030

安卓Fragment使用详解

Fragment   一、Fragment的概念和用法:     (一)、概念:         Fragment是在Android 3.0 (API level 11)开始引入新的API...
  • wuqingyidongren
  • wuqingyidongren
  • 2016-05-23 11:38
  • 13710

Android之Fragment的优点和作用

一:什么是Fragment 碎片、片段。其目的是为了解决不同屏幕分辩率的动态和灵活UI设计。大屏幕如平板小屏幕如手机,平板电脑的设计使得其有更多的空间来放更多的UI组件,而多出来的空间存放UI使其会产生更多的交互,从而诞生了fragments。   fragments 在活动Activity中为...
  • qq_34927117
  • qq_34927117
  • 2017-03-29 17:06
  • 667

Fragment详解之二——基本使用方法

上一篇给大家简单说了说Fragment是用来做什么的及生命周期的问题,这篇我们就用实例来看看我们在代码中如何使用Fragment; 在这里我们全部使用android-support-v4.jar包里Fragment,不用系统自带的Fragment;这两个基本一样,但V4包中的相对功能更强大一些。
  • harvic880925
  • harvic880925
  • 2015-04-07 23:00
  • 22246

Android Fragment 你应该知道的一切

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/42628537,本文出自:【张鸿洋的博客】很久以前写过两篇Fragment的介绍,主要就是介绍其功能:Android Fragment 真正的完全解析(上)和Android Frag...
  • lmj623565791
  • lmj623565791
  • 2015-01-12 01:56
  • 223680
    个人资料
    • 访问:1614072次
    • 积分:6533
    • 等级:
    • 排名:第4287名
    • 原创:52篇
    • 转载:0篇
    • 译文:1篇
    • 评论:1277条
    我的微信公众号
    欢迎关注我的公众号,不定期为您推送优选博文,生活趣事!
    关注我的微信公众号

    关注我的微博
    友情链接


    我的Github

    QQ交流群 547839514

    博客专栏