您好,登錄后才能下訂單哦!
如何使用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è)資訊頻道,感謝您對億速云的支持。
免責(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)容。