溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何使用newInstance()方法來實例化fragment實現(xiàn)數(shù)據(jù)傳遞

發(fā)布時間:2020-11-06 16:51:31 來源:億速云 閱讀:408 作者:Leah 欄目:開發(fā)技術(shù)

如何使用newInstance()方法來實例化fragment實現(xiàn)數(shù)據(jù)傳遞?很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。

newInstance()方法是一種“靜態(tài)工廠方法",讓我們在初始化和設(shè)置一個新的fragment的時候省去調(diào)用它的構(gòu)造函數(shù)和額外的setter方法。

為你的Fragment提供靜態(tài)工廠方法是一種好的做法,因為它封裝和抽象了在客戶端構(gòu)造對象所需的步驟。

例如,考慮下面的代碼:

 public class MyFragment extends Fragment { 
  
  /** 
  * 靜態(tài)工廠方法需要一個int型的值來初始化fragment的參數(shù), 
  * 然后返回新的fragment到調(diào)用者 
  */ 
  public static MyFragment newInstance(int index) { 
   MyFragment f = new MyFragment(); 
   Bundle args = new Bundle(); 
   args.putInt("index", index); 
   f.setArguments(args); 
   return f; 
  } 
  } 

不要讓客戶端去調(diào)用默認(rèn)的構(gòu)造函數(shù),然后手動地設(shè)置fragment的參數(shù)。我們直接為它們提供一個靜態(tài)工廠方法。這樣做比調(diào)用默認(rèn)構(gòu)造方法好,有兩個原因:一個是,它方便別人的調(diào)用。另一個是,保證了fragment的構(gòu)建過程不會出錯。通過提供一個靜態(tài)工廠方法,我們避免了自己犯錯--我們再也不用擔(dān)心不小心忘記初始化fragmnet的參數(shù)或者沒正確設(shè)置參數(shù)。

總的來說,雖然兩者的區(qū)別只在于設(shè)計,但是他們之間的差別非常大。因為提供靜態(tài)工廠方法有向上抽象了一個級別,讓代碼更容易懂。

譯者注:

其實提供靜態(tài)工廠而不是使用默認(rèn)構(gòu)造函數(shù)或者自己定義一個有參的構(gòu)造函數(shù)還有至關(guān)重要一點。fragmnet經(jīng)常會被銷毀重新實例化,Android framework只會調(diào)用fragment無參的構(gòu)造函數(shù)。在系統(tǒng)自動實例化fragment的過程中,你沒有辦法干預(yù)。一些需要外部傳入的參數(shù)來決定的初始化就沒有辦法完成。使用靜態(tài)工廠方法,將外部傳入的參數(shù)可以通過Fragment.setArgument保存在它自己身上,這樣我們可以在Fragment.onCreate(...)調(diào)用的時候?qū)⑦@些參數(shù)取出來。

傳遞數(shù)據(jù)

 public static LoginFragment newInstance(String param) {
  LoginFragment fragment = new LoginFragment();
  Bundle args = new Bundle();
  args.putString("name", param);
  fragment.setArguments(args);
  return fragment;
 }

在fragment 的onCreatView里獲取數(shù)據(jù)

@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment View myView = inflater.inflate(R.layout.xxx, container, false); String args = getArguments().getString("name"); return myView;}

在Activity里

  LoginFragment loginFragment= LoginFragment.newInstance(想要傳遞的參數(shù));
  SignUpFragment signUpFragment= SignUpFragment.newInstance(想要傳遞的參數(shù));
 
  List<Fragment> allFragment = new ArrayList<Fragment>();
  allFragment.add(loginFragment);
  allFragment.add(signUpFragment);

補(bǔ)充知識:正確使用Fragment之創(chuàng)建/傳參——newInstance方法(native)

說來懺愧,近來越發(fā)覺得寫不出可分享的東西,更糟糕的是,甚至覺得可記錄的東西都不多。

這實在是一個非常糟的信號——說明我開始逐漸把自己放在安全邊際內(nèi)了。

