編輯:關於Android編程
最近在看Google技術文檔的時候發現了一種新的方式來實例化Fragment,就是采用靜態工廠的方式創建Fragment。我們在使用Android studio創建一個類的時候,選擇New ->Fragment->Fragment(Blank)可以很直觀的看到這種方式的寫法:
public class BlankFragment extends Fragment {
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
private String mParam1;
private String mParam2;
public BlankFragment() {
// Required empty public constructor
}
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param param1 Parameter 1.
* @param param2 Parameter 2.
* @return A new instance of fragment BlankFragment.
*/
// 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;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
}
}
上述代碼其實就是在一個Fragment的newInstance方法中傳遞兩個參數,並且通過fragment.setArgument保存在它自己身上,而後通過onCreate()調用的時候將這些參數取出來。
可能有人乍一看,這樣寫沒什麼特殊的啊,不就是用靜態工廠方法傳個參數麼,用構造器傳參數不一樣處理麼?No,No,No,如果僅僅是個靜態工廠而已,又怎麼能成為谷歌推薦呢。
<framelayout android:id="@+id/layout_top" android:layout_height="0dp" android:layout_weight="1" android:layout_width="match_parent"> <framelayout android:id="@+id/layout_bottom" android:layout_height="0dp" android:layout_weight="1" android:layout_width="match_parent"> </framelayout></framelayout>
在xml中定義兩個FrameLayout,平分整個屏幕高度。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(savedInstanceState == null){
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.add(R.id.layout_top,new TopFragment("頂部的Fragment"));
transaction.add(R.id.layout_bottom,BottomFragment.newInstance("底部的Fragment"));
transaction.commit();
}
}
在activity中采用兩種不同的方式來實例化Fragment,頂部的Fragment通過構造方法將參數傳遞給它,而底部的Fragment通過newInstance的方式實例化並傳參。兩個Fragment的代碼如下所示:
public class TopFragment extends Fragment {
private String mTop = "啥也沒有";
public TopFragment(){
}
public TopFragment(String top) {
this.mTop = top;
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
TextView tv = new TextView(getActivity());
tv.setText(mTop);
tv.setGravity(Gravity.CENTER);
tv.setTextColor(Color.RED);
tv.setTextSize(25);
return tv;
}
}
public class BottomFragment extends Fragment {
private String mBottom = "啥也沒有";
public static BottomFragment newInstance(String bottom) {
BottomFragment fragment = new BottomFragment();
Bundle bundle = new Bundle();
bundle.putString("bottom",bottom);
fragment.setArguments(bundle);
return fragment;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(getArguments() != null){
mBottom = getArguments().getString("bottom");
}
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
TextView tv = new TextView(getActivity());
tv.setText(mBottom);
tv.setGravity(Gravity.CENTER);
tv.setTextColor(Color.RED);
tv.setTextSize(25);
return tv;
}
}


