Fragment

若是使用Android3.0如下的版本,須要引入android.support.v4的JAR包,而後Activity繼承FragmentActivity,經過getSupportFragmentManager得到FragmentManager。
Android在3.0以上版本,可直接使用Activity,經過getFragmentManager得到FragmentManager。html

一. 生命週期:
onAttach(Activity):Fragment和Activity創建關聯時調用。
onCreate(Bundle)
onCreateView(LayoutInflater, ViewGroup, Bundle):爲Fragment加載佈局時調用。
onActivityCreated(Bundle)
onStart()
onResume()
onPause()
onStop()
onDestroyView():Fragment中的佈局被移除時調用。
onDestory()
onDetach():Fragment和Activity解除關聯的時候調用。android

二. 兩種加載方法:
靜態加載Fragment:在佈局文件中直接佈局<fragment>
<fragment android:id="@+id/id_fragment_title"
        android:name="com.test.fragment.TitleBarFragment"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />
每個fragment都須要一個惟一的標識,若是activity重啓,系統能夠用來恢復fragment(而且你也能夠用來捕獲fragment來處理事務,例如移除它. 有3種方法來爲一個fragment提供一個標識:
爲 android:id 屬性提供一個惟一ID.
爲 android:tag 屬性提供一個惟一字符串.
若是以上2個都沒有提供, 系統使用容器view的ID.git

動態添加Fragment主要分爲4步:
1.獲取到FragmentManager,在Activity中能夠直接經過getFragmentManager獲得。
2.開啓一個事務,經過調用beginTransaction方法開啓。
3.向容器內加入Fragment,通常使用replace方法實現,須要傳入容器的id和Fragment的實例。
4.提交事務,調用commit方法提交。github

FragmentTransaction方法:
add(int containerViewId, Fragment fragment, String tag)  往containerView中添加一個Fragment
remove() 從Activity中移除一個Fragment,若是被移除的Fragment沒有添加到回退棧,這個Fragment實例將會被銷燬。
replace(int containerViewId, Fragment fragment, String tag) 使用Fragment替換當前的containerView,實際上就是remove()而後add()的合體
hide() 隱藏當前的Fragment,僅僅是設爲不可見,並不會銷燬
show() 顯示以前隱藏的Fragment
detach() 會將view從UI中移除,和remove()不一樣,此時fragment的狀態依然由FragmentManager維護。
attach() 重建view視圖,附加到UI上並顯示。
addToBackStack(String) 添加一個Fragment事務到回退棧。用戶點擊Back,就是Fragment回退棧不斷的彈棧。
注:
將fragment從後臺堆棧中彈出, 使用FragmentManager.popBackStack() (模擬用戶按下BACK 命令).
使用FragmentManager.addOnBackStackChangeListener()註冊一個監聽後臺堆棧變化的listener.shell

三. Fragment之間進行通訊:
1. 在Fragment,使用getActivity()能夠獲取到關聯的Activity,而後再調用Activity的findViewById方法,就能夠獲取到和這個Activity關聯的其它Fragment的視圖了。app

2. 在Activity,使用getFragmentManager.findFragmentByTag()或者findFragmentById()得到任何Fragment實例。
可是以上兩種方法都不提倡,由於Fragment和Activity之間耦合太緊,應採用觀察者模式,設置Listener接口,在Activity中實現接口,在Fragment中調用接口函數,從而Fragment得事件就能夠通知到Activity了。less

3. 經過Hander來實現消息傳送,但這也是緊耦合。
public void onAttach(Activity activity) {
    super.onAttach(activity);
    mActivity = (MainActivity) activity;
    mActivity.setHandler(mHandler);
}ide

4. 使用Bundle,也是緊耦合。
在Activity中動態添加Fragment時,用Bundle封裝咱們須要傳遞的數據。setArguments方法必須在fragment建立之後,添加給Activity前完成。
public void button(View view) {
    ArgFragment arg = new ArgFragment();
    Bundle bundle = new Bundle();
    bundle.putString("arg", "XXX");
    arg.setArguments(bundle);
    FragmentManager fm = getFragmentManager();
    FragmentTransaction ft = fm.beginTransaction();
    ft.replace(R.id.layout_fragment, arg);
    ft.commit();
 }
而後在ArgFragment取出Bundle對象:
Bundle bundle = getArguments();
if (bundle != null){
    String str = bundle.getString("arg");
}函數

四. 若是系統內存不足或者切換橫豎屏或者app長時間在後臺運行,Activity均可能會被系統回收,而後Fragment並不會隨着Activity的回收而被回收,從而致使,Fragment丟失對應的Activity。解決方案是:佈局

1. Activity重寫onSaveInstanceState方法,將super.onSaveInstanceState(outState);註釋掉,讓其再也不保存Fragment的狀態,達到其隨着Activity一塊兒被回收的效果!

2. 根據savedInstanceState來區別處理:
public class MainActivity extends FragmentActivity {
    private FragmentManager fragmentManager;
    private AFragment aFragment;
    private BFragment bFragment;
    private int currentIndex = TempData.fragmentNo; //默認爲0

    private void getIntentData() {
        boolean hasData = false;
        Bundle b = getIntent().getExtras();
        if( b!=null && !b.isEmpty() && b.containsKey("index") ) {
            hasData = true;
            int index = b.getInt("index");
            currentIndex = index;
        }
    }

    @Override
    /* can be restored via {@link #onCreate} or {@link #onRestoreInstanceState}
    * */
    protected void onSaveInstanceState(Bundle outState) {
        outState.putInt("currentIndex", currentIndex);
        UtilLog.d(TAG, "MainActivity onSaveInstanceState(): currentIndex = " + currentIndex);
        super.onSaveInstanceState(outState);
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        if ( savedInstanceState == null ) {
            UtilLog.d(TAG, "MainActivity onRestoreInstanceState(null)");
            changeFragment(currentIndex);
        } else {
            // 說明FragmentActivity曾經被系統回收,而Fragment多是沒有被回收的,
            // 須要先找回當前處於顯示狀態的Fragment,並找回全部沒有被回收的Fragment,
            // 從而避免Fragment顯示重疊。
            if (savedInstanceState.containsKey("currentIndex")) {
                currentIndex = savedInstanceState.getInt(
                        "currentIndex", TempData.fragmentNo);
                UtilLog.d(TAG, "MainActivity onRestoreInstanceState(savedInstanceState): currentIndex = " + currentIndex);
            }
            UtilLog.d(TAG, "MainActivity onRestoreInstanceState(savedInstanceState)");
            showExistedFragment(currentIndex);
        }
    }

        @Override
        protected void onCreate(Bundle savedInstanceState) {
        UtilLog.d(TAG, "MainActivity onCreate() <--");
        super.onCreate(savedInstanceState);
        getIntentData();

            setContentView(R.layout.activity_main);
            fragmentManager = getSupportFragmentManager();
        
            getWindow().setSoftInputMode(
                WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN |
                        WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);

        if ( savedInstanceState == null ) {
            UtilLog.d(TAG, "MainActivity onCreate(null)");
            changeFragment(currentIndex);
        } else {
            // 「內存重啓」時調用, 此時Fragment可能還存在,須要經過Tag找回
            if (savedInstanceState.containsKey("currentIndex")) {
                currentIndex = savedInstanceState.getInt(
                        "currentIndex", TempData.fragmentNo);
                UtilLog.d(TAG, "MainActivity onCreate(savedInstanceState): currentIndex = " + currentIndex);
            }
            UtilLog.d(TAG, "MainActivity onCreate(savedInstanceState)");
            showExistedFragment(currentIndex);
        }
        UtilLog.d(TAG, "MainActivity onCreate() -->");
        }

    @Override
        protected void onNewIntent(Intent intent) {
        UtilLog.d(TAG, "MainActivity onNewIntent() <--");
            super.onNewIntent(intent);
            // must store the new intent unless getIntent() will return the old one
        setIntent(intent);

            if ( (Intent.FLAG_ACTIVITY_CLEAR_TOP & intent.getFlags()) != 0 ) {
            getIntentData();
            if( TempData.fragmentNo != currentIndex ) {
                    changeFragment(currentIndex);
            }
        }
    }

    private void hideFragment(FragmentTransaction fragmentTransaction) {
        if (aFragment != null ) {
            fragmentTransaction.hide(aFragment);
        }
        if (bFragment != null) {
            fragmentTransaction.hide(bFragment);
        }
    }

    private void changeFragment(int index) {
        FragmentTransaction fragmentTransaction = fragmentManager
                .beginTransaction();
        
        currentIndex = index;
        TempData.fragmentNo = index;
        hideFragment(fragmentTransaction);
        
        switch (index) {
        case 0:
            if(aFragment!=null && aFragment.isAdded()){
                fragmentTransaction.show(aFragment);
            }else {
                aFragment = new AFragment();
                    fragmentTransaction.add(R.id.details, aFragment, "AFragment");
            }
            break;
        case 1:
            if(bFragment!=null && bFragment.isAdded()){
                fragmentTransaction.show(bFragment);
            }else {
                bFragment = new BFragment();
                    fragmentTransaction.add(R.id.details, bFragment, "BFragment");
            }
            break;
        default:
            break;
        }
        fragmentTransaction.commitAllowingStateLoss();
    }
    
    private void showExistedFragment(int index) {
        Fragment first = fragmentManager.findFragmentByTag("AFragment");
        if(first!=null && first instanceof AFragment) {
            aFragment = (AFragment) first;
        }
        
        Fragment second = fragmentManager.findFragmentByTag("BFragment");
        if(second!=null && second instanceof BFragment) {
            bFragment = (BFragment) second;
        }
        
        changeFragment(index);
    }
}

Fragment內存重啓的模擬和處理:
adb shell pm list packages     讀取到本機安裝的應用列表
adb shell am kill 包名        將該應用置於後臺,而後模擬該應用的內存重啓

 

http://www.yrom.net/blog/2013/03/10/fragment-switch-not-restart/

http://www.cnblogs.com/kissazi2/p/4116456.html

五. 第三方fragmentation
// https://github.com/YoKeyword/Fragmentation
    compile 'me.yokeyword:fragmentation:0.10.4'

1. 加載多個同級根Fragment,沒有添加Fragment事務到回退棧。切換採用showHideFragment(...)

public class MainActivity extends SupportActivity{
    @Override
    /* can be restored via {@link #onCreate} or {@link #onRestoreInstanceState}
    * */
    protected void onSaveInstanceState(Bundle outState) {
        outState.putInt("mSelectedTabbar", mSelectedTabbar);
        super.onSaveInstanceState(outState);
    }

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

        if (savedInstanceState == null) {
            mFragments[FIRST] = ZhihuFirstFragment.newInstance();
            mFragments[SECOND] = ZhihuSecondFragment.newInstance();
            mFragments[THIRD] = ZhihuThirdFragment.newInstance();
            mFragments[FOURTH] = ZhihuFourthFragment.newInstance();
            // loadMultipleRootFragment的做用:
            // 一次性加載多個Fragments,可是containerId只默認顯示其中一個,
            // 默認顯示的那個Fragment會依次調用onCreateView(),onLazyInitView()
            // 其餘被隱藏的Fragment會依次調用onHiddenChanged(true),onCreateView()
            loadMultipleRootFragment(R.id.fl_container, FIRST,
                    mFragments[FIRST],
                    mFragments[SECOND],
                    mFragments[THIRD],
                    mFragments[FOURTH]);
        } else {
            // 這裏庫已經作了Fragment恢復,全部不須要額外的處理了, 不會出現重疊問題
            // 這裏咱們須要拿到mFragments的引用,也能夠經過getSupportFragmentManager.getFragments()自行進行判斷查找(效率更高些),用下面的方法查找更方便些
            mFragments[FIRST] = findFragment(ZhihuFirstFragment.class);
            mFragments[SECOND] = findFragment(ZhihuSecondFragment.class);
            mFragments[THIRD] = findFragment(ZhihuThirdFragment.class);
            mFragments[FOURTH] = findFragment(ZhihuFourthFragment.class);
            if ( savedInstanceState.containsKey("mSelectedTabbar") ) {
                mSelectedTabbar = savedInstanceState.getInt("mSelectedTabbar", FIRST);
            }
        }
        initView();
    }


    public void onTabSelected(int position, int prePosition) {
                mSelectedTabbar = position;
                // 在作Fragment的切換時,showHideFragment的做用:
                // 待顯示的Fragment若是沒有加載過界面,會先調用onLazyInitView(),而後調用onHiddenChanged(false)
                // 待隱藏的Fragment調用onHiddenChanged(true)
                showHideFragment(mFragments[position], mFragments[prePosition]);
            }

}

