Fragment 創(chuàng)建和生命周期

<meta charset="utf-8">

<article class="_2rhmJa">

new 一個Fragment,F(xiàn)ragment只是調(diào)用了自身的空參數(shù)構造方法翁潘,并沒有其他操作描馅。
  * Fragment要執(zhí)行其onAttach()及其之后的生命周期方法攒至,需要被FragmentTransition記錄并真正提交到FragmentManager處刽辙,才能實現(xiàn)玛追。
  * 如果Fragment要準備顯示了(即被事務提交到FragmentManager,具體后面會說到)俱萍,那么它再繼續(xù)執(zhí)行onCreateView()到onResume()的方法端壳,去創(chuàng)建和綁定UI,最后在前臺顯示枪蘑。
  * 由于ViewPager在Activity.onCreate()中并沒有真正初始化界面完成损谦,即在onCreate()階段中,ViewPager適配器內(nèi)部并沒有真的調(diào)用Fragment的構造方法并從FragmentManager中獲取FragmentTransaction事務來記錄Fragment的操作岳颇,等到Activity.onResume()方法被執(zhí)行(即Activity啟動完成并在前臺顯示了)照捡,這時,ViewPager才選取默認要顯示的那一頁话侧,并對應執(zhí)行這一頁的Fragment的創(chuàng)建+事務添加+提交給Manager的一個過程栗精。
 * 在Activity被停止或即將被銷毀的過程中,都首先停止或銷毀它內(nèi)部的所有Fragment瞻鹏,而它內(nèi)部的所有Fragment則各自檢查自己是否有置于前臺或顯示悲立,如果有,那就得destroyView()銷毀掉它所綁定的View布局并使自己處于完全不可見狀態(tài)乙漓,再判斷地執(zhí)行onDestroy()级历。
 * 對于最后一個例子,F(xiàn)ragment執(zhí)行自身的startActivityForResult()和getActivity().startActivityForResult()是有區(qū)別的:如果執(zhí)行自身的startActivityForResult()叭披,那么在另一個Activity返回時,F(xiàn)ragment就會執(zhí)行自身的onActivityResult()方法玩讳,否則涩蜘,只會觸發(fā)Activity的onActivityResult(),除非你在Activity的onActivityResult()方法中添加幾行代碼熏纯,讓它把結果也返回給指定Fragment同诫,如:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (fragmentA!=null)
fragmentA.onActivityResult(requestCode,resultCode,data);

if (fragmentB!=null)
    fragmentB.onActivityResult(requestCode,resultCode,data);

}


Fragment開發(fā)遇到的問題和解決(Fragment注意點)


一、Fragment 與 后臺事務棧管理

前面說了Fragment的幾個生命周期方法可能不止執(zhí)行一次樟澜,關鍵點就在于Fragment是否被事務管理到后臺棧中误窖,這里就涉及到與Fragment有關的幾個類的相關方法:

  • FragmentTransaction.addToBackStack(String)【關鍵:讓攜帶著Fragment記錄的事務保存到后臺棧中】
  • FragmentActivity.onBackPressed()【點擊BACK按鍵或者其他方式觸發(fā)Activity的回退方法時,會促發(fā)此方法秩贰,F(xiàn)ragmentActivity的此方法中會多加一個判斷霹俺,看后臺棧中是否存在事務,存在毒费,則一個事務出棧丙唧,這個事務對應的Fragment操作記錄則被回退清空,也就是這個事務下Fragment的操作觅玻,全部撤銷想际,F(xiàn)ragment會從FragmentManager維護的Fragment隊列中拿出并被銷毀從而執(zhí)行它的生命周期的余下方法】
  • FragmentManager.popBackStack()【Manager管理的后臺棧的一個出棧操作培漏,返回一個FragmentTransaction事務,但是此操作要等到Application返回它是事件loop時才會觸發(fā)】
  • FragmentManager.popBackStackImmediate()【與popBackStack()方法差不多胡本,只是它是立即執(zhí)行出棧操作牌柄,而不用顧忌Application】

