Android Fragment之間的通訊處理
Fragment是google由3.0開始加入SDK的界面解決方案。
后續(xù)由谷歌團(tuán)隊(duì)維護(hù)并發(fā)行了support包以支持低版本SDK來使用Fragment。
誰在使用Fragment
- 網(wǎng)易新聞
- 網(wǎng)易云音樂
- 百度音樂
- 多米
- 豌豆莢
- 小米app
- Path
- Fuubo
###Fragment的優(yōu)點(diǎn) —————- * adding and removing Fragment可以做動畫的效果,平滑過度
-
自動化堆棧管理,所以返回鍵可以刪除動態(tài)添加的Fragment,***銷毀Activity,無需做過多判斷
-
集成ActionBar的標(biāo)簽,可以替代TabHost,ActivityGrounp,與谷歌設(shè)計(jì)風(fēng)格緊密結(jié)合
-
布局更加模塊化.與原Activity中的Layout分塊化,VCBase的分塊化道理相同
-
靈活準(zhǔn)確的生命周期去管理當(dāng)前View的狀態(tài)記錄以及橫豎屏處理
-
Fragment管理的View,可同時(shí)使用在Phone和Pad上,一份代碼兩份機(jī)器,可重用性高
-
Is a View, More than View
-
可以從startActivityForResult中接收到返回結(jié)果,但是View不能
-
唯一Id標(biāo)識,可以從FragmentManager中獲取id對應(yīng)的Fragment
Fragment的缺點(diǎn)
與其說是Fragment的缺點(diǎn),不如說是每個應(yīng)用程序模塊之間的通訊都面臨地耦合問題
- Fragment之間的通訊依賴Activity使用接口管理并通知
如何解決模塊之間的通訊的耦合問題
1.使用接口,讓Activity扮演管理角色,負(fù)責(zé)分發(fā)消息到該窗口的子View
該方案的缺點(diǎn)
- 不方便使用單元測試
- 隨著應(yīng)用功能的增加,需要監(jiān)聽的事件越來越多,導(dǎo)致越來越多的接口聲明以及綁定
2.使用LocalBroadcastManager + IntentFilter解決不同組件通訊,Intent負(fù)責(zé)搭載數(shù)據(jù)
該方案的缺點(diǎn)
- 不方便單元測試,需要實(shí)例化Intent,填裝Intent的數(shù)據(jù),實(shí)現(xiàn)Broadcast receivers以及再次提取Intent中的數(shù)據(jù)
- receiver中不可做耗時(shí)操作,因?yàn)閞eciver是限時(shí)進(jìn)程,10秒后會被系統(tǒng)kill掉,如果需要做耗時(shí)操作,需另外啟Service來完成
3.EventBus
- 消息訂閱者:Activity or Fragment等訂閱類注冊自己到EventBus中
- 消息發(fā)布者:只負(fù)責(zé)發(fā)布消息以及消息包裝數(shù)據(jù)到EventBus
- 回調(diào)基于命名約定以及消息包裝對象
-
方便的單元測試
4.otto 這里不做介紹,下面有demo鏈接,基于注解的解偶通信組件
其實(shí)按照MVC的思想,Activity就真正的變成了Controler,
Activity中不涉及任何的業(yè)務(wù)邏輯的代碼,只負(fù)責(zé)分發(fā)消息到不同的子View(Fragment)。
如果希望整個應(yīng)用只有一個Activity,就需要再抽象出一層Controller,負(fù)責(zé)處理Activity與其子Controller的通訊
相關(guān)下載
- EventBus項(xiàng)目托管
- EventBus-PPT
- [otto項(xiàng)目托管](https://github.com/square/otto
- otto-demo
項(xiàng)目
我們直接看代碼吧,因?yàn)楸磉_(dá)能力還訓(xùn)練,加上有點(diǎn)懶 ^_^ 😄
項(xiàng)目結(jié)構(gòu)
###首先是布局de代碼 - /layout/article_view.xml ** ArticleFragment.java ** 關(guān)聯(lián)的布局
- <?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 ** 關(guān)聯(lián)的布局
- <?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 ** 關(guān)聯(lián)的布局,在平板大分辨率的時(shí)候回被自動啟用
- <?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 首頁 -_- 其實(shí)就那么一頁 哈哈哈
- 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);
- //如果是手機(jī)分辨率布局
- if (findViewById(R.id.fragment_container) != null) {
- // 如果之前保存了狀態(tài),我們不需要做任何事情,否則會重復(fù)加載Fragment
- if (savedInstanceState != null) {
- return;
- }
- // Create an instance of ExampleFragment
- HeadlinesFragment firstFragment = new HeadlinesFragment();
- //如果這個Activity被一個特殊的Intent傳遞,如果有需要,把該數(shù)據(jù)也傳給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);
- }
- /*
- * 實(shí)現(xiàn)HeadlinesFragment.OnHeadlineSelectedListener中的ListView點(diǎn)擊事件的回調(diào)接口
- */
- public void onArticleSelected(int position) {
- // 獲取當(dāng)前Activity是否已經(jīng)加載了ArticleFragment
- ArticleFragment articleFrag = (ArticleFragment)
- getSupportFragmentManager().findFragmentById(R.id.article_fragment);
- if (articleFrag != null) {
- //如果進(jìn)到這里,說明我們正在使用大屏幕布局/.
- //直接更新ArticleFragment的布局
- articleFrag.updateArticleView(position);
- } else {
- // 我們正在使用小屏幕布局
- // 創(chuàng)建Fragment,并且傳遞參數(shù)
- ArticleFragment newFragment = new ArticleFragment();
- Bundle args = new Bundle();
- args.putInt(ArticleFragment.ARG_POSITION, position);
- newFragment.setArguments(args);
- FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
- //可定制Fragment的退出和進(jìn)入動畫 , 設(shè)置在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);
- // 添加事物回退棧,讓系統(tǒng)管理,當(dāng)用戶點(diǎn)擊返回按鈕時(shí),銷毀當(dāng)前加載到容器布局中的ArticleFragment
- transaction.addToBackStack(null);
- // 提交事物...不然你永遠(yuǎn)看不到ArticleFragment的出現(xiàn) ^_^
- 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();
- //取消事件監(jiān)聽
- EventBus.getDefault().unregister(this);
- mBroadcastManager.unregisterReceiver(mItemViewListClickReceiver);
- }
- }
HeadlinesFragment.java ListView菜單布局
- public class HeadlinesFragment extends ListFragment {
- OnHeadlineSelectedListener mCallback;
- // 通訊接口, 加載該Fragment的容器Activity必須實(shí)現(xiàn)此接口可以接收ListView的點(diǎn)擊消息
- public interface OnHeadlineSelectedListener {
- /** 當(dāng)HeadlinesFragment中的ListView點(diǎn)擊的時(shí)候觸發(fā) */
- 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實(shí)現(xiàn)了回調(diào)接口 否則拋出異常警告
- 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 發(fā)送廣播
- Intent intent = new Intent(MainActivity.ACTION_ITEMVIEW_LISTCLICK);
- intent.putExtra("position", position);
- LocalBroadcastManager.getInstance(getActivity()).sendBroadcast(intent);
- //3.通訊方式3 發(fā)送事件到消息中心,由消息中心負(fù)責(zé)分發(fā)事件
- EventBus.getDefault().post(new ListClickEvent(position));
- // 大屏幕pad分辨率使用兩個panel的時(shí)候設(shè)置
- 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) {
- //回復(fù)在onSaveInstanceState中保存的是狀態(tài)數(shù)據(jù)
- 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/