public class NewFeatureFragment extends SupportFragment {

@Override
    public void onLazyInitView(@Nullable Bundle savedInstanceState) {
        super.onLazyInitView(savedInstanceState);
        // 懶加載
        // 同級Fragment場景、ViewPager場景均適用
    }

    @Override
    public void onSupportVisible() {
        super.onSupportVisible();
        // 當對用戶可見時 回調
        // 無論是 父Fragment仍是子Fragment 都有效!
    }

    @Override
    public void onSupportInvisible() {
        super.onSupportInvisible();
        // 當對用戶不可見時 回調
        // 無論是 父Fragment仍是子Fragment 都有效!
    }
}

2. loadRootFragment(int containerId, SupportFragment toFragment)
加載根Fragment,將Fragment事務添加到回退棧中,是棧底元素。

3. replaceLoadRootFragment(int containerId, SupportFragment toFragment, boolean addToBack)
以replace方式加載根Fragment, 由addToBack參數決定是否將Fragment事務添加到回退棧。

public class MainActivity extends SupportActivity {
@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if (savedInstanceState == null) {
            // 加載根Fragment,這裏是將HomeFragment事務添加到回退棧中,是棧底元素
            loadRootFragment(R.id.fl_container, HomeFragment.newInstance());
        }
    }

    // 無論切換多少項,內存中始終只保留根Fragment(HomeFragment)和可見的Fragment。
    public void changeContainer( int id ) {
        
                final SupportFragment topFragment = getTopFragment(); // 獲取根Fragment

                if (id == R.id.nav_home) {                     HomeFragment fragment = findFragment(HomeFragment.class);                     Bundle newBundle = new Bundle();                     newBundle.putString("from", "主頁-->來自:" + topFragment.getClass().getSimpleName());                     fragment.putNewBundle(newBundle);                     start(fragment, SupportFragment.SINGLETASK);                 } else if (id == R.id.nav_discover) {                     DiscoverFragment fragment = findFragment(DiscoverFragment.class);                     if (fragment == null) {                         // 將棧中的Fragment出棧直到HomeFragment爲止,                         // 出棧的Fragment會被析構掉,出棧完成後,啓動DiscoverFragment併入棧.                         popTo(HomeFragment.class, false, new Runnable() {                             @Override                             public void run() {                                 start(DiscoverFragment.newInstance());                             }                         });                     } else {                         // 若是已經在棧內,則以SingleTask模式start,也能夠用popTo                         start(fragment, SupportFragment.SINGLETASK);                     }                 } else if (id == R.id.nav_msg) {                     ShopFragment fragment = findFragment(ShopFragment.class);                     if (fragment == null) {                         popTo(HomeFragment.class, false, new Runnable() {                             @Override                             public void run() {                                 start(ShopFragment.newInstance());                             }                         });                     } else {                         // 若是已經在棧內,則以SingleTask模式start,也能夠用popTo                         popTo(ShopFragment.class, false);                     }                 }      } }

相關文章
相關標籤/搜索