下面舉個例子,給大家看看Fragment是怎么跑的:
【這里使用FragmentTransaction.replace()作為主要Fragment操作】

原因:
?add()方法與replace()方法大多數(shù)情況下效果是一樣的侧甫,add是指“添加”珊佣,replace是指“替換”,一般使用同一個FrameLayout去加載Fragment時闺骚,推薦是用replace()的彩扔,省去add的多層Fragment重疊,當然僻爽,在需要進行輪播等需要及時看到多個Fragment的時候虫碉,add()比較好的。
?remove()胸梆、hide()敦捧、show()、detach()碰镜、attach()等方法兢卵,都相應地只是執(zhí)行對應Fragment的對應生命周期方法而已
?所以這里用replace()做演示更好。

主要執(zhí)行的代碼:

  /***
   * 方式一:單個Fragment 做下記錄到backStack
   *
   * @param baseFragment
   */
  public void onAddFragment(Fragment baseFragment) {
      FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
      ft.replace(R.id.frame_root_fragment, baseFragment, "tag");
      ft.addToBackStack("tag");
      ft.commit();
  }

  /**
   * 方式二:多個Fragment同時addToBackStack
   *
   * @param fragmentList
   */
  public void onAddFragments(Fragment[] fragmentList) {
      FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
      int pos = 0;
      for (Fragment item : fragmentList) {
          pos += 1;
          ft.replace(R.id.frame_root_fragment, item, "tag" + pos);
          ft.addToBackStack("tag" + pos);
      }
      ft.commit();
  }

gif圖:

  1. 執(zhí)行方式一代碼(事務一對一Fragment)

    [圖片上傳失敗...(image-a1a51a-1594347282698)]

  • Log截圖:

    [圖片上傳失敗...(image-5378c-1594347282698)]

  1. 執(zhí)行方式二代碼(事務一對多Fragments)

    [圖片上傳失敗...(image-9193fe-1594347282698)]

  • Log截圖:

    [圖片上傳失敗...(image-a7b73b-1594347282698)]

    得出結論:

    • 從代碼可以看到绪颖,F(xiàn)ragmentTransaction可以記錄一到多個Fragment的相關操作秽荤。
    • 示例可以知道,F(xiàn)ragmentActivity回退事件發(fā)生時柠横,會先把所有的FragmentTransaction事務一一彈出后臺棧先窃款。【如果一個事務對應一個Fragment牍氛,那么這里就實現(xiàn)了一個Fragment之間的跳轉(zhuǎn)過程晨继;而如果一個事務對應多個Fragment,那么搬俊,一個事務彈出紊扬,它涉及到的后臺隊列中Fragment集合便會一下子都彈出銷毀,而不是一個個Fragment地出隊】

二唉擂、Fragment以及它的宿主Activity的復用

在平時開發(fā)中餐屎,怎么樣方便開發(fā)、方便維護楔敌、盡量解耦啤挎,就怎么寫代碼,這里給一個比較好的Fragment寫法例子(參考鴻洋博客中的一個Fragment例子),供參考:

  public class ContentFragment extends Fragment  
  {  
      private String mArgument;///Activity傳遞的數(shù)據(jù)(值)
      public static final String ARGUMENT = "argument";///Activity傳遞的數(shù)據(jù)名(鍵)  
      public static final String RESPONSE = "response";///Activity

      @Override  
      public void onCreate(Bundle savedInstanceState)  
      {  
          super.onCreate(savedInstanceState);  
          Bundle bundle = getArguments();  
          if (bundle != null)  
          {  
              mArgument = bundle.getString(ARGUMENT);  
              Intent intent = new Intent();  
              intent.putExtra(RESPONSE, "good");  
              getActivity().setResult(ListTitleFragment.REQUEST_DETAIL, intent);  
          }  

      }  
      ////在實例化時獲取Activity傳入的值(這里示例為String類型)
      public static ContentFragment newInstance(String argument)  
      {  
          Bundle bundle = new Bundle();  
          bundle.putString(ARGUMENT, argument);  
          ContentFragment contentFragment = new ContentFragment();  
          contentFragment.setArguments(bundle);  
          return contentFragment;  
      }  

      @Override  
      public View onCreateView(LayoutInflater inflater, ViewGroup container,  
              Bundle savedInstanceState)  
      {  
          Random random = new Random();  
          TextView tv = new TextView(getActivity());  
          ///.........
          return tv;  
      }  
  }  

