Android中Fragment的解析和使用詳解
前言
Android Fragment的生命周期和Activity類似,實(shí)際可能會(huì)涉及到數(shù)據(jù)傳遞,onSaveInstanceState的狀態(tài)保存,F(xiàn)ragmentManager的管理和Transaction,切換的Animation。
我們首先簡(jiǎn)單的介紹一下Fragment的生命周期。

大致上,從名字就可以判斷出每個(gè)生命周期是干嘛的。
AppCompatActivity就是FragmentActivity的子類,如果想使用Fragment,是要繼承FragmentActivity,因?yàn)榭紤]到兼容的問(wèn)題,我們要使用getSupportFragmentManager,而這個(gè)方法是FragmentActivity中聲明的。
Activity中同樣也有個(gè)類似的方法,getFragmentManager,兩個(gè)方法返回的都是FragmentManager,不過(guò)一個(gè)是v4包。
至于Android到底是如何為低版本兼容Fragment這個(gè)問(wèn)題,這里就不研究了,因?yàn)樯婕暗降脑创a估計(jì)應(yīng)該很多,而且可能會(huì)很深。
Fragment到底是如何將自己的生命周期和Activity綁定在一起呢?
這里有一個(gè)很關(guān)鍵的類:FragmentController。
在FragmentActivity的生命周期中,會(huì)調(diào)用FragmentController對(duì)應(yīng)的方法,而這些方法會(huì)調(diào)用到FragmentManager對(duì)應(yīng)的方法。
我們來(lái)看看FragmentActivity的onCreate方法。
mFragments.attachHost(null /*parent*/); super.onCreate(savedInstanceState);
這里調(diào)用了attachHost方法,而attachHost方法又調(diào)用了FragmentManager的attachController方法。
attachController這個(gè)方法實(shí)際上,是將需要的FragmentHostCallback,F(xiàn)ragmentContainer和Fragment傳進(jìn)來(lái)。
FragmentHostCallback是FragmentContainer的子類,實(shí)際上,它就是Fragment所要附加的Activity,它持有這個(gè)Activity的實(shí)例,Context和Handler。
FragmentContainer和FragmentHostCallback是同一個(gè)實(shí)例,就是要附加的Activity。
而Fragment傳入的是null,參數(shù)名是parent,這里附加的是Activity,因此沒(méi)有Parent Fragment是很正常的。
當(dāng)我們使用FragmentManager的時(shí)候,如果要添加Fragment,是需要這樣寫(xiě):
FragmentManager manager = ((FragmentActivity) context).getSupportFragmentManager(); FragmentTransaction transaction = manager.beginTransaction(); transaction.add(fragment, context.getClass().getSimpleName()); transaction.commit();
這里出現(xiàn)了新的類:FragmentTransaction。
FragmentTransaction是用于處理Fragment的棧操作,具體的子類是BackStackRecord,它同時(shí)也是一個(gè)Runnable。
當(dāng)我們調(diào)用FragmentTransaction的add時(shí)候,實(shí)際上是調(diào)用BackStackRecord的addOp方法,Op是自定義的數(shù)據(jù)結(jié)構(gòu):
static final class Op {
Op next;
Op prev;
int cmd;
Fragment fragment;
int enterAnim;
int exitAnim;
int popEnterAnim;
int popExitAnim;
ArrayList<Fragment> removed;
}
也就是Fragment棧里面的節(jié)點(diǎn)的數(shù)據(jù)結(jié)構(gòu)。
當(dāng)我們commit的時(shí)候,就會(huì)調(diào)用FragmentManager的allocBackStackIndex,方法內(nèi)部使用了對(duì)象這是為了保證Fragment的正常寫(xiě)入順序,實(shí)際上,內(nèi)部是用一個(gè)BackStackRecord的ArrayList來(lái)保存?zhèn)魅氲腂ackStackRecord。
執(zhí)行Fragment的寫(xiě)入后,關(guān)鍵一步就是調(diào)用FragmentManager的enqueueAction,將我們的操作添加到操作隊(duì)列中。
執(zhí)行這個(gè)方法的時(shí)候,會(huì)先檢查是否已經(jīng)保存了狀態(tài),也就是是否處于onStop的生命周期,如果是的話,就會(huì)報(bào)異常信息。所以我們不能在Activity的onStop里面進(jìn)行任何有關(guān)Fragment的操作。
為了保證操作是串行的,同樣也使用了對(duì)象鎖。
最關(guān)鍵的是運(yùn)行了FragmentManager的mExecCommit這個(gè)Runnable,這里主要是把每一個(gè)Active的Fragment作為參數(shù)傳給moveToState這個(gè)方法,判斷Fragment的狀態(tài)。
這里的邏輯比較復(fù)雜,會(huì)將Fragment的State和mCurState進(jìn)行比較。一開(kāi)始commit的每個(gè)Fagment的狀態(tài)都是INITIALIZING。
分為2種情況:
1.mCurState > State
說(shuō)明Fragment開(kāi)始創(chuàng)建。
onCreate最后會(huì)調(diào)用FragmentController和FragmentManager的dispatchCreate,將mCurState的狀態(tài)改為CREATED,這時(shí)同樣是調(diào)用moveToState方法,每個(gè)Fragment的狀態(tài)都是INITIALIZING,就會(huì)開(kāi)始讀取保存的狀態(tài),并且分別調(diào)用Fragment的onAttach,onCreate,onCreateView和onViewCreate。
如果沒(méi)有在commit之前就setArguments來(lái)傳遞數(shù)據(jù),調(diào)用commit后是無(wú)法讀取到的,因?yàn)閟etArguments傳遞過(guò)來(lái)的Bundle是在Fragment初始化的時(shí)候才會(huì)賦值給Fragment的mArguments,而Fragment的初始化動(dòng)作是在FragmentManager的onCreateView中進(jìn)行。我們使用Fragment的時(shí)候,都是在FragmentActivity的onCreate中commit,所以這時(shí)候Fragment實(shí)際上在commit的時(shí)候就會(huì)開(kāi)始初始化了,如果放在commit后面setArguments,就根本沒(méi)機(jī)會(huì)傳遞給Fragment。
這里我們要注意,上面都是在FragmentActivity的onCreate中進(jìn)行,也就是說(shuō),這時(shí)候Activity根本還沒(méi)創(chuàng)建好,所以關(guān)于Activity的資源在這里是無(wú)法獲取到的。
2.mCurState < State
說(shuō)明Fragment已經(jīng)創(chuàng)建完畢。
所以,F(xiàn)ragment真正和Activity綁定是在commit調(diào)用的時(shí)候。
官方推薦我們通過(guò)setArguments來(lái)傳遞構(gòu)造Fragment需要的參數(shù),不推薦通過(guò)構(gòu)造方法直接來(lái)傳遞參數(shù),因?yàn)闄M豎屏切換的時(shí)候,是重新創(chuàng)建新的Activity,也就是重新創(chuàng)建新的Fragment,原先的數(shù)據(jù)就會(huì)全部丟失,但是setArguments傳遞的Bundle會(huì)保留下來(lái)。
我們只要看FragmentActivity的onCreate方法就知道,它會(huì)判斷之前的配置和savedInstanceState是否不為null,而savedInstanceState會(huì)保存Fragment的數(shù)據(jù),這些數(shù)據(jù)是以Parcelable的形式保存下來(lái),這些數(shù)據(jù)就是FragmentManagerState,如果不為null,就會(huì)重新加載這些數(shù)據(jù)。
實(shí)際上,上面的生命周期的圖是有問(wèn)題的,onActivityCreated真正被調(diào)用是在FragmentActivity的onStart里面,這時(shí)mCurState就變成ACTIVITY_CREATED,而Fragment的狀態(tài)變成CREATED,這時(shí)如果Fragment并不是布局文件中聲明 ,采用的是動(dòng)態(tài)添加的方式,那么Fragment就是在這里調(diào)用onCreateView和onViewCreated,并且將Fragment添加到FragmentActivity的布局上。
首先我們必須明確的是,onStart的時(shí)候,Activity雖然可見(jiàn),但是還沒(méi)有顯示到前臺(tái),所以這時(shí)候才處理動(dòng)態(tài)添加Fragment的情況是合理的,如果我們把動(dòng)態(tài)添加Fragment的邏輯放在onCreate的時(shí)候,那時(shí)候Activity自身的布局都還沒(méi)創(chuàng)建,怎么可能找到Container加載Fragment呢?
這同時(shí)也是提醒我們,不要在Fragment的onCreateView和onViewCreated處理耗時(shí)的邏輯,否則就會(huì)影響到FragmentActivity顯示到前臺(tái)的時(shí)間。
當(dāng)FragmentActivity進(jìn)入onResume的時(shí)候,已經(jīng)顯示到前臺(tái)了,這時(shí)候發(fā)送一個(gè)消息給Handler,通知FragmentManager,mCurState變?yōu)镽ESUMED,這時(shí)Fragment就會(huì)開(kāi)始進(jìn)行監(jiān)聽(tīng)事件等的設(shè)置。
當(dāng)FragmentActivity進(jìn)入onPause的時(shí)候,會(huì)先檢查Fragment是否還沒(méi)有設(shè)置監(jiān)聽(tīng)事件,如果沒(méi)有,就讓它進(jìn)行設(shè)置,然后修改mCurState為STARTED,這時(shí)就屬于前面的第二種情況,F(xiàn)ragment進(jìn)入onPause。
當(dāng)FragmentActivity進(jìn)入onStop的時(shí)候,首先通知FragmentManager修改mCurState為STOPPED,這時(shí)就會(huì)通知Fragment進(jìn)入onStop,然后就是Handler接收到消息,通知FragmentManager將mCurState改為ACTIVITY_CREATED,通知Fragment調(diào)用performReallyStop,也就是真正的結(jié)束。
當(dāng)FragmentActivity進(jìn)入onDestroy的時(shí)候,會(huì)確認(rèn)是否真的reallyStop,然后通知FragmentManager修改mCurState為CREATED,這時(shí)Fragment的狀態(tài)為ACTIVITY_CREATED,開(kāi)始保存視圖數(shù)據(jù),調(diào)用onDestroyView,父布局開(kāi)始移除Fragment。
仔細(xì)看這段邏輯,就會(huì)發(fā)現(xiàn),不管有沒(méi)有設(shè)置Fragment是需要保留的,都會(huì)進(jìn)入onDetach,表示該Fragment和FragmentActivity已經(jīng)不再關(guān)聯(lián)了。
我們?cè)賮?lái)看一下onRetainNonConfigurationInstance這個(gè)方法,它會(huì)設(shè)置Fragment的mRetaining為true,這樣就會(huì)使Fragment不會(huì)進(jìn)入onDestroy,就算是重新創(chuàng)建新的FragmentActivity,也只是清除Fragment的mHost,mParentFragment,mFragmentManager和mChildFragmentManager,之前的數(shù)據(jù)都會(huì)保存下來(lái),并且這個(gè)Fragment并沒(méi)有被銷毀,這就會(huì)導(dǎo)致一個(gè)問(wèn)題:重新創(chuàng)建的FragmentActivity本身也會(huì)創(chuàng)建新的Fragment,因此會(huì)出現(xiàn)Fragment的重疊,因?yàn)檫@時(shí)Fragment的狀態(tài)為STOPPED,會(huì)分別進(jìn)入onStart和onResume,也就是重新顯示到前臺(tái)的過(guò)程。
我們?cè)趯?shí)際的測(cè)試中就會(huì)發(fā)現(xiàn),在沒(méi)做任何處理的情況下,F(xiàn)ragmentManager中的Fragment是越來(lái)越多,所以實(shí)際上,考慮到這種情況:應(yīng)用在后臺(tái)如果被殺掉的話,重新啟動(dòng)應(yīng)用,之前的Fragment就可能會(huì)重疊在界面上。
這種情況在處理Tab的時(shí)候是比較麻煩的,因?yàn)門ab是好幾個(gè)Fragment同時(shí)顯示在前臺(tái),如果Activity被干掉,重新創(chuàng)建的時(shí)候,進(jìn)入的是第一個(gè)Fragment,但如果這時(shí)候是在另一個(gè)Fragment下被干掉的,就可能導(dǎo)致這兩個(gè)Fragment重疊。
所以可以在onCreate中判斷是否重新創(chuàng)建Activity,只要判斷savedInstanceState是否為null,如果為null,說(shuō)明該Activity沒(méi)有被重建過(guò),可以添加Fragment,就算是上面的Tab的情況也可以處理,只要不添加第一個(gè)Fragment就可以。
如果是基于這樣的判斷來(lái)解決這個(gè)問(wèn)題,我們還可以在添加Fragment的時(shí)候,指定一個(gè)Id或者Tag,判斷FragmentManager中對(duì)應(yīng)的Id或者Tag的Fragment是否存在來(lái)決定是否要添加。
當(dāng)然,如果項(xiàng)目實(shí)在沒(méi)有需要,我們是可以強(qiáng)制豎屏的。
如果只是針對(duì)橫豎屏切換,也有另一種解決方案,在AndroidManifest中對(duì)應(yīng)的activity標(biāo)簽中設(shè)置android:configChanges="orientation|keyboardHidden" ,但是這個(gè)屬性在Android 4.0以上就失效了,必須這樣寫(xiě)才行:android:configChanges="orientation|keyboardHidden|screenSize" 。這樣在橫豎屏切換的時(shí)候,不會(huì)走onRetainNonConfigurationInstance,走的是onConfigurationChanged,切換時(shí)不會(huì)銷毀當(dāng)前的FragmentActivity,自然Fragment也同樣能夠保持下來(lái)。
如果我們想要為Fragment增加過(guò)場(chǎng)動(dòng)畫(huà),針對(duì)v4和非v4,有兩種做法。
1.針對(duì)v4,使用的是View Animation,動(dòng)畫(huà)資源放在res\anim\目錄下。
2.針對(duì)非v4,使用的是屬性動(dòng)畫(huà),動(dòng)畫(huà)資源放在res\animator\目錄下。
一般我們使用的都是v4的Fragment,并且針對(duì)的轉(zhuǎn)場(chǎng)動(dòng)畫(huà),View Animation已經(jīng)足夠滿足我們的要求。
我們?cè)賮?lái)看一下FragmentTransaction的addToBackStack這個(gè)方法。
如果我們想要實(shí)現(xiàn)這樣的效果:點(diǎn)擊返回鍵,返回的是上一個(gè)Fragment。那就得調(diào)用addToBackStack這個(gè)方法。這個(gè)方法要求傳入一個(gè)String的參數(shù),實(shí)際上我們只要傳入null就行,如果我們不想指定棧(雖說(shuō)是棧,實(shí)際上只是個(gè)ArrayList,并沒(méi)有實(shí)現(xiàn)棧的結(jié)構(gòu))的名字。
仔細(xì)看源碼,我們就會(huì)發(fā)現(xiàn),如果不調(diào)用這個(gè)方法,在按返回鍵的時(shí)候,就直接finish當(dāng)前的FragmentActivity。
Fragment的回退和Activity的回退是有很大的區(qū)別的,我們知道,F(xiàn)ragment的操作是FragmentTransaction,而B(niǎo)ackStackRecord真是這些操作的具體子類實(shí)現(xiàn)。
這時(shí)問(wèn)題就來(lái)了:如果我們是兩次FragmentTransactiont添加Fragment,第一次添加A,第二次添加B和C,我們回退并不是Fragment,是BackStackRecord的Op,而Op中記錄的是每次操作的Fragment,當(dāng)我們回退第二次操作的時(shí)候,是把第二次添加的B和C都退出來(lái)。
如果我們只有一個(gè)Fragment,并且也不想實(shí)現(xiàn)Fragment的回退棧,就千萬(wàn)不要調(diào)用addToBackState,不然在Activity按返回鍵的時(shí)候,并不會(huì)馬上退出Activity,而是返回一個(gè)空白,因?yàn)榫退闶莕ull,也會(huì)添加到BackStackRecord的ArrayList中,因?yàn)檫@個(gè)參數(shù)是作為mName來(lái)標(biāo)記BackStackRecord, 在實(shí)際的處理中,它是否為null根本不重要。
當(dāng)然,我們也可以自己調(diào)用FragmentManager的popBackStack方法進(jìn)行回退棧的操作,如果我們想要馬上執(zhí)行的話,就要調(diào)用popBackStackImmediate方法,實(shí)際上,默認(rèn)調(diào)用的就是這個(gè)方法。
如果我們?cè)谔砑覨ragment的時(shí)候,并沒(méi)有設(shè)置任何Tag,但是在彈出棧的時(shí)候,要求彈出最新的Fragment,增加新的Fragment。
Fragment的棧并不像是Activity的棧那么復(fù)雜,提供多種啟動(dòng)模式,如果看源碼的話,就會(huì)發(fā)現(xiàn),實(shí)際上它就只有一種:彈出最近的BackStackRecord中的所有Fragment。
如果我們調(diào)用popBackStack的時(shí)候,沒(méi)有指定flag為POP_BACK_STACK_INCLUSIVE,源碼中的實(shí)現(xiàn)雖然是用if-else分成兩種判斷情況,但實(shí)際的處理是差不多的,不過(guò)沒(méi)有指定的話,它會(huì)處理比較麻煩,如果可能的話,我們還是指定一下。
回到我們上面的問(wèn)題,我們?cè)撊绾巫瞿兀?br />
replace并不會(huì)影響到回退棧,如果我們真的要使用replace來(lái)替代某個(gè)Fragment,并且想要實(shí)現(xiàn)回退棧,就要addToBackStack,但如果這時(shí)我們想要替換某個(gè)Fragment,回退棧中的記錄并不會(huì)跟著被替換,也就是說(shuō),這時(shí)我們選擇回退,會(huì)退回到我們被替換的Fragment,所以我們必須在替換前就彈出這個(gè)Fragment。
FragmentManager提供了getBackStackEntryCount方法告訴我們回退棧的數(shù)量,還有g(shù)etBackStackEntryAt方法來(lái)獲取到對(duì)應(yīng)的BackStackRecord,這時(shí)我們就能以下的處理來(lái)實(shí)現(xiàn)彈出:
if(manager.getBackStackEntryCount()>0){
int n = manager.getBackStackEntryCount();
manager.popBackStack(manager.getBackStackEntryAt(n-1).getName(), FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
然后我們就能使用replace了。
我們必須注意,add,remove和replace影響到的是Fragment在界面上的顯示,它們跟回退棧一點(diǎn)關(guān)系都沒(méi)有,實(shí)際上,如果我們沒(méi)有調(diào)用addToBackStack,甚至根本就不會(huì)有回退棧,而且回退棧是在該方法每次調(diào)用后,就會(huì)添加一個(gè),不論是否重復(fù),它都不會(huì)進(jìn)行任何判斷,所以如果一次FragmentTransaction提交多個(gè)Fragment,但是只是調(diào)用一次addToBackStack,雖然界面上有多個(gè)Fragment,但是回退棧中只有一個(gè)記錄。
Fragment說(shuō)歸到底,在源碼上來(lái)看,就只是和Activity生命周期同步的View,它不可能做到和Activity一樣復(fù)雜的功能,它的任何邏輯業(yè)務(wù)代碼,實(shí)際上也屬于Activity,只不過(guò)移動(dòng)到另一個(gè)類中而已,當(dāng)然,如果愿意的話,就算把它當(dāng)做一個(gè)輕量級(jí)的ViewController也是可以的,畢竟它只是負(fù)責(zé)自己負(fù)責(zé)的View的一切業(yè)務(wù)功能。
FragmentTransaction為Fragment提供了add,remove,hide,show和replace幾種操作,我們要注意的是,add和replace的區(qū)別。
replace實(shí)際上就是remove + add的結(jié)合,并且使用replace的話,每次切換的話,會(huì)導(dǎo)致Fragment重新創(chuàng)建,因?yàn)樗鼤?huì)把被替換的Fragment從視圖中移除,這樣當(dāng)替換回來(lái)的時(shí)候,就要重新創(chuàng)建了。
這樣頻繁切換,就會(huì)嚴(yán)重影響到性能和流量。
所以,官方的說(shuō)法是:replace()這個(gè)方法只是在上一個(gè)Fragment不再需要時(shí)采用的簡(jiǎn)便方法。
正確的切換方式是add() ,切換時(shí)hide() ,add()另一個(gè)Fragment;再次切換時(shí),只需hide()當(dāng)前,show()另一個(gè)。
當(dāng)然,在hide之前,我們還需通過(guò)isAdd來(lái)判斷是否添加過(guò)。
如果通過(guò)hide和show來(lái)實(shí)現(xiàn)切換,我們就不需要保存數(shù)據(jù),因?yàn)镕ragment并沒(méi)有被銷毀,如果是replace這種方式,我們就要保存數(shù)據(jù),舉個(gè)例子,如果界面中有EditText,我們?nèi)绻胍4嬷霸贓ditText的輸入,就要保存這個(gè)值,不然使用replace的話,是會(huì)移除整個(gè)View的。
Fragment還涉及到和Activity以及其他Fragment的通信。
最好的方式就是只讓Activity和Fragment進(jìn)行通信,如果Fragment想要和其他Fragment進(jìn)行通信,也得通過(guò)Activity。
我們可以利用回調(diào)Fragment的方法進(jìn)行通信,當(dāng)然,也可以在Fragment中聲明接口,只要Activity實(shí)現(xiàn)這些接口,就能實(shí)現(xiàn)Activity和Fragment的通信。
想到setArguments是通過(guò)Bundle的形式來(lái)保存數(shù)據(jù),那么我們是否可以利用這點(diǎn),在傳參上做一點(diǎn)文章呢?
在軟件設(shè)計(jì)上,為了減少依賴,提議利用一個(gè)高層抽象來(lái)負(fù)責(zé)組件之間的通信,這樣各個(gè)組件之間就不需要互相依賴了,也就是所謂的依賴倒置原則。
那么,我們這里是否也可以利用這個(gè)原則來(lái)做點(diǎn)事情呢?
依賴倒置在很多框架中的表現(xiàn)是采取注解的形式,我們可以考慮一下注解的方式來(lái)解決這個(gè)問(wèn)題。
如果僅僅是為了構(gòu)建Fragment而傳輸?shù)膮?shù),問(wèn)題倒是比較簡(jiǎn)單,只要合理的利用反射,我們就可以獲取到Fragment的字段,然后賦值。
類似的表現(xiàn)形式如下:
class FragmentA extends Fragment{
@Arg
private int age;
public void onCreate(){
FragmentInject.inject(this);
}
}
class ActivityA extends Activity{
public voi onCreate(){
FragmentA a = new FragmentA();
Bundle bundle = new Bundle();
bundle.putString("text", "你好");
a.setArguments(bundle);
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.add(R.id.container, a);
transaction.commit();
}
}
實(shí)際上,這種方式無(wú)非就是代碼組織方式上的改變,因?yàn)槲覀兺耆梢栽贔ragment的onCreate中獲取到Bundle,同樣也可以進(jìn)行相同的操作,并且總的代碼量會(huì)更少,但如果單純只是從Fragment來(lái)看,我們只需要調(diào)用FragmentInject.inject方法和聲明Arg注解,其他的東西根本不用考慮,相關(guān)的解析Bundle和字段賦值都放在FragmentInject這個(gè)抽象中,我們就不用每個(gè)Fragment都要寫(xiě)同樣的代碼,只要交給FragmentInject就行。
當(dāng)然,上面只是簡(jiǎn)單的實(shí)現(xiàn),真的是要實(shí)現(xiàn)一個(gè)成熟的東西是要考慮很多方面的,我們這里就把這個(gè)簡(jiǎn)單的項(xiàng)目放在Github上:https://github.com/wenjiang/FragmentArgs.git,如果有新的想法,歡迎補(bǔ)充。
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來(lái)一定的幫助,如果有疑問(wèn)大家可以留言交流。
- Android實(shí)現(xiàn)Tab布局的4種方式(Fragment+TabPageIndicator+ViewPager)
- Android Fragment與Activity之間的相互通信實(shí)例代碼
- Android Activity與Fragment實(shí)現(xiàn)底部導(dǎo)航器
- Android開(kāi)發(fā)技巧之Fragment的懶加載
- Android用Fragment創(chuàng)建選項(xiàng)卡
- Android Fragment+FragmentTabHost組件實(shí)現(xiàn)常見(jiàn)主頁(yè)面(仿微信新浪)
- Android Fragment的生命周期詳解
- Android Fragment多層嵌套重影問(wèn)題的解決方法
- Android 中 Fragment 嵌套 Fragment使用存在的bug附完美解決方案
- Android中關(guān)于FragmentA嵌套FragmentB的問(wèn)題
- Android Fragment(動(dòng)態(tài),靜態(tài))碎片詳解及總結(jié)
相關(guān)文章
Android實(shí)現(xiàn)美團(tuán)、大眾點(diǎn)評(píng)的購(gòu)買懸浮效果(ScrollView滾動(dòng)監(jiān)聽(tīng))
這篇文章主要為大家詳細(xì)介紹了Android對(duì)ScrollView滾動(dòng)監(jiān)聽(tīng),實(shí)現(xiàn)美團(tuán)、大眾點(diǎn)評(píng)的購(gòu)買懸浮效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-02-02
Kotlin基礎(chǔ)學(xué)習(xí)之循環(huán)和異常
最近在學(xué)習(xí)kotlin,Kotlin 是一個(gè)基于 JVM 的新的編程語(yǔ)言,下面這篇文章主要給大家介紹了關(guān)于Kotlin基礎(chǔ)學(xué)習(xí)之循環(huán)和異常的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面來(lái)一起看看吧。2017-12-12
Android startActivityForResult的基本用法詳解
這篇文章主要介紹了Android startActivityForResult的基本用法詳解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08
Android registerForActivityResult動(dòng)態(tài)申請(qǐng)權(quán)限案例詳解
這篇文章主要介紹了Android registerForActivityResult動(dòng)態(tài)申請(qǐng)權(quán)限案例詳解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-09-09
Android recyclerview實(shí)現(xiàn)縱向虛線時(shí)間軸的示例代碼
本文主要介紹了Android recyclerview實(shí)現(xiàn)縱向虛線時(shí)間軸的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-07-07
Flutter 日期時(shí)間DatePicker控件及國(guó)際化
這篇文章主要介紹了Flutter 日期時(shí)間DatePicker控件及國(guó)際化,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03
Android實(shí)現(xiàn)知乎選項(xiàng)卡動(dòng)態(tài)隱藏效果實(shí)例
選項(xiàng)卡相信對(duì)大家來(lái)說(shuō)應(yīng)該不陌生,最近發(fā)現(xiàn)知乎選項(xiàng)卡的動(dòng)態(tài)隱藏效果不錯(cuò),下面這篇文章主要給大家介紹了關(guān)于Android實(shí)現(xiàn)知乎選項(xiàng)卡動(dòng)態(tài)隱藏效果的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2018-02-02
Android中自定義View實(shí)現(xiàn)圓環(huán)等待及相關(guān)的音量調(diào)節(jié)效果
這篇文章主要介紹了Android中自定義View實(shí)現(xiàn)圓環(huán)等待及相關(guān)的音量調(diào)節(jié)效果,邏輯非常簡(jiǎn)單,或許繪圖方面更加繁瑣XD 需要的朋友可以參考下2016-04-04

