轉載:http://blog.csdn.net/crazy1235/article/details/50933621
Fragment
還是先來基本介紹。
Fragment –> 片段。
在Android3.0的時候被引入,它的出現主要是給大屏幕設備提供更加靈活的UI支持。通過對Activity佈局進行分片,更加方便的對每塊進行獨立控制。這些片段可以被不同的activity複用。
fragment生命週期
每個fragment擁有自己的生命週期,但是fragment要依賴於activity存在,生命週期受到包括它的activity的生命週期控制。
來兩張神圖~~
左圖就是fragment的生命週期圖。右圖是fragment與activity各自生命週期的對照。
介紹一下常用的幾個生命週期函數:
-
onAttach(Context) –> 當fragment被綁定到activity上時回調
-
onCreate(Bundle) –> 當fragment對象創建的時候回調,一般在此方法裏做參數接收。
-
onCreateView(LayoutInflater, ViewGroup, Bundle) –> 創建fragment視圖時回調
-
onDestoryView –> 視圖銷燬時回調
-
onDestory –> 銷燬fragment時回調
-
onDetech() –> fragment與activity失去關聯時回調
fragment的使用
使用fragment可以當成一個控件,直接放到activity佈局文件裏;也可以在代碼裏面動態的添加、更新或者刪除。
下面的activity佈局文件中定義了一個fragment和一個frameLayout。使用標籤可以稱之爲靜態的Fragment,在activity創建的時候也會去創建並顯示它,而framelayout是一個容器,我們在代碼中可以動態的添加一個fragment進去。
<code class="hljs xml has-numbering"><span class="hljs-pi"><?xml version="1.0" encoding="utf-8"?></span> <span class="hljs-tag"><<span class="hljs-title">LinearLayout</span> <span class="hljs-attribute">xmlns:android</span>=<span class="hljs-value">"http://schemas.android.com/apk/res/android"</span> <span class="hljs-attribute">xmlns:tools</span>=<span class="hljs-value">"http://schemas.android.com/tools"</span> <span class="hljs-attribute">android:layout_width</span>=<span class="hljs-value">"match_parent"</span> <span class="hljs-attribute">android:layout_height</span>=<span class="hljs-value">"match_parent"</span> <span class="hljs-attribute">android:orientation</span>=<span class="hljs-value">"horizontal"</span> <span class="hljs-attribute">tools:context</span>=<span class="hljs-value">".fragments.ArticleActivity"</span>></span> <span class="hljs-comment"><!--headlines--></span> <span class="hljs-tag"><<span class="hljs-title">fragment </span> <span class="hljs-attribute">android:id</span>=<span class="hljs-value">"@+id/headline_fragment"</span> <span class="hljs-attribute">android:name</span>=<span class="hljs-value">"com.jacksen.demo.view.fragments.HeadlinesFragment"</span> <span class="hljs-attribute">android:layout_width</span>=<span class="hljs-value">"0dp"</span> <span class="hljs-attribute">android:layout_height</span>=<span class="hljs-value">"match_parent"</span> <span class="hljs-attribute">android:layout_weight</span>=<span class="hljs-value">"1"</span> <span class="hljs-attribute">tools:layout</span>=<span class="hljs-value">"@layout/fragment_item_list"</span> /></span> <span class="hljs-comment"><!--article--></span> <span class="hljs-tag"><<span class="hljs-title">FrameLayout </span> <span class="hljs-attribute">android:id</span>=<span class="hljs-value">"@+id/article_frame_layout"</span> <span class="hljs-attribute">android:layout_width</span>=<span class="hljs-value">"0dp"</span> <span class="hljs-attribute">android:layout_height</span>=<span class="hljs-value">"match_parent"</span> <span class="hljs-attribute">android:layout_weight</span>=<span class="hljs-value">"2"</span> /></span> <span class="hljs-tag"></<span class="hljs-title">LinearLayout</span>></span></code>
注意:
使用標籤顯示Fragment的時候,需要對這個fragment設置一個id或者tag,否則會出現”Error inflating class fragment”錯誤。
<code class="hljs lasso has-numbering">Caused <span class="hljs-keyword">by</span>: java<span class="hljs-built_in">.</span>lang<span class="hljs-built_in">.</span>IllegalArgumentException: Binary <span class="hljs-built_in">XML</span> file line <span class="hljs-variable">#10</span>: Must specify unique android:id, android:<span class="hljs-built_in">tag</span>, <span class="hljs-literal">or</span> have a <span class="hljs-keyword">parent</span> <span class="hljs-keyword">with</span> an id for com<span class="hljs-built_in">.</span>jacksen<span class="hljs-built_in">.</span>demo<span class="hljs-built_in">.</span>view<span class="hljs-built_in">.</span>fragments<span class="hljs-built_in">.</span>HeadlinesFragment</code><ul class="pre-numbering" style="display: block;"><li>1</li></ul>
Fragment的子類繼承的時候,如果你的minSdkVersion <= 11,需要引入V4包,然後倒入android.support.v4.app.Fragment包。如果是大於11,直接導入android.app.Fragment包即可。
<code class="hljs java has-numbering"><span class="hljs-keyword">package</span> com.jacksen.demo.view.fragments; <span class="hljs-keyword">import</span> android.content.Context; <span class="hljs-keyword">import</span> android.os.Bundle; <span class="hljs-keyword">import</span> android.support.v4.app.Fragment; <span class="hljs-keyword">import</span> android.support.v7.widget.GridLayoutManager; <span class="hljs-keyword">import</span> android.support.v7.widget.LinearLayoutManager; <span class="hljs-keyword">import</span> android.support.v7.widget.RecyclerView; <span class="hljs-keyword">import</span> android.util.Log; <span class="hljs-keyword">import</span> android.view.LayoutInflater; <span class="hljs-keyword">import</span> android.view.View; <span class="hljs-keyword">import</span> android.view.ViewGroup; <span class="hljs-keyword">import</span> com.jacksen.demo.view.R; <span class="hljs-keyword">import</span> com.jacksen.demo.view.fragments.dummy.ArticleBean; <span class="hljs-keyword">import</span> com.jacksen.demo.view.fragments.dummy.ArticleBean.ArticleItem; <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">HeadlinesFragment</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Fragment</span> {</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> String ARG_COLUMN_COUNT = <span class="hljs-string">"column-count"</span>; <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> mColumnCount = <span class="hljs-number">2</span>; <span class="hljs-keyword">private</span> OnChangeArticleListener mListener; <span class="hljs-keyword">public</span> <span class="hljs-title">HeadlinesFragment</span>() { } <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> HeadlinesFragment <span class="hljs-title">newInstance</span>(<span class="hljs-keyword">int</span> columnCount) { HeadlinesFragment fragment = <span class="hljs-keyword">new</span> HeadlinesFragment(); Bundle args = <span class="hljs-keyword">new</span> Bundle(); args.putInt(ARG_COLUMN_COUNT, columnCount); fragment.setArguments(args); <span class="hljs-keyword">return</span> fragment; } <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onAttach</span>(Context context) { <span class="hljs-keyword">super</span>.onAttach(context); Log.d(<span class="hljs-string">"HeadlinesFragment"</span>, <span class="hljs-string">"onAttach"</span>); <span class="hljs-keyword">if</span> (context <span class="hljs-keyword">instanceof</span> OnChangeArticleListener) { mListener = (OnChangeArticleListener) context; } <span class="hljs-keyword">else</span> { <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> RuntimeException(context.toString() + <span class="hljs-string">" must implement OnChangeArticleListener"</span>); } } <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onCreate</span>(Bundle savedInstanceState) { <span class="hljs-keyword">super</span>.onCreate(savedInstanceState); Log.d(<span class="hljs-string">"HeadlinesFragment"</span>, <span class="hljs-string">"onCreate"</span>); <span class="hljs-keyword">if</span> (getArguments() != <span class="hljs-keyword">null</span>) { mColumnCount = getArguments().getInt(ARG_COLUMN_COUNT); } } <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> View <span class="hljs-title">onCreateView</span>(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { Log.d(<span class="hljs-string">"HeadlinesFragment"</span>, <span class="hljs-string">"onCreateView"</span>); View view = inflater.inflate(R.layout.fragment_item_list, container, <span class="hljs-keyword">false</span>); <span class="hljs-comment">// Set the adapter</span> <span class="hljs-keyword">if</span> (view <span class="hljs-keyword">instanceof</span> RecyclerView) { Context context = view.getContext(); RecyclerView recyclerView = (RecyclerView) view; <span class="hljs-keyword">if</span> (mColumnCount <= <span class="hljs-number">1</span>) { recyclerView.setLayoutManager(<span class="hljs-keyword">new</span> LinearLayoutManager(context)); } <span class="hljs-keyword">else</span> { recyclerView.setLayoutManager(<span class="hljs-keyword">new</span> GridLayoutManager(context, mColumnCount)); } recyclerView.setAdapter(<span class="hljs-keyword">new</span> HeadlinesRecyclerViewAdapter(ArticleBean.ITEMS, mListener)); } <span class="hljs-keyword">return</span> view; } <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onDetach</span>() { <span class="hljs-keyword">super</span>.onDetach(); Log.d(<span class="hljs-string">"HeadlinesFragment"</span>, <span class="hljs-string">"onDetach"</span>); mListener = <span class="hljs-keyword">null</span>; } <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">OnChangeArticleListener</span> {</span> <span class="hljs-keyword">void</span> onChangeArticle(ArticleItem item); } }</code>
- 76
動態的添加fragment就需要在代碼裏面通過FragmentManager等類進行操作:
<code class="hljs avrasm has-numbering">frameLayout = (FrameLayout) findViewById(R<span class="hljs-preprocessor">.id</span><span class="hljs-preprocessor">.article</span>_frame_layout)<span class="hljs-comment">;</span> fragmentManager = getSupportFragmentManager()<span class="hljs-comment">;</span> articleFragment = ArticleFragment<span class="hljs-preprocessor">.newInstance</span>()<span class="hljs-comment">;</span> fragmentManager<span class="hljs-preprocessor">.beginTransaction</span>()<span class="hljs-preprocessor">.replace</span>(R<span class="hljs-preprocessor">.id</span><span class="hljs-preprocessor">.article</span>_frame_layout, articleFragment)<span class="hljs-preprocessor">.commit</span>()<span class="hljs-comment">;</span></code>
Fragment的事務管理
android裏通過FragmentManager類進行管理fragment,一組對fragment的操作(添加、刪除、替換等)稱爲一個事務,通過FragmentTransaction類來提交執行。也可以把事務添加到回退棧中,進行回滾。這有點類似於數據庫的事務機制。
注意:
-
事物操作最後必須調用commit()才能執行。
-
調用commit()方法之後,也不是立刻執行;如果需要立刻執行,可以使用executePendingTransactions()方法。
-
一次性add多個fragment,顯示的是最後一個。
-
任務棧回退針對的是事務,而不是fragment。一次事務操作過程中可以有很多個對fragment的操作。
-
只能在activity處於可保存狀態的情況下,進行事務操作。否則引發如下異常:
<code class="hljs erlang has-numbering">java.lang.<span class="hljs-variable">IllegalStateException</span>: <span class="hljs-variable">Can</span> <span class="hljs-keyword">not</span> perform this action <span class="hljs-keyword">after</span> on<span class="hljs-variable">SaveInstanceState</span></code>
比如,在onPause()或者onStop()中提交事務,就會出現以上問題,如果非要在這些生命週期裏面進行事務提交,請使用FragmentTransaction類的commitAllowingStateLoss()方法,允許狀態丟失。
- 如果activity繼承的是AppCompatActivity,onBackPressed()回調函數裏面是利用的V4包的getSupportFragmentManager()進行的棧回退,所以做fragment回退的時候需要注意引用的是不是V4包的Fragment類。
<code class="hljs java has-numbering"><span class="hljs-javadoc">/** * Take care of popping the fragment back stack or finishing the activity * as appropriate. */</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onBackPressed</span>() { <span class="hljs-keyword">if</span> (!mFragments.getSupportFragmentManager().popBackStackImmediate()) { supportFinishAfterTransition(); } }</code>
Fragment之間的切換
Fragment的切換就是基本就是利用add()、hide()、show()、replace()這四個方法。
情況一:採用add方式切換fragment
activity:
<code class="hljs avrasm has-numbering">@Override protected void onCreate(Bundle savedInstanceState) { super<span class="hljs-preprocessor">.onCreate</span>(savedInstanceState)<span class="hljs-comment">;</span> setContentView(R<span class="hljs-preprocessor">.layout</span><span class="hljs-preprocessor">.activity</span>_test_fragments2)<span class="hljs-comment">;</span> ButterKnife<span class="hljs-preprocessor">.bind</span>(this)<span class="hljs-comment">;</span> FragmentManager fragmentManager = getSupportFragmentManager()<span class="hljs-comment">;</span> TabFragment1 tabFragment1 = TabFragment1<span class="hljs-preprocessor">.newInstance</span>()<span class="hljs-comment">;</span> fragmentManager<span class="hljs-preprocessor">.beginTransaction</span>()<span class="hljs-preprocessor">.add</span>(R<span class="hljs-preprocessor">.id</span><span class="hljs-preprocessor">.test</span>_fragments_layout, tabFragment1)<span class="hljs-preprocessor">.addToBackStack</span>(<span class="hljs-string">"tab1"</span>)<span class="hljs-preprocessor">.commit</span>()<span class="hljs-comment">;</span> }</code>
TabFragment1代碼:
<code class="hljs java has-numbering"><span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> View <span class="hljs-title">onCreateView</span>(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { Log.d(<span class="hljs-string">"TabFragment1"</span>, <span class="hljs-string">"onCreateView"</span>); View view = inflater.inflate(R.layout.fragment_tab_fragment1, container, <span class="hljs-keyword">false</span>); ButterKnife.bind(<span class="hljs-keyword">this</span>, view); openNextBtn.setOnClickListener(<span class="hljs-keyword">new</span> View.OnClickListener() { <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onClick</span>(View v) { FragmentManager fragmentManager = getFragmentManager(); TabFragment2 tabFragment2 = TabFragment2.newInstance(); fragmentManager.beginTransaction().add(R.id.test_fragments_layout, tabFragment2).addToBackStack(<span class="hljs-keyword">null</span>).commit(); } }); <span class="hljs-keyword">return</span> view; }</code>
TabFragment2代碼:
<code class="hljs java has-numbering"><span class="hljs-javadoc">/** * Tab Fragment 2 */</span> <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TabFragment2</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Fragment</span> {</span> <span class="hljs-keyword">public</span> <span class="hljs-title">TabFragment2</span>() { } <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> TabFragment2 <span class="hljs-title">newInstance</span>() { TabFragment2 fragment = <span class="hljs-keyword">new</span> TabFragment2(); <span class="hljs-keyword">return</span> fragment; } <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onAttach</span>(Context context) { <span class="hljs-keyword">super</span>.onAttach(context); Log.d(<span class="hljs-string">"TabFragment2"</span>, <span class="hljs-string">"onAttach"</span>); } <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onCreate</span>(Bundle savedInstanceState) { <span class="hljs-keyword">super</span>.onCreate(savedInstanceState); Log.d(<span class="hljs-string">"TabFragment2"</span>, <span class="hljs-string">"onCreate"</span>); <span class="hljs-keyword">if</span> (getArguments() != <span class="hljs-keyword">null</span>) { } } <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> View <span class="hljs-title">onCreateView</span>(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { Log.d(<span class="hljs-string">"TabFragment2"</span>, <span class="hljs-string">"onCreateView"</span>); <span class="hljs-keyword">return</span> inflater.inflate(R.layout.fragment_tab_fragment2, container, <span class="hljs-keyword">false</span>); } <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onDestroyView</span>() { <span class="hljs-keyword">super</span>.onDestroyView(); Log.d(<span class="hljs-string">"TabFragment2"</span>, <span class="hljs-string">"onDestroyView"</span>); } <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onDestroy</span>() { <span class="hljs-keyword">super</span>.onDestroy(); Log.d(<span class="hljs-string">"TabFragment2"</span>, <span class="hljs-string">"onDestroy"</span>); } <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onDetach</span>() { <span class="hljs-keyword">super</span>.onDetach(); Log.d(<span class="hljs-string">"TabFragment2"</span>, <span class="hljs-string">"onDetach"</span>); } }</code>
此時都是採用add的方式進行顯示fragment。每次都會把添加fragment的事務疊加到回退棧上面。
在TabFragment1界面點擊按鈕添加TabFragment2界面,然後在按返回鍵回退,打印出生命週期如下:
情況二:採用replace的方式切換fragment
activity代碼不變。
TabFragment1的代碼如下:
<code class="hljs java has-numbering"><span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> View <span class="hljs-title">onCreateView</span>(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { Log.d(<span class="hljs-string">"TabFragment1"</span>, <span class="hljs-string">"onCreateView"</span>); View view = inflater.inflate(R.layout.fragment_tab_fragment1, container, <span class="hljs-keyword">false</span>); ButterKnife.bind(<span class="hljs-keyword">this</span>, view); openNextBtn.setOnClickListener(<span class="hljs-keyword">new</span> View.OnClickListener() { <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onClick</span>(View v) { FragmentManager fragmentManager = getFragmentManager(); TabFragment2 tabFragment2 = TabFragment2.newInstance(); fragmentManager.beginTransaction().replace(R.id.test_fragments_layout, tabFragment2).addToBackStack(<span class="hljs-keyword">null</span>).commit(); } }); <span class="hljs-keyword">return</span> view; }</code>
TabFragment1切換到TabFragment2的時候,使用的replace方法。replace方法的作用是remove掉所有添加到相同id的容器裏的fragment,然後添加參數裏的fragment。所以會回調TabFragment1的onDestoryView()方法,等到從TabFragment2返回的時候,去執行replace事務的相反操作,也就會重新創建TabFragment1的視圖,回調onCreateView()方法。
remove:
我們在TabFragment2界面中調用remove方法,把TabFragment1移除掉。
TabFragment1.java:
<code class="hljs java has-numbering"><span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> View <span class="hljs-title">onCreateView</span>(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { Log.d(<span class="hljs-string">"TabFragment1"</span>, <span class="hljs-string">"onCreateView"</span>); View view = inflater.inflate(R.layout.fragment_tab_fragment1, container, <span class="hljs-keyword">false</span>); ButterKnife.bind(<span class="hljs-keyword">this</span>, view); openNextBtn.setOnClickListener(<span class="hljs-keyword">new</span> View.OnClickListener() { <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onClick</span>(View v) { FragmentManager fragmentManager = getFragmentManager(); TabFragment2 tabFragment2 = TabFragment2.newInstance(); fragmentManager.beginTransaction().add(R.id.test_fragments_layout, tabFragment2).addToBackStack(<span class="hljs-keyword">null</span>).commit(); } }); <span class="hljs-keyword">return</span> view; }</code>
TabFragment2.java:
<code class="hljs java has-numbering"><span class="hljs-keyword">private</span> onBtnClickListener onBtnClickListener; <span class="hljs-keyword">public</span> <span class="hljs-title">TabFragment2</span>() { } <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> TabFragment2 <span class="hljs-title">newInstance</span>() { TabFragment2 fragment = <span class="hljs-keyword">new</span> TabFragment2(); <span class="hljs-keyword">return</span> fragment; } <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onAttach</span>(Context context) { <span class="hljs-keyword">super</span>.onAttach(context); Log.d(<span class="hljs-string">"TabFragment2"</span>, <span class="hljs-string">"onAttach"</span>); <span class="hljs-keyword">if</span> (context <span class="hljs-keyword">instanceof</span> onBtnClickListener){ onBtnClickListener = (TabFragment2.onBtnClickListener) context; }<span class="hljs-keyword">else</span>{ <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> RuntimeException(context.toString() + <span class="hljs-string">" must implement onBtnClickListener"</span>); } } <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onCreate</span>(Bundle savedInstanceState) { <span class="hljs-keyword">super</span>.onCreate(savedInstanceState); Log.d(<span class="hljs-string">"TabFragment2"</span>, <span class="hljs-string">"onCreate"</span>); <span class="hljs-keyword">if</span> (getArguments() != <span class="hljs-keyword">null</span>) { } } <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> View <span class="hljs-title">onCreateView</span>(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { Log.d(<span class="hljs-string">"TabFragment2"</span>, <span class="hljs-string">"onCreateView"</span>); View view = inflater.inflate(R.layout.fragment_tab_fragment2, container, <span class="hljs-keyword">false</span>); ButterKnife.bind(<span class="hljs-keyword">this</span>, view); removeFragmentBtn.setOnClickListener(<span class="hljs-keyword">new</span> View.OnClickListener() { <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onClick</span>(View v) { onBtnClickListener.removeFragment1(); } }); <span class="hljs-keyword">return</span> view; } <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">onBtnClickListener</span>{</span> <span class="hljs-keyword">void</span> removeFragment1(); }</code>
activity:
<code class="hljs java has-numbering"> <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">removeFragment1</span>() { fragmentManager.beginTransaction().remove(tabFragment1).commit(); }</code>
從代碼裏看到,remove操作的事務沒有添加到回退棧中,所以從TabFragment2中返回的時候,直接退到了activity界面。
但是從界面來看,從TabFragment2會退到activity之後,再次按返回鍵並沒有退出activity,然後再按返回鍵的時候纔會退出activity。原因就是,雖然remove了TabFragment1,但是隻是回調了onDestoryView()方法銷燬了視圖,此時TabFragment1的對象資源和與activity的關聯還沒有斷開。所以點擊返回鍵的時候會有一個沒有“響應”。
下面看看我們把reomve()操作添加到回退棧的情況:
<code class="hljs java has-numbering"><span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">removeFragment1</span>() { fragmentManager.beginTransaction().remove(tabFragment1).addToBackStack(<span class="hljs-string">"remove"</span>).commit(); }</code>
與上面的情況相比,多了TabFragment1的onCreateView()這一步。這是因爲把remove事務添加到了任務棧,回退的時候逆向執行該操作。
使用replace()方法和remove()方法會導致視圖銷燬,所以,切換fragment的時候,如果需要視圖保留視圖,就不能用這兩個方法。
Fragment與Activity之間傳值
-
fragment –> activity 99%的做法都是通過接口回調來做的。定義一個接口,activity中實現此接口方法,在fragment裏面調用接口方法
-
activity –> fragment 通過findFragmentById()後者findFragmentByTag()方法獲取fragment實例調用fragment裏的public方法即可。
接着往下看~~
TabFragment2調用接口方法:
<code class="hljs java has-numbering"> <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> View <span class="hljs-title">onCreateView</span>(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { Log.d(<span class="hljs-string">"TabFragment2"</span>, <span class="hljs-string">"onCreateView"</span>); View view = inflater.inflate(R.layout.fragment_tab_fragment2, container, <span class="hljs-keyword">false</span>); ButterKnife.bind(<span class="hljs-keyword">this</span>, view); removeFragmentBtn.setOnClickListener(<span class="hljs-keyword">new</span> View.OnClickListener() { <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onClick</span>(View v) { onBtnClickListener.removeFragment1(); } }); tellSthBtn.setOnClickListener(<span class="hljs-keyword">new</span> View.OnClickListener() { <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onClick</span>(View v) { onBtnClickListener.tellSth(<span class="hljs-string">"這是我要對你說的話!"</span>); } }); <span class="hljs-keyword">return</span> view; } <span class="hljs-javadoc">/** * 回調接口 */</span> <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">onBtnClickListener</span> {</span> <span class="hljs-keyword">void</span> removeFragment1(); <span class="hljs-keyword">void</span> tellSth(String str); }</code>
activity實現該接口的方法:
<code class="hljs java has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TestFragments</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">AppCompatActivity</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">TabFragment2</span>.<span class="hljs-title">onBtnClickListener</span> {</span> <span class="hljs-annotation">@Bind</span>(R.id.test_fragments_layout) FrameLayout testFragmentsLayout; <span class="hljs-keyword">private</span> FragmentManager fragmentManager; <span class="hljs-keyword">private</span> TabFragment1 tabFragment1; <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onCreate</span>(Bundle savedInstanceState) { <span class="hljs-keyword">super</span>.onCreate(savedInstanceState); setContentView(R.layout.activity_test_fragments2); ButterKnife.bind(<span class="hljs-keyword">this</span>); fragmentManager = getSupportFragmentManager(); tabFragment1 = TabFragment1.newInstance(); fragmentManager.beginTransaction().add(R.id.test_fragments_layout, tabFragment1, <span class="hljs-string">"tab1"</span>).addToBackStack(<span class="hljs-string">"tab1"</span>).commit(); } <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onBackPressed</span>() { <span class="hljs-keyword">super</span>.onBackPressed(); } <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">removeFragment1</span>() { fragmentManager.beginTransaction().remove(tabFragment1).addToBackStack(<span class="hljs-string">"remove"</span>).commit(); } <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">tellSth</span>(String str) { ((TabFragment1)fragmentManager.findFragmentByTag(<span class="hljs-string">"tab1"</span>)).showSth(<span class="hljs-string">"hello: "</span> + str); } }</code>
在TabFragment1中定義方法供activity調用:
<code class="hljs cs has-numbering"> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">showSth</span>(String str) { getSthEt.setText(str); }</code>
Fragment與Fragment傳值
activity給fragment傳值你回了,fragment給activity傳值你也會了。那麼這個問題就不要問我了!!!
Fragment切換動畫
Fragment的切換動畫可以使用系統的標準動畫,也可以自定義動畫。
使用系統的需要用到setTransition(),但是隻能設置系統提供的有限的動畫效果。
-
FragmentTransaction.TRANSIT_FRAGMENT_OPEN
-
FragmentTransaction.TRANSIT_FRAGMENT_CLOSE
-
FragmentTransaction.TRANSIT_FRAGMENT_FADE
自定義動畫需要用到的類:
<code class="hljs java has-numbering"><span class="hljs-javadoc">/** * Set specific animation resources to run for the fragments that are * entering and exiting in this transaction. These animations will not be * played when popping the back stack. */</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">abstract</span> FragmentTransaction <span class="hljs-title">setCustomAnimations</span>(@AnimRes <span class="hljs-keyword">int</span> enter, @AnimRes <span class="hljs-keyword">int</span> exit);</code>
<code class="hljs java has-numbering"><span class="hljs-javadoc">/** * Set specific animation resources to run for the fragments that are * entering and exiting in this transaction. The <code>popEnter</code> * and <code>popExit</code> animations will be played for enter/exit * operations specifically when popping the back stack. */</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">abstract</span> FragmentTransaction <span class="hljs-title">setCustomAnimations</span>(@AnimRes <span class="hljs-keyword">int</span> enter, @AnimRes <span class="hljs-keyword">int</span> exit, @AnimRes <span class="hljs-keyword">int</span> popEnter, @AnimRes <span class="hljs-keyword">int</span> popExit);</code>
注意:
setCustomAnimations()必須在add()、remove()、replace()調用之前設置,否則不起作用。
比如:有兩個fragment A和B,從A切換到B的時候
-
@AnimRes int enter
表示Fragment B的進入動畫 -
@AnimRes int exit
表示Fragment A的退出動畫 -
@AnimRes int popEnter
表示當從B界面pop回到A時,Fragment A的進入動畫 -
@AnimRes int popExit
表示當從B界面pop回到A是,Fragment B的退出動畫
如果使用add的方式顯示下一個fragment,則只會觸發enter 和 popExit動畫,因爲這種情況下A並沒有被移除,只是觸發了與B相關的動畫。
比如:
從TabFragment1使用add()方式顯示TabFragment2:
<code class="hljs avrasm has-numbering">FragmentManager fragmentManager = getFragmentManager()<span class="hljs-comment">;</span> TabFragment2 tabFragment2 = TabFragment2<span class="hljs-preprocessor">.newInstance</span>()<span class="hljs-comment">;</span> FragmentTransaction fragmentTransaction = fragmentManager<span class="hljs-preprocessor">.beginTransaction</span>()<span class="hljs-comment">;</span> fragmentTransaction<span class="hljs-preprocessor">.setCustomAnimations</span>(R<span class="hljs-preprocessor">.anim</span><span class="hljs-preprocessor">.slide</span>_in_from_right, R<span class="hljs-preprocessor">.anim</span><span class="hljs-preprocessor">.slide</span>_out_to_left, R<span class="hljs-preprocessor">.anim</span><span class="hljs-preprocessor">.slide</span>_in_from_top, R<span class="hljs-preprocessor">.anim</span><span class="hljs-preprocessor">.slide</span>_out_to_bottom)<span class="hljs-comment">;</span> fragmentTransaction<span class="hljs-preprocessor">.add</span>(R<span class="hljs-preprocessor">.id</span><span class="hljs-preprocessor">.test</span>_fragments_layout, tabFragment2, <span class="hljs-string">"tab2"</span>)<span class="hljs-comment">;</span> fragmentTransaction<span class="hljs-preprocessor">.addToBackStack</span>(<span class="hljs-string">"tab2"</span>)<span class="hljs-comment">;</span> fragmentTransaction<span class="hljs-preprocessor">.commit</span>()<span class="hljs-comment">;</span></code>
4個動畫都是使用的簡單的view動畫,用屬性動畫可以做出更加絢麗的動畫:
slide_in_from_right.xml
<code class="hljs xml has-numbering"><span class="hljs-pi"><?xml version="1.0" encoding="utf-8"?></span> <span class="hljs-tag"><<span class="hljs-title">translate</span> <span class="hljs-attribute">xmlns:android</span>=<span class="hljs-value">"http://schemas.android.com/apk/res/android"</span> <span class="hljs-attribute">android:duration</span>=<span class="hljs-value">"800"</span> <span class="hljs-attribute">android:fromXDelta</span>=<span class="hljs-value">"100.0%"</span> <span class="hljs-attribute">android:interpolator</span>=<span class="hljs-value">"@android:interpolator/accelerate_decelerate_interpolator"</span> <span class="hljs-attribute">android:toXDelta</span>=<span class="hljs-value">"0.0"</span> /></span></code>
slide_out_to_left.xml
<code class="hljs xml has-numbering"><span class="hljs-pi"><?xml version="1.0" encoding="utf-8"?></span> <span class="hljs-tag"><<span class="hljs-title">translate</span> <span class="hljs-attribute">xmlns:android</span>=<span class="hljs-value">"http://schemas.android.com/apk/res/android"</span> <span class="hljs-attribute">android:duration</span>=<span class="hljs-value">"800"</span> <span class="hljs-attribute">android:fromXDelta</span>=<span class="hljs-value">"0.0"</span> <span class="hljs-attribute">android:interpolator</span>=<span class="hljs-value">"@android:interpolator/accelerate_decelerate_interpolator"</span> <span class="hljs-attribute">android:toXDelta</span>=<span class="hljs-value">"-100%"</span> /></span></code>
slide_in_from_top.xml
<code class="hljs xml has-numbering"><span class="hljs-pi"><?xml version="1.0" encoding="utf-8"?></span> <span class="hljs-tag"><<span class="hljs-title">translate</span> <span class="hljs-attribute">xmlns:android</span>=<span class="hljs-value">"http://schemas.android.com/apk/res/android"</span> <span class="hljs-attribute">android:duration</span>=<span class="hljs-value">"1000"</span> <span class="hljs-attribute">android:fromYDelta</span>=<span class="hljs-value">"-100.0%"</span> <span class="hljs-attribute">android:interpolator</span>=<span class="hljs-value">"@android:interpolator/accelerate_decelerate"</span> <span class="hljs-attribute">android:toYDelta</span>=<span class="hljs-value">"0.0"</span> /></span></code>
slide_out_to_bottom.xml
<code class="hljs xml has-numbering"><span class="hljs-pi"><?xml version="1.0" encoding="utf-8"?></span> <span class="hljs-tag"><<span class="hljs-title">translate</span> <span class="hljs-attribute">xmlns:android</span>=<span class="hljs-value">"http://schemas.android.com/apk/res/android"</span> <span class="hljs-attribute">android:duration</span>=<span class="hljs-value">"1000"</span> <span class="hljs-attribute">android:fromYDelta</span>=<span class="hljs-value">"0"</span> <span class="hljs-attribute">android:interpolator</span>=<span class="hljs-value">"@android:interpolator/accelerate_decelerate"</span> <span class="hljs-attribute">android:toYDelta</span>=<span class="hljs-value">"100%p"</span> /></span></code><ul class="pre-numbering" style="display: block;"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li></ul>
效果圖:
如果採用的replace的方式,則會正常的觸發4個動畫。
<code class="hljs avrasm has-numbering">FragmentManager fragmentManager = getFragmentManager()<span class="hljs-comment">;</span> TabFragment2 tabFragment2 = TabFragment2<span class="hljs-preprocessor">.newInstance</span>()<span class="hljs-comment">;</span> FragmentTransaction fragmentTransaction = fragmentManager<span class="hljs-preprocessor">.beginTransaction</span>()<span class="hljs-comment">;</span> fragmentTransaction<span class="hljs-preprocessor">.setCustomAnimations</span>(R<span class="hljs-preprocessor">.anim</span><span class="hljs-preprocessor">.slide</span>_in_from_right, R<span class="hljs-preprocessor">.anim</span><span class="hljs-preprocessor">.slide</span>_out_to_left, R<span class="hljs-preprocessor">.anim</span><span class="hljs-preprocessor">.slide</span>_in_from_top, R<span class="hljs-preprocessor">.anim</span><span class="hljs-preprocessor">.slide</span>_out_to_bottom)<span class="hljs-comment">;</span> fragmentTransaction<span class="hljs-preprocessor">.replace</span>(R<span class="hljs-preprocessor">.id</span><span class="hljs-preprocessor">.test</span>_fragments_layout, tabFragment2, <span class="hljs-string">"tab2"</span>)<span class="hljs-comment">;</span> fragmentTransaction<span class="hljs-preprocessor">.addToBackStack</span>(<span class="hljs-string">"tab2"</span>)<span class="hljs-comment">;</span> fragmentTransaction<span class="hljs-preprocessor">.commit</span>()<span class="hljs-comment">;</span></code>
這裏只介紹了setCustomAnimations()的用法,setTransition()方式的動畫很簡單,就不介紹了。