其實寫法不一庆聘,可能有很多更加靈活的寫法胜臊,但本文不深究,只要大家有“Fragment的復用”這個思想伙判,旨在寫出容易復用象对、與Activity耦合度小、存在與外界通信接口的Fragment宴抚,就能夠?qū)嶋H減少工作量勒魔、理清思路。

下面是一個抽象Activity菇曲,用于簡單狀態(tài)Fragment 的Activity自身代碼的復用冠绢,可參考:

public abstract class SingleFragmentActivity extends FragmentActivity  {  
  protected abstract Fragment createFragment();  
  @Override  
  protected void onCreate(Bundle savedInstanceState) {  
      super.onCreate(savedInstanceState);  
      setContentView(R.layout.activity_single_fragment);  
      FragmentManager fm = getSupportFragmentManager();  
      Fragment fragment =fm.findFragmentById(R.id.id_fragment_container);  
      if(fragment == null )  
      {  
          fragment = createFragment() ;  
          fm.beginTransaction().add(R.id.id_fragment_container,fragment).commit();  
      }  
  }  
}  

三、關于DialogFragment

Android3.0被引入的一類特殊Fragment常潮,方便我們構建具有和Fragment一樣生命周期的一類Dialog等組件弟胀,從而解決普通的Dialog等組件難以管理它的生命周期、與Activity喊式、Fragment交互的限制孵户。詳細可參考Android 官方推薦 : DialogFragment 創(chuàng)建對話框

四、Fragment與外界的通信

理解了Fragment生命周期和它的基本寫法岔留,那么夏哭,再說說Fragment與外界的通信。上面提到的DialogFragment就是用于優(yōu)化通信和管理的献联,那么竖配,平常我們使用Fragment,應該如何保證與其他Fragment和外部Activity進行通信呢里逆?

  1. Fragment與它的宿主Activity通信:
* (Activity傳給Fragment)Activity通過`Fragment.setArguments(Bundle)`在創(chuàng)建Fragment時傳遞數(shù)據(jù)給Fragment械念,在Fragment的onCreate()中,F(xiàn)ragment通過`getArguments()`獲取Bundle數(shù)據(jù)运悲。
* (Activity傳給Fragment)Activity通過`getSupportFragmentManager().findFragmentById()`或`getSupportFragmentManager().findFragmentByTag()`獲取Fragment并調(diào)用Fragment自己定義的方法,數(shù)據(jù)通過參數(shù)形式傳給Fragment项钮。
* (Fragment傳給Activity)Fragment通過`getActivity()`方式獲取宿主Activity班眯,就可以調(diào)用Activity的方法【但是這樣不嚴謹,F(xiàn)ragment能夠調(diào)用的方法烁巫,應該要受到限定署隘,所以,使用如下代碼的方式亚隙,通過在Fragment中定義一個接口磁餐,然后讓宿主Activity實現(xiàn)這些方法】