咦。。。頂部的Fragment的數據呢?為什麼只顯示默認的數據?activity給它傳過去的數據哪去了呢?
我們來分析一下產生上述情況的原因:當我們橫豎屏切換的時候,activity會重建,相應的,依附於它上面的Fragment也會重新創建。好,順著這個思路,進activity的onCreate方法中看看:
protected void onCreate(@Nullable Bundle savedInstanceState) {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onCreate " + this + ": " + savedInstanceState);
if (mLastNonConfigurationInstances != null) {
mFragments.restoreLoaderNonConfig(mLastNonConfigurationInstances.loaders);
}
if (mActivityInfo.parentActivityName != null) {
if (mActionBar == null) {
mEnableDefaultActionBarUp = true;
} else {
mActionBar.setDefaultDisplayHomeAsUpEnabled(true);
}
}
if (savedInstanceState != null) {
Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
mFragments.restoreAllState(p, mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.fragments : null);//這裡,會恢復所有Fragment的狀態
}
mFragments.dispatchCreate();
getApplication().dispatchActivityCreated(this, savedInstanceState);
if (mVoiceInteractor != null) {
mVoiceInteractor.attachActivity(this);
}
mCalled = true;
}
顯而易見,所有Fragment的狀態恢復應該是在mFragments.restoreAllState()這個方法,跟進去看看:
public void restoreAllState(Parcelable state, ListnonConfigList) { mHost.mFragmentManager.restoreAllState(state, nonConfigList); }
找到FragmentManager這個類,查看它的restoreAllState方法:
void restoreAllState(Parcelable state, FragmentManagerNonConfig nonConfig) {
...
for (int i=0; i();
}
if (DEBUG) Log.v(TAG, "restoreAllState: avail #" + i);
mAvailIndices.add(i);
}
}
...
}
尋找關鍵代碼,我們發現了Fragment f = fs.instantiate(mHost, mParent, childNonConfig);這句,這裡應該是Fragment重新實例化的地方了吧,趕緊點進去瞧瞧:
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);//創建Fragment對象的地方
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;
}
繼續跟進mInstance = Fragment.instantiate(context, mClassName, mArguments);看看裡面的真正實現:
public static Fragment instantiate(Context context, String fname, @Nullable Bundle args) {
try {
Class clazz = sClassMap.get(fname);
if (clazz == null) {
// Class not found in the cache, see if it's real, and try to add it
clazz = context.getClassLoader().loadClass(fname);
sClassMap.put(fname, clazz);
}
Fragment f = (Fragment)clazz.newInstance();
if (args != null) {
args.setClassLoader(f.getClass().getClassLoader());
f.mArguments = args;//將之前設置的參數保存在自己身上
}
return f;
} catch (ClassNotFoundException e) {
throw new InstantiationException("Unable to instantiate fragment " + fname
+ ": make sure class name exists, is public, and has an"
+ " empty constructor that is public", e);
} catch (java.lang.InstantiationException e) {
throw new InstantiationException("Unable to instantiate fragment " + fname
+ ": make sure class name exists, is public, and has an"
+ " empty constructor that is public", e);
} catch (IllegalAccessException e) {
throw new InstantiationException("Unable to instantiate fragment " + fname
+ ": make sure class name exists, is public, and has an"
+ " empty constructor that is public", e);
}
}
終於走到了Fragment最終被實例化創建的地方,我們可以看到Fragment對象被反射創建之後,會調用這麼一句代碼:f.mArguments = args; 哦,原來如此,Fragment在重新創建的時候只會調用無參的構造方法,並且如果之前通過fragment.setArguments(bundle)這種方式設置過參數的話,Fragment重建時會得到這些參數,所以,在onCreate中我們可以通過getArguments()的方式拿到我們之前設置的參數。同時由於Fragment在重建時並不會調用我們自定義的帶參數的構造方法,所以我們傳遞的參數它也就獲取不到了,這就是為什麼會出現上述情況的原因。
細心的童鞋可以發現,上面的代碼在catch語句當中拋出了幾個異常,意思是:在Fragment重建過程中,確保你的Fragment的類是public的,並且帶有一個public的空參的構造器,否則就讓你崩潰~~~
好了,拋棄之前那些不好的代碼習慣吧,支持谷歌,擁抱變化。
android開機啟動過程
Linux內核啟動之後就到Android Init進程,進而啟動Android相關的服務和應用。啟動的過程如下圖所示: 下面將從Android4.0源碼中,和
Android開源項目第三篇——優秀項目篇
本文為那些不錯的Android開源項目第三篇——優秀項目篇,主要介紹那些還不錯的完整Android項目。記錄的項目主要依據是項目有意思或項目分層規
為Android Studio編寫自定義Gradle插件的教程
Google已經建議Android開發全部轉向Android Studio開發,Android Studio 是使用gradle編譯、打包的,那麼問題來了,gradle可
android的UI中經常出現的菊花圈(圓形的加載圈)
夜深也是無聊,翻看以前的老代碼,發現那個我們經常用的菊花圈,原來是幀動畫做的,有點意思。突然感覺幀動畫做的東西效果不錯啊,至少看起來聽耐看的。開工上代碼: 先是布局文件: