Android Fragment之間的通訊處理
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/