public static class FragmentA extends ListFragment{
OnArticleSelectedListener mListener;
...
@Override
public void onAttach(Activity activity){
super.onAttach(activity);
try{
mListener =(OnArticleSelectedListener)activity;
}catch(ClassCastException e){
throw new ClassCastException(activity.toString()+"must implement OnArticleSelectedListener");
}
}
...
`* (Activity傳給Fragment、Fragment傳Fragment)還可以通過廣播的方式,讓Fragment去注冊廣播诊霹,然后羞延,F(xiàn)ragment調(diào)用`getActivity().sendBroadcast(Intent)`或者Activity調(diào)用`sendBroadcast(Intent)`來發(fā)出廣播,讓注冊了該廣播的Fragment去接收并過濾廣播信息【然而這樣做有些小題大做脾还,而且需考慮廣播的注冊等】 *<u>關鍵代碼</u>*:`
public class FragmentA extends Fragment{
MyBroadcast broadcast;
//.........
@Override
public View onCreateView(LayoutInflater inflater
, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
//........
IntentFilter filter = new IntentFilter();
try {
if (mReceiver != null) {
getActivity().unregisterReceiver(broadcast);
}
} catch (Exception e) {
e.printStackTrace();
}

broadcast = new MyBroadcast();
filter.addAction(PageOneFragment.DATA_CHANGED);
getActivity().registerReceiver(broadcast, filter);

//........


}
//..................
class MyBroadcast extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(PageOneFragment.DATA_CHANGED)) {
///伴箩。。鄙漏。嗤谚。。
}
}
}
}
  1. Fragment之間的通信:
* 在同一個Activity下的兩個Fragment的通信:FragmentA調(diào)用其宿主Activity的方法怔蚌,宿主Activity再根據(jù)FragmentA的調(diào)用參數(shù)去調(diào)用FragmentB的方法并傳遞參數(shù)給B巩步。
* 不同Activity下的兩個Fragment的通信:
  * 首先能夠保證兩者都能夠執(zhí)行到onActivityResult()方法【文章前面有說到】
  * (A傳給B)FragmentA通過startActivityForResult()【調(diào)用Fragment本身或者getActivity()都可以】的方式,把Intent數(shù)據(jù)等傳到另一個Activity桦踊,然后讓另一個Activity傳值給FragmentB
  * (B返回給A)FragmentB處理完椅野,通過getActivity().setResult()把返回的數(shù)據(jù)設置好,然后返回Activity钞钙,A再從它的onActivityResult中拿鳄橘。
* 同一個Activity下,DialogFragment 與 Fragment的交互:
  > 原理:FragmentA中new一個DialogFragment對象芒炼,并讓其`setTargetFragment()`來綁定目的Fragment瘫怜,等DialogFragment處理完數(shù)據(jù),調(diào)用剛剛綁定的FragmentA的`onActivityResult()`把數(shù)據(jù)傳回給FragmetnA本刽。

上關鍵代碼:

  *<u>Fragment 的代碼</u>*

public class ContentFragment extends Fragment
{
//...
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
//....
tv.setOnClickListener(new OnClickListener()
{

        @Override  
        public void onClick(View v)  
        {  
            EvaluateDialog dialog = new EvaluateDialog();  
            //注意setTargetFragment  
            dialog.setTargetFragment(ContentFragment.this, REQUEST_EVALUATE);  
            dialog.show(getFragmentManager(), EVALUATE_DIALOG);  
        }  
    });  
    return tv;  
}  

//接收返回回來的數(shù)據(jù)  
@Override  
public void onActivityResult(int requestCode, int resultCode, Intent data)  
{  
    super.onActivityResult(requestCode, resultCode, data);  

    if (requestCode == REQUEST_EVALUATE)  
    {  
        String evaluate = data  
                .getStringExtra(EvaluateDialog.RESPONSE_EVALUATE);  
        Toast.makeText(getActivity(), evaluate, Toast.LENGTH_SHORT).show();  
        Intent intent = new Intent();  
        intent.putExtra(RESPONSE, evaluate);  
        getActivity().setResult(Activity.REQUEST_OK, intent);  
    }  
}  

}