人若總是將自己畏縮在安全邊際之內(nèi),不去做一些陣痛的改變,埋下的會是病來如山倒般的災(zāi)難種子。

好在,好在我還在不斷的學(xué)習(xí),只是但前處于一種較混沌的狀態(tài),需要踏出去更多一步。

今天來說一個簡單的話題,找回一些狀態(tài)。

關(guān)于Fragment,相信大家已經(jīng)熟之不能再熟了。然而,

使用頻率如此之高的Fragment,你的使用姿勢,真的正確嗎?

先對比一下兩種使用姿勢:

1.姿勢A:

MyFragment mFragment = new MyFragment();
   Bundle bundle = new Bundle();
   bundle.putString("arg1", "a");
   bundle.putString("arg2", "b");
   bundle.putString("arg3", "c");
   mFragment.setArguments(bundle);
   getSupportFragmentManager().beginTransaction().replace(R.id.frame, mFragment).commit();

2.姿勢B:

MyFragment mFragment = MyFragment.newInstance("a", "b","c");

getSupportFragmentManager().beginTransaction().replace(R.id.frame, mFragment).commit();

有沒有,有沒有覺得第二種姿勢特別爽。

接來下進(jìn)入今天的正題,關(guān)于Fragment.newInstance()這個方法。

我先聲明,其實第一種姿勢沒什么問題,(引用斯坦福白胡子老頭一句話)”這只是代碼風(fēng)格的問題,但我不建議這么做?!?/p>

使用Android Studio新建一個Fragment就一切明了了:

我們看到,Studio默認(rèn)幫我們創(chuàng)建的Fragment中,有這樣一段代碼:

// TODO: Rename and change types and number of parameters
public static BlankFragment newInstance(String param1, String param2) {
 BlankFragment fragment = new BlankFragment();
 Bundle args = new Bundle();
 args.putString(ARG_PARAM1, param1);
 args.putString(ARG_PARAM2, param2);
 fragment.setArguments(args);
 return fragment;
}

一個靜態(tài)方法,返回我們創(chuàng)建的Fragment類本身,顯而易見的是,這個方法幫我們做了姿勢A中我們手寫的方法。

再來關(guān)注看我們較少Override的方法onCreate(這里默認(rèn)直接幫我們Override了)

@Override
public void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 if (getArguments() != null) {
  mParam1 = getArguments().getString(ARG_PARAM1);
  mParam2 = getArguments().getString(ARG_PARAM2);
 }
}

到這里,我們先捋一捋。姿勢B的機(jī)理在于,通過傳遞參數(shù)給Fragment.newInstance()方法,它會創(chuàng)建一個該Fragment類,并通過創(chuàng)建Bundle把我們的參數(shù)代入。然后在onCreate()生命周期中,把參數(shù)拿回出來。(為什么這么做?是本文后半部分傳參討論的內(nèi)容,先跳過),之后的事情大家都是熟手了,把參數(shù)拿來用就好。

為什么谷歌默認(rèn)要使用這樣一個工廠方式創(chuàng)建我們的Fragment呢?

既然newInstance()是父類Fragment的方法,我們跟進(jìn)去一看究竟:

//可以看到這是一個native方法

public native T newInstance() throws InstantiationException, IllegalAccessException;

題外話:關(guān)于native方法: native關(guān)鍵字說明其修飾的方法是一個原生態(tài)方法,方法對應(yīng)的實現(xiàn)不是在當(dāng)前文件,而是在用其他語言(如C和C++)實現(xiàn)的文件中。Java語言本身不能對操作系統(tǒng)底層進(jìn)行訪問和操作,但是可以通過JNI接口調(diào)用其他語言來實現(xiàn)對底層的訪問。

就感覺線索斷了一樣,這是要往下去讀C/C++啊,拋開底層機(jī)理不知,不說,姑且猜測為,二者完全是一樣的方式,只是姿勢B封裝了一點內(nèi)容,讓Fragment的宿主Activity更加整潔一些,僅此而已。

