使用newInstance()來實(shí)例化fragment并傳遞數(shù)據(jù)操作
好問題。答案就是這篇文章的題目所建議的,這是一種合理的設(shè)計(jì)。在這種情況下,newInstance()方法是一種“靜態(tài)工廠方法",讓我們在初始化和設(shè)置一個(gè)新的fragment的時(shí)候省去調(diào)用它的構(gòu)造函數(shù)和額外的setter方法。
為你的Fragment提供靜態(tài)工廠方法是一種好的做法,因?yàn)樗庋b和抽象了在客戶端構(gòu)造對象所需的步驟。
例如,考慮下面的代碼:
public class MyFragment extends Fragment { /** * 靜態(tài)工廠方法需要一個(gè)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ù),然后手動(dòng)地設(shè)置fragment的參數(shù)。我們直接為它們提供一個(gè)靜態(tài)工廠方法。這樣做比調(diào)用默認(rèn)構(gòu)造方法好,有兩個(gè)原因:一個(gè)是,它方便別人的調(diào)用。另一個(gè)是,保證了fragment的構(gòu)建過程不會(huì)出錯(cuò)。通過提供一個(gè)靜態(tài)工廠方法,我們避免了自己犯錯(cuò)--我們再也不用擔(dān)心不小心忘記初始化fragmnet的參數(shù)或者沒正確設(shè)置參數(shù)。
總的來說,雖然兩者的區(qū)別只在于設(shè)計(jì),但是他們之間的差別非常大。因?yàn)樘峁╈o態(tài)工廠方法有向上抽象了一個(gè)級別,讓代碼更容易懂。
譯者注:
其實(shí)提供靜態(tài)工廠而不是使用默認(rèn)構(gòu)造函數(shù)或者自己定義一個(gè)有參的構(gòu)造函數(shù)還有至關(guān)重要一點(diǎn)。fragmnet經(jīng)常會(huì)被銷毀重新實(shí)例化,Android framework只會(huì)調(diào)用fragment無參的構(gòu)造函數(shù)。在系統(tǒng)自動(dòng)實(shí)例化fragment的過程中,你沒有辦法干預(yù)。一些需要外部傳入的參數(shù)來決定的初始化就沒有辦法完成。使用靜態(tài)工廠方法,將外部傳入的參數(shù)可以通過Fragment.setArgument保存在它自己身上,這樣我們可以在Fragment.onCreate(...)調(diào)用的時(shí)候?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ā)覺得寫不出可分享的東西,更糟糕的是,甚至覺得可記錄的東西都不多。
這實(shí)在是一個(gè)非常糟的信號——說明我開始逐漸把自己放在安全邊際內(nèi)了。
人若總是將自己畏縮在安全邊際之內(nèi),不去做一些陣痛的改變,埋下的會(huì)是病來如山倒般的災(zāi)難種子。
好在,好在我還在不斷的學(xué)習(xí),只是但前處于一種較混沌的狀態(tài),需要踏出去更多一步。
今天來說一個(gè)簡單的話題,找回一些狀態(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()這個(gè)方法。
我先聲明,其實(shí)第一種姿勢沒什么問題,(引用斯坦福白胡子老頭一句話)”這只是代碼風(fēng)格的問題,但我不建議這么做?!?/p>
使用Android Studio新建一個(gè)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; }
一個(gè)靜態(tài)方法,返回我們創(chuàng)建的Fragment類本身,顯而易見的是,這個(gè)方法幫我們做了姿勢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()方法,它會(huì)創(chuàng)建一個(gè)該Fragment類,并通過創(chuàng)建Bundle把我們的參數(shù)代入。然后在onCreate()生命周期中,把參數(shù)拿回出來。(為什么這么做?是本文后半部分傳參討論的內(nèi)容,先跳過),之后的事情大家都是熟手了,把參數(shù)拿來用就好。
為什么谷歌默認(rèn)要使用這樣一個(gè)工廠方式創(chuàng)建我們的Fragment呢?
既然newInstance()是父類Fragment的方法,我們跟進(jìn)去一看究竟:
//可以看到這是一個(gè)native方法
public native T newInstance() throws InstantiationException, IllegalAccessException;
題外話:關(guān)于native方法: native關(guān)鍵字說明其修飾的方法是一個(gè)原生態(tài)方法,方法對應(yīng)的實(shí)現(xiàn)不是在當(dāng)前文件,而是在用其他語言(如C和C++)實(shí)現(xiàn)的文件中。Java語言本身不能對操作系統(tǒng)底層進(jìn)行訪問和操作,但是可以通過JNI接口調(diào)用其他語言來實(shí)現(xiàn)對底層的訪問。
就感覺線索斷了一樣,這是要往下去讀C/C++啊,拋開底層機(jī)理不知,不說,姑且猜測為,二者完全是一樣的方式,只是姿勢B封裝了一點(diǎn)內(nèi)容,讓Fragment的宿主Activity更加整潔一些,僅此而已。
既然如此,我們轉(zhuǎn)為本文的下半部分,關(guān)于傳參。
想過嗎?Fragment作為java類
為什么傳參需要用Fragment.setArguments(bundle)這樣的方式,
而不通過構(gòu)造函數(shù)直接傳遞new Fragment(arg1,arg2);
實(shí)踐出真知,其實(shí)在大多數(shù)時(shí)候,這兩種方法傳遞參數(shù)都是沒有問題的。
但是,但是當(dāng)某些情景發(fā)生,一切就不一樣了。(比如豎屏切換橫屏?xí)r),切換到橫屏?xí)r,構(gòu)造方法傳遞的參數(shù)就找不到了。
原因很簡單,因?yàn)镕ragment是有自己封裝的生命周期的,這一點(diǎn)和Activity類似,Activity傳參也不是用構(gòu)造方法的方式。
但是究竟生命周期對構(gòu)造方法傳遞參數(shù)有什么影響呢?
源碼中一探究竟:
在Fragment中,是通過Bundle來保存參數(shù)的,它的私有聲明在此:
Bundle mArguments;
順著這個(gè)聲明的命名mArguments找下去,發(fā)現(xiàn)其實(shí)相關(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; }
這三個(gè)比較簡單,就不說了
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()實(shí)例化過程中,可以看到
if (mArguments != null) { mArguments.setClassLoader(context.getClassLoader()); }
也就是說,如果我們調(diào)用時(shí)使用setArguments()傳遞了Bundle,它會(huì)被保存在mArguments 這個(gè)私有聲明中。
而如果是通過構(gòu)造函數(shù)傳遞的參數(shù),那很不幸,F(xiàn)ragment重建過程中,并沒有持有相應(yīng)參數(shù)的屬性或方法,自然,你通過構(gòu)造函數(shù)傳遞的參數(shù)就丟失了。
其實(shí)目前大家單純無參new Fragment()的方式并沒有錯(cuò),只是可以讓Activity更優(yōu)雅的調(diào)用Fragment.newInstance(),
而如果涉及到傳遞參數(shù),萬不可通過構(gòu)造函數(shù)傳遞,會(huì)丟失。
知其然,知其所以然
總結(jié),F(xiàn)ragment.newInstance() ,別無其他,只是事關(guān)風(fēng)格(代碼”整”“潔”之道),建議大家以后均使用谷歌推薦的該方法
以上這篇使用newInstance()來實(shí)例化fragment并傳遞數(shù)據(jù)操作就是小編分享給大家的全部內(nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Android Okhttp斷點(diǎn)續(xù)傳面試深入解析
這篇文章主要給大家介紹了關(guān)于Android Okhttp斷點(diǎn)續(xù)傳面試的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用Android具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06Flutter實(shí)現(xiàn)文本滾動(dòng)高亮效果的示例講解
這篇文章主要介紹了如何利用Flutter時(shí)時(shí)渲染頁面從而達(dá)到文本滾動(dòng)高亮的效果,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2022-03-03Android入門之利用Spinner實(shí)現(xiàn)彈出選擇對話框
這篇文章主要為大家詳細(xì)介紹了Android里如何巧用Spinner做彈出選擇對話框,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,感興趣的小伙伴可以了解一下2022-11-11Android中PathMeasure仿支付寶支付動(dòng)畫
這篇文章主要為大家詳細(xì)介紹了Android中PathMeasure仿支付寶支付動(dòng)畫,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08Android 基于google Zxing實(shí)現(xiàn)二維碼、條形碼掃描,仿微信二維碼掃描效果(推薦)
這篇文章主要介紹了 Android 基于google Zxing實(shí)現(xiàn)二維碼、條形碼掃描,仿微信二維碼掃描效果,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下2017-01-01Android基于API的Tabs3實(shí)現(xiàn)仿優(yōu)酷t(yī)abhost效果實(shí)例
這篇文章主要介紹了Android基于API的Tabs3實(shí)現(xiàn)仿優(yōu)酷t(yī)abhost效果,結(jié)合完整實(shí)例形式分析了Android實(shí)現(xiàn)優(yōu)酷界面效果的相關(guān)技巧,需要的朋友可以參考下2015-12-12Android中通過外部程序啟動(dòng)App的三種方法
這篇文章主要介紹了Android中通過外部程序啟動(dòng)App的三種方法,本文講解了直接通過包名、通過自定義的Action、通過Scheme三種方法,并分別給出操作代碼,需要的朋友可以參考下2015-04-04Flutter?LinearProgressIndicator使用指南分析
這篇文章主要為大家介紹了Flutter?LinearProgressIndicator使用指南分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03