*<u>DialogFragment 的代碼</u>*
  ```

public class EvaluateDialog extends DialogFragment
{
private String[] mEvaluteVals = new String[] { "GOOD", "BAD", "NORMAL" };
public static final String RESPONSE_EVALUATE = "response_evaluate";
@Override
public Dialog onCreateDialog(Bundle savedInstanceState)
{
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle("Evaluate :").setItems(mEvaluteVals,
new OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
setResult(which);
}
});
return builder.create();
}
// 設置返回數(shù)據(jù)
protected void setResult(int which)
{
// 判斷是否設置了targetFragment
if (getTargetFragment() == null)
return;
Intent intent = new Intent();
intent.putExtra(RESPONSE_EVALUATE, mEvaluteVals[which]);
getTargetFragment().onActivityResult(ContentFragment.REQUEST_EVALUATE,
Activity.RESULT_OK, intent);
}
}


> 補充:關于DialogFragment調(diào)整窗口大小鲸湃,可以參考[這篇文章](https://link.jianshu.com?t=http://blog.csdn.net/angcyo/article/details/50613084)

## 感謝閱讀

* * *

對于Fragment的小小講解,就到此結束了子寓,之后會慢慢深入暗挑,謝謝大家支持,喜歡的讀者可以關注我一波~~

推薦閱讀:
?本人的幾篇文章:
[Android開發(fā)細節(jié)--查漏補缺(二):易忘難懂](http://www.reibang.com/p/c0a4fa16b8d6)
[Java面試相關(一)-- Java類加載全過程](http://www.reibang.com/p/ace2aa692f96)
?Fragment相關文章:
[http://blog.csdn.net/lmj623565791/article/details/37815413](https://link.jianshu.com?t=http://blog.csdn.net/lmj623565791/article/details/37815413)
[http://blog.csdn.net/lmj623565791/article/details/42628537/](https://link.jianshu.com?t=http://blog.csdn.net/lmj623565791/article/details/42628537/)
[http://www.cnblogs.com/android-joker/p/4414891.html](https://link.jianshu.com?t=http://www.cnblogs.com/android-joker/p/4414891.html)

</article>

104人點贊

[Android組件與UI](/nb/5332097)


[http://www.reibang.com/p/1ff18ec1fb6b](http://www.reibang.com/p/1ff18ec1fb6b)
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末斜友,一起剝皮案震驚了整個濱河市炸裆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌鲜屏,老刑警劉巖烹看,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異洛史,居然都是意外死亡惯殊,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進店門也殖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來土思,“玉大人,你說我怎么就攤上這事〖喝澹” “怎么了崎岂?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長址愿。 經(jīng)常有香客問我该镣,道長,這世上最難降的妖魔是什么响谓? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任损合,我火速辦了婚禮,結果婚禮上娘纷,老公的妹妹穿的比我還像新娘嫁审。我一直安慰自己,他們只是感情好赖晶,可當我...
    茶點故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布律适。 她就那樣靜靜地躺著,像睡著了一般遏插。 火紅的嫁衣襯著肌膚如雪捂贿。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天胳嘲,我揣著相機與錄音厂僧,去河邊找鬼。 笑死了牛,一個胖子當著我的面吹牛颜屠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播鹰祸,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼甫窟,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蛙婴?” 一聲冷哼從身側(cè)響起粗井,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎街图,沒想到半個月后背传,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡台夺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了痴脾。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖怪得,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情冤灾,我是刑警寧澤,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布辕近,位于F島的核電站韵吨,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏移宅。R本人自食惡果不足惜归粉,卻給世界環(huán)境...
    茶點故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望漏峰。 院中可真熱鬧糠悼,春花似錦、人聲如沸浅乔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽靖苇。三九已至席噩,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間贤壁,已是汗流浹背悼枢。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留芯砸,地道東北人萧芙。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像假丧,于是被迫代替她去往敵國和親双揪。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,490評論 2 348