您好,登錄后才能下訂單哦!
如何使用newInstance()方法來實例化fragment實現數據傳遞?很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。
newInstance()方法是一種“靜態工廠方法",讓我們在初始化和設置一個新的fragment的時候省去調用它的構造函數和額外的setter方法。
為你的Fragment提供靜態工廠方法是一種好的做法,因為它封裝和抽象了在客戶端構造對象所需的步驟。
例如,考慮下面的代碼:
public class MyFragment extends Fragment { /** * 靜態工廠方法需要一個int型的值來初始化fragment的參數, * 然后返回新的fragment到調用者 */ public static MyFragment newInstance(int index) { MyFragment f = new MyFragment(); Bundle args = new Bundle(); args.putInt("index", index); f.setArguments(args); return f; } }
不要讓客戶端去調用默認的構造函數,然后手動地設置fragment的參數。我們直接為它們提供一個靜態工廠方法。這樣做比調用默認構造方法好,有兩個原因:一個是,它方便別人的調用。另一個是,保證了fragment的構建過程不會出錯。通過提供一個靜態工廠方法,我們避免了自己犯錯--我們再也不用擔心不小心忘記初始化fragmnet的參數或者沒正確設置參數。
總的來說,雖然兩者的區別只在于設計,但是他們之間的差別非常大。因為提供靜態工廠方法有向上抽象了一個級別,讓代碼更容易懂。
譯者注:
其實提供靜態工廠而不是使用默認構造函數或者自己定義一個有參的構造函數還有至關重要一點。fragmnet經常會被銷毀重新實例化,Android framework只會調用fragment無參的構造函數。在系統自動實例化fragment的過程中,你沒有辦法干預。一些需要外部傳入的參數來決定的初始化就沒有辦法完成。使用靜態工廠方法,將外部傳入的參數可以通過Fragment.setArgument保存在它自己身上,這樣我們可以在Fragment.onCreate(...)調用的時候將這些參數取出來。
傳遞數據
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里獲取數據
@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(想要傳遞的參數); SignUpFragment signUpFragment= SignUpFragment.newInstance(想要傳遞的參數); List<Fragment> allFragment = new ArrayList<Fragment>(); allFragment.add(loginFragment); allFragment.add(signUpFragment);
補充知識:正確使用Fragment之創建/傳參——newInstance方法(native)
說來懺愧,近來越發覺得寫不出可分享的東西,更糟糕的是,甚至覺得可記錄的東西都不多。
這實在是一個非常糟的信號——說明我開始逐漸把自己放在安全邊際內了。
人若總是將自己畏縮在安全邊際之內,不去做一些陣痛的改變,埋下的會是病來如山倒般的災難種子。
好在,好在我還在不斷的學習,只是但前處于一種較混沌的狀態,需要踏出去更多一步。
今天來說一個簡單的話題,找回一些狀態。
關于Fragment,相信大家已經熟之不能再熟了。然而,
使用頻率如此之高的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();
有沒有,有沒有覺得第二種姿勢特別爽。
接來下進入今天的正題,關于Fragment.newInstance()這個方法。
我先聲明,其實第一種姿勢沒什么問題,(引用斯坦福白胡子老頭一句話)”這只是代碼風格的問題,但我不建議這么做。”
使用Android Studio新建一個Fragment就一切明了了:
我們看到,Studio默認幫我們創建的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; }
一個靜態方法,返回我們創建的Fragment類本身,顯而易見的是,這個方法幫我們做了姿勢A中我們手寫的方法。
再來關注看我們較少Override的方法onCreate(這里默認直接幫我們Override了)
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (getArguments() != null) { mParam1 = getArguments().getString(ARG_PARAM1); mParam2 = getArguments().getString(ARG_PARAM2); } }
到這里,我們先捋一捋。姿勢B的機理在于,通過傳遞參數給Fragment.newInstance()方法,它會創建一個該Fragment類,并通過創建Bundle把我們的參數代入。然后在onCreate()生命周期中,把參數拿回出來。(為什么這么做?是本文后半部分傳參討論的內容,先跳過),之后的事情大家都是熟手了,把參數拿來用就好。
為什么谷歌默認要使用這樣一個工廠方式創建我們的Fragment呢?
既然newInstance()是父類Fragment的方法,我們跟進去一看究竟:
//可以看到這是一個native方法
public native T newInstance() throws InstantiationException, IllegalAccessException;
題外話:關于native方法: native關鍵字說明其修飾的方法是一個原生態方法,方法對應的實現不是在當前文件,而是在用其他語言(如C和C++)實現的文件中。Java語言本身不能對操作系統底層進行訪問和操作,但是可以通過JNI接口調用其他語言來實現對底層的訪問。
就感覺線索斷了一樣,這是要往下去讀C/C++啊,拋開底層機理不知,不說,姑且猜測為,二者完全是一樣的方式,只是姿勢B封裝了一點內容,讓Fragment的宿主Activity更加整潔一些,僅此而已。
既然如此,我們轉為本文的下半部分,關于傳參。
想過嗎?Fragment作為java類
為什么傳參需要用Fragment.setArguments(bundle)這樣的方式,
而不通過構造函數直接傳遞new Fragment(arg1,arg2);
實踐出真知,其實在大多數時候,這兩種方法傳遞參數都是沒有問題的。
但是,但是當某些情景發生,一切就不一樣了。(比如豎屏切換橫屏時),切換到橫屏時,構造方法傳遞的參數就找不到了。
原因很簡單,因為Fragment是有自己封裝的生命周期的,這一點和Activity類似,Activity傳參也不是用構造方法的方式。
但是究竟生命周期對構造方法傳遞參數有什么影響呢?
源碼中一探究竟:
在Fragment中,是通過Bundle來保存參數的,它的私有聲明在此:
Bundle mArguments;
順著這個聲明的命名mArguments找下去,發現其實相關的主要方法并不多:
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()); }
也就是說,如果我們調用時使用setArguments()傳遞了Bundle,它會被保存在mArguments 這個私有聲明中。
而如果是通過構造函數傳遞的參數,那很不幸,Fragment重建過程中,并沒有持有相應參數的屬性或方法,自然,你通過構造函數傳遞的參數就丟失了。
其實目前大家單純無參new Fragment()的方式并沒有錯,只是可以讓Activity更優雅的調用Fragment.newInstance(),
而如果涉及到傳遞參數,萬不可通過構造函數傳遞,會丟失。
知其然,知其所以然
總結,Fragment.newInstance() ,別無其他,只是事關風格(代碼”整”“潔”之道),建議大家以后均使用谷歌推薦的該方法
看完上述內容是否對您有幫助呢?如果還想對相關知識有進一步的了解或閱讀更多相關文章,請關注億速云行業資訊頻道,感謝您對億速云的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。