既然如此,我們轉(zhuǎn)為本文的下半部分,關(guān)于傳參。

想過嗎?Fragment作為java類

為什么傳參需要用Fragment.setArguments(bundle)這樣的方式,

而不通過構(gòu)造函數(shù)直接傳遞new Fragment(arg1,arg2);

實踐出真知,其實在大多數(shù)時候,這兩種方法傳遞參數(shù)都是沒有問題的。

但是,但是當(dāng)某些情景發(fā)生,一切就不一樣了。(比如豎屏切換橫屏?xí)r),切換到橫屏?xí)r,構(gòu)造方法傳遞的參數(shù)就找不到了。

原因很簡單,因為Fragment是有自己封裝的生命周期的,這一點和Activity類似,Activity傳參也不是用構(gòu)造方法的方式。

但是究竟生命周期對構(gòu)造方法傳遞參數(shù)有什么影響呢?

源碼中一探究竟:

在Fragment中,是通過Bundle來保存參數(shù)的,它的私有聲明在此:

Bundle mArguments;

順著這個聲明的命名mArguments找下去,發(fā)現(xiàn)其實相關(guān)的主要方法并不多:

 public FragmentState(Fragment frag) {
 ...
 mArguments = frag.mArguments;
 ...
}
 public void setArguments(Bundle args) {
  if (mIndex >= 0) {
   throw new IllegalStateException("Fragment already active");
  }
  mArguments = args;
 }
 final public Bundle getArguments() {
  return mArguments;
 }

這三個比較簡單,就不說了

public Fragment instantiate(FragmentHostCallback host, Fragment parent,
   FragmentManagerNonConfig childNonConfig) {
  if (mInstance == null) {
   final Context context = host.getContext();
   if (mArguments != null) {
    mArguments.setClassLoader(context.getClassLoader());
   }
   mInstance = Fragment.instantiate(context, mClassName, mArguments);

   if (mSavedFragmentState != null) {
    mSavedFragmentState.setClassLoader(context.getClassLoader());
    mInstance.mSavedFragmentState = mSavedFragmentState;
   }
   mInstance.setIndex(mIndex, parent);
   mInstance.mFromLayout = mFromLayout;
   mInstance.mRestored = true;
   mInstance.mFragmentId = mFragmentId;
   mInstance.mContainerId = mContainerId;
   mInstance.mTag = mTag;
   mInstance.mRetainInstance = mRetainInstance;
   mInstance.mDetached = mDetached;
   mInstance.mHidden = mHidden;
   mInstance.mFragmentManager = host.mFragmentManager;

   if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,
     "Instantiated fragment " + mInstance);
  }
  mInstance.mChildNonConfig = childNonConfig;
  return mInstance;
 }

在instantiate()實例化過程中,可以看到

if (mArguments != null) {
mArguments.setClassLoader(context.getClassLoader());
}

也就是說,如果我們調(diào)用時使用setArguments()傳遞了Bundle,它會被保存在mArguments 這個私有聲明中。

而如果是通過構(gòu)造函數(shù)傳遞的參數(shù),那很不幸,F(xiàn)ragment重建過程中,并沒有持有相應(yīng)參數(shù)的屬性或方法,自然,你通過構(gòu)造函數(shù)傳遞的參數(shù)就丟失了。

其實目前大家單純無參new Fragment()的方式并沒有錯,只是可以讓Activity更優(yōu)雅的調(diào)用Fragment.newInstance(),

而如果涉及到傳遞參數(shù),萬不可通過構(gòu)造函數(shù)傳遞,會丟失。

知其然,知其所以然

總結(jié),F(xiàn)ragment.newInstance() ,別無其他,只是事關(guān)風(fēng)格(代碼”整”“潔”之道),建議大家以后均使用谷歌推薦的該方法

看完上述內(nèi)容是否對您有幫助呢?如果還想對相關(guān)知識有進(jìn)一步的了解或閱讀更多相關(guān)文章,請關(guān)注億速云行業(yè)資訊頻道,感謝您對億速云的支持。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI