Fragment使用姿势之NoFragment
来源:互联网 发布:网络浪涌保护器 编辑:程序博客网 时间:2024/06/06 14:07
版权声明:转载必须注明本文转自严振杰的博客:http://blog.yanzhenjie.com
我还算是个比较传统的人,所以我一直觉得明年是从春节开始,所以这是我今年最后一骗博客了,当然也是我阳历2017年第一骗博客,希望大家可以吸收到对自己有帮助的东西,因此这是一骗承上启下的文章。对了,你没看错,是骗(反正你也不能顺着网线来打我)。
今天会先介绍Fragment
通常的四种用法,然后再介绍一个开源项目NoFragment
的特点和用法。
对于Fragment大家一定不陌生,记得第一次接触时我还是个大三的学生。是在2013年底参加Google全国大学生Android
应用设计大赛时,初赛获得华中五省第二名,有资格参加全国总决赛。总决赛是现场编码,需求大概是这样的:要求使用的技术Fragment
、Socket
、NFC
、二维码识别
,远程控制一个装有摄像头的小车沿着规定的路线做task,并且一路拍照并上传到Android
端,Android
端分析图片颜色,根据指定颜色再进行下一个随机任务,重点是,Android端手动发送一个start命令后,人就不能触碰手机了,剩下的事情全部由我们开发的Android端智能完成。项目和任务都完成了,遗憾的是由于其它人耗时更短,所以只获得了15/34。不过从那个比赛也收获了很多,从此从JavaWeb跳入了Android的坑,但是我一直很庆幸自己在从事Android
开发。当时印象最深的是玩Fragment
和NFC
,网上没有任何文章,就只好从官网看文档,后来也养成了自己喜欢看官网的文档的习惯。
回到正题,不少被Fragment
的生命周期、相互跳转、参数传递、stack回退等问题困扰。如果是结合ViewPager
还好说。然而很多同学的项目结构是一个Activity + 多个Fragment的形式,这样会加快页面之间的跳转,但是也带来不少问题,请往下看。
因此顺理成章的安利一个开源项目NoFragment
,代码开源在Github:https://github.com/yanzhenjie/NoFragment
NoFragment 特点
- 支持传统
Fragment
的所有用法。 - 支持
startFragmentForResult(Fragment)
、onFragmentResult(int, int, Bundle)
,原生只有Activity。 - 支持同一个
Fragment
启动多个实例。 - 支持自动维护
Back Stack
,不会错乱。 - 支持在
Fragment
中直接setToolbar()
、setTitle()
、displayHomeButton()
。 - 返回键和
homeButton
自动处理,支持开发者拦截处理。 - 支持
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 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
错乱,没有Activity
的Back 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()
用法。 - 没有
Activity
的standard
模式,也就是同一个Fragment启动多个实例。
因此我结合support
包中的Fragment
开发了NoFragment
,没错它和NoHttp是同一个系列,就是为了让你忘记Fragment
。
NoFragment新姿势新玩法
其实Fragment的用法上图是没法很直观的了解的,所以建议大家去下载Demo看看,如果觉得好用的话,顺便点个Star:https://github.com/yanzhenjie/NoFragment
特点
- 支持传统
Fragment
的所有用法。 - 支持
startFragmentForResult(Fragment)
、onFragmentResult(int, int, Bundle)
,原生只有Activity。 - 支持同一个
Fragment
启动多个实例。 - 支持自动维护
Back Stack
,不会错乱。 - 支持在
Fragment
中直接setToolbar()
、setTitle()
、displayHomeButton()
。 - 返回键和
homeButton
自动处理,支持开发者拦截处理。 - 支持
ActionBar Menu
、溢出Menu
等。 - 开发者不用管跳转逻辑、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()。@Overridepublic 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()
方法,此时你就该加载菜单了,当然也只需要一句话。
@Overridepublic void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { // Load your menu. inflater.inflate(R.menu.menu_fragment_main, menu);}
当用户点击meun的item时将会回调这个方法,和原生Activity是一样的。
@Overridepublic 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,那么:
@Overridepublic 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
- Fragment使用姿势之NoFragment
- Fragment使用的正确姿势
- Fragment全解析系列(二):正确的使用姿势
- Fragment全解析系列(二):正确的使用姿势
- Fragment全解析系列(二):正确的使用姿势
- Fragment全解析系列(二):正确的使用姿势
- Fragment 全解析(2):正确的使用姿势
- Fragment全解析系列(二):正确的使用姿势
- Fragment全解析系列(二):正确的使用姿势
- Fragment全解析系列(二):正确的使用姿势
- Fragment全解析系列(二):正确的使用姿势
- Fragment全解析系列(二):正确的使用姿势
- Fragment全解析系列(二):正确的使用姿势
- fragment之基本使用
- android之Fragment使用
- Fragment之静态使用
- Fragment之动态使用
- QtQuick折腾结论之model delegate 的正确使用姿势
- ORA-01102: cannot mount database in EXCLUSIVE mode
- 欢迎使用CSDN-markdown编辑器
- 上下界网络流学习总结
- Longest Common Prefix
- 我自己的视图业务数据库模式
- Fragment使用姿势之NoFragment
- zoj2208
- windows环境下面配置pip环境变量
- 关系型数据库的优缺点
- centos 7安装jdk
- HTTP之请求响应内容详解
- vs2012 32位64 来回编译运行提示0xc000007b错误
- git分支操作
- JS中==、===和Object.is()的区别