Fragment是google由3.0开始加入SDK的界面解决方案。
后续由谷歌团队维护并发行了support包以支持低版本SDK来使用Fragment。
谁在使用Fragment
- 网易新闻
- 网易云音乐
- 百度音乐
- 多米
- 豌豆荚
- 小米app
- Path
- Fuubo
###Fragment的优点 —————- * adding and removing Fragment可以做动画的效果,平滑过度
-
自动化堆栈管理,所以返回键可以删除动态添加的Fragment,***销毁Activity,无需做过多判断
-
集成ActionBar的标签,可以替代TabHost,ActivityGrounp,与谷歌设计风格紧密结合
-
布局更加模块化.与原Activity中的Layout分块化,VCBase的分块化道理相同
-
灵活准确的生命周期去管理当前View的状态记录以及横竖屏处理
-
Fragment管理的View,可同时使用在Phone和Pad上,一份代码两份机器,可重用性高
-
Is a View, More than View
-
可以从startActivityForResult中接收到返回结果,但是View不能
-
唯一Id标识,可以从FragmentManager中获取id对应的Fragment
Fragment的缺点
与其说是Fragment的缺点,不如说是每个应用程序模块之间的通讯都面临地耦合问题
- Fragment之间的通讯依赖Activity使用接口管理并通知
如何解决模块之间的通讯的耦合问题
1.使用接口,让Activity扮演管理角色,负责分发消息到该窗口的子View
该方案的缺点
- 不方便使用单元测试
- 随着应用功能的增加,需要监听的事件越来越多,导致越来越多的接口声明以及绑定
2.使用LocalBroadcastManager + IntentFilter解决不同组件通讯,Intent负责搭载数据
该方案的缺点
- 不方便单元测试,需要实例化Intent,填装Intent的数据,实现Broadcast receivers以及再次提取Intent中的数据
- receiver中不可做耗时操作,因为reciver是限时进程,10秒后会被系统kill掉,如果需要做耗时操作,需另外启Service来完成
3.EventBus
- 消息订阅者:Activity or Fragment等订阅类注册自己到EventBus中
- 消息发布者:只负责发布消息以及消息包装数据到EventBus
- 回调基于命名约定以及消息包装对象
-
方便的单元测试
4.otto 这里不做介绍,下面有demo链接,基于注解的解偶通信组件
其实按照MVC的思想,Activity就真正的变成了Controler,
Activity中不涉及任何的业务逻辑的代码,只负责分发消息到不同的子View(Fragment)。
如果希望整个应用只有一个Activity,就需要再抽象出一层Controller,负责处理Activity与其子Controller的通讯
相关下载
项目
我们直接看代码吧,因为表达能力还训练,加上有点懒 ^_^ 😄
项目结构
###首先是布局de代码 - /layout/article_view.xml ** ArticleFragment.java ** 关联的布局
- <?xml version="1.0" encoding="utf-8"?>
- <TextView xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/article"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:padding="16dp"
- android:textSize="18sp" >
- </TextView>
/layout/news_articles.xml ** HeadlinesFragment.java ** 关联的布局
- <?xml version="1.0" encoding="utf-8"?>
- <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/fragment_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
- </FrameLayout>
/layout-large/new_articles.xml ** HeadlinesFragment.java ** 关联的布局,在平板大分辨率的时候回被自动启用
- <?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="horizontal" >
- <fragment
- android:id="@+id/headlines_fragment"
- android:name="tree.love.android.fragments.HeadlinesFragment"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1" />
- <fragment
- android:id="@+id/article_fragment"
- android:name="tree.love.android.fragments.ArticleFragment"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="2" />
- </LinearLayout>
#p#
MainActivity.java 首页 -_- 其实就那么一页 哈哈哈
- public class MainActivity extends FragmentActivity implements HeadlinesFragment.OnHeadlineSelectedListener {
- private static final String TAG = "MainActivity";
- private LocalBroadcastManager mBroadcastManager;
- private BroadcastReceiver mItemViewListClickReceiver;
- public static final String ACTION_ITEMVIEW_LISTCLICK = "tree.love.android.fragments.itemview.listclick";
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.news_articles);
- //如果是手机分辨率布局
- if (findViewById(R.id.fragment_container) != null) {
- // 如果之前保存了状态,我们不需要做任何事情,否则会重复加载Fragment
- if (savedInstanceState != null) {
- return;
- }
- // Create an instance of ExampleFragment
- HeadlinesFragment firstFragment = new HeadlinesFragment();
- //如果这个Activity被一个特殊的Intent传递,如果有需要,把该数据也传给Fragment
- firstFragment.setArguments(getIntent().getExtras());
- // 添加该Fragment到R.id.fragment_container这个容器布局中
- getSupportFragmentManager().beginTransaction()
- .add(R.id.fragment_container, firstFragment).commit();
- }
- }
- private void initBroadcastListener() {
- mBroadcastManager = LocalBroadcastManager.getInstance(this);
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(ACTION_ITEMVIEW_LISTCLICK);
- mItemViewListClickReceiver = new BroadcastReceiver()
- {
- @Override
- public void onReceive(Context context, Intent intent)
- {
- if(intent.getAction().equals(ACTION_ITEMVIEW_LISTCLICK))
- {
- Log.v(TAG, ACTION_ITEMVIEW_LISTCLICK + "," + intent.getIntExtra("position", -1));
- }
- }
- };
- mBroadcastManager.registerReceiver(mItemViewListClickReceiver, intentFilter);
- }
- /*
- * 实现HeadlinesFragment.OnHeadlineSelectedListener中的ListView点击事件的回调接口
- */
- public void onArticleSelected(int position) {
- // 获取当前Activity是否已经加载了ArticleFragment
- ArticleFragment articleFrag = (ArticleFragment)
- getSupportFragmentManager().findFragmentById(R.id.article_fragment);
- if (articleFrag != null) {
- //如果进到这里,说明我们正在使用大屏幕布局/.
- //直接更新ArticleFragment的布局
- articleFrag.updateArticleView(position);
- } else {
- // 我们正在使用小屏幕布局
- // 创建Fragment,并且传递参数
- ArticleFragment newFragment = new ArticleFragment();
- Bundle args = new Bundle();
- args.putInt(ArticleFragment.ARG_POSITION, position);
- newFragment.setArguments(args);
- FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
- //可定制Fragment的退出和进入动画 , 设置在replace or add之前
- transaction.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out, android.R.anim.fade_in, android.R.anim.fade_out);
- // 替换R.id.fragment_container容器布局中的View
- transaction.replace(R.id.fragment_container, newFragment);
- // 添加事物回退栈,让系统管理,当用户点击返回按钮时,销毁当前加载到容器布局中的ArticleFragment
- transaction.addToBackStack(null);
- // 提交事物...不然你永远看不到ArticleFragment的出现 ^_^
- transaction.commit();
- }
- }
- /**
- * EventBus事件回掉
- * @param event
- */
- public void onEvent(ListClickEvent event)
- {
- Log.v("", "onEvent position:" + event.getPosition());
- }
- @Override
- protected void onStart() {
- super.onStart();
- //在需要接收事件通知的类添加到EventBus
- EventBus.getDefault().register(this);
- //注册Receiver
- initBroadcastListener();
- }
- @Override
- protected void onPause()
- {
- super.onPause();
- //取消事件监听
- EventBus.getDefault().unregister(this);
- mBroadcastManager.unregisterReceiver(mItemViewListClickReceiver);
- }
- }
HeadlinesFragment.java ListView菜单布局
- public class HeadlinesFragment extends ListFragment {
- OnHeadlineSelectedListener mCallback;
- // 通讯接口, 加载该Fragment的容器Activity必须实现此接口可以接收ListView的点击消息
- public interface OnHeadlineSelectedListener {
- /** 当HeadlinesFragment中的ListView点击的时候触发 */
- public void onArticleSelected(int position);
- }
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- int layout = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ? android.R.layout.simple_list_item_activated_1 : android.R.layout.simple_list_item_1;
- setListAdapter(new ArrayAdapter<String>(getActivity(), layout, Ipsum.Headlines));
- }
- @Override
- public void onStart() {
- super.onStart();
- if (getFragmentManager().findFragmentById(R.id.article_fragment) != null) {
- getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
- }
- }
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
- // 保证容器Activity实现了回调接口 否则抛出异常警告
- try {
- mCallback = (OnHeadlineSelectedListener) activity;
- } catch (ClassCastException e) {
- throw new ClassCastException(activity.toString() + " must implement OnHeadlineSelectedListener");
- }
- }
- @Override
- public void onListItemClick(ListView l, View v, int position, long id) {
- //1.通讯方式1 接口通知Activity
- mCallback.onArticleSelected(position);
- //2.通讯方式2 发送广播
- Intent intent = new Intent(MainActivity.ACTION_ITEMVIEW_LISTCLICK);
- intent.putExtra("position", position);
- LocalBroadcastManager.getInstance(getActivity()).sendBroadcast(intent);
- //3.通讯方式3 发送事件到消息中心,由消息中心负责分发事件
- EventBus.getDefault().post(new ListClickEvent(position));
- // 大屏幕pad分辨率使用两个panel的时候设置
- getListView().setItemChecked(position, true);
- }
- }
ArticleFragment.java 详情页布局。。就一个TextView啦。
- public class ArticleFragment extends Fragment {
- final static String ARG_POSITION = "position";
- int mCurrentPosition = -1;
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- //回复在onSaveInstanceState中保存的是状态数据
- if (savedInstanceState != null) {
- mCurrentPosition = savedInstanceState.getInt(ARG_POSITION);
- }
- return inflater.inflate(R.layout.article_view, container, false);
- }
- @Override
- public void onStart() {
- super.onStart();
- Bundle args = getArguments();
- if (args != null) {
- updateArticleView(args.getInt(ARG_POSITION));
- } else if (mCurrentPosition != -1) {
- updateArticleView(mCurrentPosition);
- }
- EventBus.getDefault().register(this);
- }
- @Override
- public void onPause()
- {
- super.onPause();
- EventBus.getDefault().unregister(this);
- }
- public void updateArticleView(int position) {
- TextView article = (TextView) getActivity().findViewById(R.id.article);
- article.setText(Ipsum.Articles[position]);
- mCurrentPosition = position;
- }
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putInt(ARG_POSITION, mCurrentPosition);
- }
- public void onEvent(ListClickEvent event)
- {
- Log.v("ArticleFragment", "onEvent" + event.getPosition());
- }
原文地址:http://wuyexiong.github.io/blog/2013/04/30/android-fragment-communication/