本篇文章主要介紹以下幾個(gè)知識(shí)點(diǎn):
- 碎片
fragment
的用法;- 碎片
fragment
的生命周期隶糕;- 動(dòng)態(tài)加載布局的技巧冈在,限定符的使用府框;
- 實(shí)戰(zhàn):簡(jiǎn)易版的新聞應(yīng)用。
4.1 碎片是什么
碎片(Fragment
)是一種可以嵌入在活動(dòng)當(dāng)中的 UI
片段讥邻,它能讓程序更加合理和充分地利用大屏幕的空間,因而在平板上應(yīng)用的非常廣泛院峡。
如開發(fā)一個(gè)新聞應(yīng)用兴使,其中一個(gè)界面使用 RecyclerView
展示了一組新聞的標(biāo)題,當(dāng)點(diǎn)擊了其中一個(gè)標(biāo)題照激,就打開另一個(gè)界面顯示新聞的詳細(xì)內(nèi)容发魄。若是在手機(jī)中設(shè)計(jì),可以將新聞標(biāo)題列表放在一個(gè)活動(dòng)中俩垃,將新聞的詳細(xì)內(nèi)容放在另一個(gè)活動(dòng)中励幼,如圖所示:
但顯示在平板上,那么新聞標(biāo)題列表將會(huì)被拉長(zhǎng)至填充滿整個(gè)平板的屏幕口柳,而新聞的標(biāo)題一般都不會(huì)太長(zhǎng)苹粟,這樣將會(huì)導(dǎo)致界面上有大量的空白區(qū)域,如圖所示:
因此跃闹,更好的設(shè)計(jì)方案是將新聞標(biāo)題列表界面和新聞詳細(xì)內(nèi)容界面分別放在兩個(gè)碎片中嵌削,然后在同一個(gè)活動(dòng)里引入這兩個(gè)碎片,這樣就可以將屏幕空間充分地利用起來了望艺,如圖所示:
4.2 碎片的使用方式
4.2.1 碎片的簡(jiǎn)單用法
在一個(gè)活動(dòng)當(dāng)中添加兩個(gè)碎片苛秕,并讓這兩個(gè)碎片平分活動(dòng)空間。
新建一個(gè)左側(cè)碎片布局 fragment_left.xml
如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="按鈕"/>
</LinearLayout>
這個(gè)布局只放置了一個(gè)按鈕找默,并讓它水平居中顯示艇劫。然后新建右側(cè)碎片布局 fragment_right.xml
如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#00ff00">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:textSize="20sp"
android:text="這是右邊的fragment"/>
</LinearLayout>
將這個(gè)布局的背景色設(shè)置成綠色,并放置了一個(gè) TextView
用于顯示一段文本惩激。
接著新建一個(gè) LeftFragment
類店煞,繼承自 Fragment
。注意咧欣,這里可能會(huì)有兩個(gè)不同包下的 Fragment
供你選擇,一個(gè)是系統(tǒng)內(nèi)置的 android.app.Fragment
魄咕,一個(gè)是 support-v4
庫(kù)中的 android.support.v4.app.Fragment
衩椒。
這里強(qiáng)烈建議使用 support-v4 庫(kù)中的 Fragment,因?yàn)樗梢宰屗槠谒?Android 系統(tǒng)版本中保持功能一致性。
代碼如下:
public class LeftFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_left, container, false);
return view;
}
}
這里僅僅是重寫了 Fragment
的 onCreateView()
方法毛萌,然后在這個(gè)方法中通過 LayoutInflater
的 inflate()
方法將剛定義的 fragment_left
布局動(dòng)態(tài)加載進(jìn)來苟弛,整個(gè)方法簡(jiǎn)單明了。
接著用同樣的方法再新建一個(gè) RightFragment
:
public class RightFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_right, container, false);
return view;
}
}
接下來修改 activity_fragment.xml
中的代碼如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<fragment
android:id="@+id/fragment_left"
android:name="com.wonderful.myfirstcode.inquiry_fragment.LeftFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"/>
<fragment
android:id="@+id/fragment_right"
android:name="com.wonderful.myfirstcode.inquiry_fragment.RightFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"/>
</LinearLayout>
上面使用了 <fragment>
標(biāo)簽在布局中添加碎片阁将,通過 android:name
屬性來顯式指明要添加的碎片類名膏秫,注意一定要將類的包名也加上。
這樣簡(jiǎn)單的碎片示例就已經(jīng)寫好了做盅,運(yùn)行程序缤削,(平板上)效果如圖:
4.2.2 動(dòng)態(tài)添加碎片
碎片真正的強(qiáng)大之處在于,它可以在程序運(yùn)行時(shí)動(dòng)態(tài)地添加到活動(dòng)當(dāng)中吹榴。根據(jù)具體情況來動(dòng)態(tài)地添加碎片亭敢,你就可以將程序界面定制得更加多樣化。
在上一節(jié)代碼的基礎(chǔ)上繼續(xù)完善图筹,新建 fragment_another_right.xml
如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#ffff00">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:textSize="20sp"
android:text="這是另外一個(gè)右邊的fragment"/>
</LinearLayout>
然后新建 AnotherRightFragment
作為另一個(gè)右側(cè)碎片如下:
public class AnotherRightFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_another_right, container, false);
return view;
}
}
接下來看一下如何將它動(dòng)態(tài)地添加到活動(dòng)當(dāng)中帅刀。修改 activity_fragment.xml
如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<fragment
android:id="@+id/fragment_left"
android:name="com.wonderful.myfirstcode.inquiry_fragment.LeftFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"/>
<FrameLayout
android:id="@+id/right_layout"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"/>
<!--
<fragment
android:id="@+id/fragment_right"
android:name="com.wonderful.myfirstcode.inquiry_fragment.RightFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"/>
-->
</LinearLayout>
上面將右側(cè)碎片放在了一個(gè) FrameLayout
中,下面在代碼中向 FrameLayout
里添加內(nèi)容远剩,從而實(shí)現(xiàn)動(dòng)態(tài)添加碎片的功能扣溺。修改 Activity
中的代碼如下:
public class FragmentActivity extends AppCompatActivity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment);
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(this);
replaceFragment(new RightFragment());
}
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.button:
replaceFragment(new AnotherRightFragment());
break;
default:
break;
}
}
private void replaceFragment(Fragment fragment){
// 獲取FragmentManager
FragmentManager fragmentManager = getSupportFragmentManager();
// 開啟事務(wù)
FragmentTransaction transaction = fragmentManager.beginTransaction();
// 添加或替換碎片
transaction.replace(R.id.right_layout,fragment);
// 提交事務(wù)
transaction.commit();
}
}
上述代碼,給左側(cè)碎片中的按鈕注冊(cè)了一個(gè)點(diǎn)擊事件瓜晤,調(diào)用 replaceFragment()
方法動(dòng)態(tài)添加碎片锥余。結(jié)合代碼可看出,動(dòng)態(tài)添加碎片主要分為 5 步活鹰。
1. 創(chuàng)建待添加的碎片實(shí)例哈恰。
2. 獲取 FragmentManager,在活動(dòng)中可以直接調(diào)用 getSupportFragmentManager()
方法得到志群。
3. 開啟一個(gè)事務(wù)着绷,通過調(diào)用 beginTransaction()
方法開啟。
4. 向容器內(nèi)添加或替換碎片锌云,一般使用 replace()
方法實(shí)現(xiàn)荠医,需要傳入容器的 id
和待添加的碎 片實(shí)例。
5. 提交事務(wù)桑涎,調(diào)用 commit()
方法來完成彬向。
重新運(yùn)行程序,效果如圖:
4.2.3 在碎片中模擬返回棧
在上一小節(jié)中攻冷,實(shí)現(xiàn)了向活動(dòng)中動(dòng)態(tài)添加碎片的功能娃胆,但通過點(diǎn)擊按鈕添加了一個(gè)碎片之后,按下 Back
鍵程序就會(huì)直接退出等曼。該如何實(shí)現(xiàn)按下 Back
鍵可以回到上一個(gè)碎片呢里烦?
FragmentTransaction 中提供了一個(gè) addToBackStack()
方法凿蒜,可以用于將一個(gè)事務(wù)添加到返回棧中,修改 Activity
中的代碼如下:
public class FragmentActivity extends AppCompatActivity implements View.OnClickListener {
. . .
private void replaceFragment(Fragment fragment){
// 獲取FragmentManager
FragmentManager fragmentManager = getSupportFragmentManager();
// 開啟事務(wù)
FragmentTransaction transaction = fragmentManager.beginTransaction();
// 添加或替換碎片
transaction.replace(R.id.right_layout,fragment);
// 用于描述返回棧的狀態(tài)
transaction.addToBackStack(null);
// 提交事務(wù)
transaction.commit();
}
}
在事務(wù)提交之前調(diào)用了 FragmentTransaction
的 addToBackStack()
方法胁黑,它可以接收一個(gè)名字用于描述返回棧的狀態(tài)废封,一般傳入 null
即可。這樣問題就解決了丧蘸。
4.2.4 碎片和活動(dòng)之間進(jìn)行通信
為了方便碎片和活動(dòng)之間進(jìn)行通信漂洋,FragmentManager
提供了一個(gè)類似于 findViewById()
的方法,專門用于從布局文件中獲取碎片的實(shí)例力喷。在活動(dòng)中調(diào)用碎片里的方法:
RightFragment rightFragment = (RightFragment) getFragmentManager().findFragmentById(R.id.right_fragment);
在碎片中調(diào)用活動(dòng)里的方法:通過調(diào)用 getActivity()
方法來得到和當(dāng)前碎片相關(guān)聯(lián)的活動(dòng)實(shí)例刽漂,代碼如下:
MainActivity activity = (MainActivity) getActivity();
4.3 碎片的生命周期
每個(gè)活動(dòng)在其生命周期內(nèi)可能會(huì)有四種狀態(tài):運(yùn)行狀態(tài)、暫停 狀態(tài)弟孟、停止?fàn)顟B(tài)和銷毀狀態(tài)爽冕。類似地,每個(gè)碎片在其生命周期內(nèi)也可能會(huì)經(jīng)歷這幾種狀態(tài)披蕉,只不過在一些細(xì)小的地方會(huì)有部分區(qū)別。
運(yùn)行狀態(tài):
當(dāng)一個(gè)碎片是可見的乌奇,并且它所關(guān)聯(lián)的活動(dòng)正處于運(yùn)行狀態(tài)時(shí)没讲,該碎片也處于運(yùn)行狀態(tài)应役。暫停狀態(tài):
當(dāng)一個(gè)活動(dòng)進(jìn)入暫停狀態(tài)時(shí)(由于另一個(gè)未占滿屏幕的活動(dòng)被添加到了棧頂)坷剧,與它相關(guān)聯(lián)的可見碎片就會(huì)進(jìn)入到暫停狀態(tài)。停止?fàn)顟B(tài):
當(dāng)一個(gè)活動(dòng)進(jìn)入停止?fàn)顟B(tài)時(shí)冒黑,與它相關(guān)聯(lián)的碎片就會(huì)進(jìn)入到停止?fàn)顟B(tài)试伙∴倚牛或者通過調(diào)用FragmentTransaction
的remove()
、replace()
方法將碎片從活動(dòng)中移除疏叨,但若在事務(wù)提交之前調(diào)用addToBackStack()
方法潘靖,這時(shí)的碎片也會(huì)進(jìn)入到停止?fàn)顟B(tài)≡槁總的來說卦溢,進(jìn)入停止?fàn)顟B(tài)的碎片對(duì)用戶來說是完全不可見的,有可能會(huì)被系統(tǒng)回收秀又。銷毀狀態(tài):
碎片總是依附于活動(dòng)而存在的单寂,因此當(dāng)活動(dòng)被銷毀時(shí),與它相關(guān)聯(lián)的碎片就會(huì)進(jìn)入到銷毀狀態(tài)吐辙⌒觯或者通過調(diào)用FragmentTransaction
的remove()
、replace()
方法將碎片從活動(dòng)中移除昏苏,但在事務(wù)提交之前并沒有調(diào)用addToBackStack()
方法尊沸,這時(shí)的碎片也會(huì)進(jìn)入 到銷毀狀態(tài)威沫。
和活動(dòng) Acitvity
相似,Fragment
類中也提供了一系列的回調(diào)方法椒丧,以覆蓋碎片生命周期的每個(gè)環(huán)節(jié)壹甥。其中,活動(dòng)中有的回調(diào)方法壶熏,碎片中幾乎都有句柠,不過碎片還提供了一些附加的回調(diào)方法,重點(diǎn)來看下這幾個(gè)回調(diào):
- onAttach() 當(dāng)碎片和活動(dòng)建立關(guān)聯(lián)的時(shí)候調(diào)用棒假。
- onCreateView() 為碎片創(chuàng)建視圖(加載布局)時(shí)調(diào)用溯职。
- onActivityCreated() 確保與碎片相關(guān)聯(lián)的活動(dòng)一定已經(jīng)創(chuàng)建完畢的時(shí)候調(diào)用。
- onDestroyView() 當(dāng)與碎片關(guān)聯(lián)的視圖被移除的時(shí)候調(diào)用帽哑。
- onDetach() 當(dāng)碎片和活動(dòng)解除關(guān)聯(lián)的時(shí)候調(diào)用谜酒。
碎片完整的生命周期可參考源自 Android
官網(wǎng)圖的示意圖:
另外,在碎片中你也可以通過 onSaveInstanceState()
方法來保存數(shù)據(jù)妻枕, 因?yàn)檫M(jìn)入停止?fàn)顟B(tài)的碎片有可能在系統(tǒng)內(nèi)存不足的時(shí)候被回收僻族。保存下來的數(shù)據(jù)在 onCreate()
、onCreateView()
和 onActivityCreated()
這三個(gè)方法中你都可以重新得到屡谐,它們都含有一個(gè) Bundle
類型的 savedInstanceState
參數(shù)述么。
4.4 動(dòng)態(tài)加載布局的技巧
4.4.1 使用限定符
現(xiàn)有個(gè)需求:在平板上使用雙頁模式,在手機(jī)上顯示單頁模式愕掏。那么怎樣才能在運(yùn)行時(shí)判斷程序應(yīng)該是使用雙頁模式還是單頁模式呢度秘?這就需要借助限定符(Qualifiers)來實(shí)現(xiàn)了。
通過一個(gè)例子來學(xué)習(xí)一下它的用法饵撑,修改項(xiàng)目中 activity_fragment.xml
的代碼:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<fragment
android:id="@+id/fragment_left"
android:name="com.wonderful.myfirstcode.inquiry_fragment.LeftFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
這里將多余的代碼都刪掉剑梳,只留下一個(gè)左側(cè)碎片,并讓它充滿整個(gè)父布局滑潘。接著在 res
目錄下新建 layout-large
文件夾垢乙,在這個(gè)文件夾下新建一個(gè)布局,也叫做 activity_fragment.xml
语卤, 代碼如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<fragment
android:id="@+id/fragment_left"
android:name="com.wonderful.myfirstcode.inquiry_fragment.LeftFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"/>
<fragment
android:id="@+id/fragment_right"
android:name="com.wonderful.myfirstcode.inquiry_fragment.RightFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="3"/>
</LinearLayout>
上面 layout/activity_fragment
布局只包含了一個(gè)碎片侨赡,即單頁模式,而 layout-large/activity_fragment
布局包含了兩個(gè)碎片粱侣,即雙頁模式羊壹。其中 large
就是一個(gè)限定符,那些屏幕被認(rèn)為是 large
的設(shè)備就會(huì)自動(dòng)加載 layout-large
文件夾下的布局齐婴,而小屏幕的設(shè)備則還是會(huì)加載 layout
文件夾下的布局油猫。
然后將 Activity
中 replaceFragment()
方法注釋掉,并在平板模擬器上重新運(yùn)行程序柠偶, 效果如圖:
再啟動(dòng)一個(gè)手機(jī)模擬器情妖,并在這個(gè)模擬器上重新運(yùn)行程序睬关,效果如圖:
Android 中一些常見的限定符可以參考下表:
4.4.2 使用最小寬度限定符
有時(shí)候希望可以更加靈活地為不同設(shè)備加載布局,不管它們是不是被系統(tǒng)認(rèn)定為 “ large
”毡证,這時(shí)就可以使用最小寬度限定符(Smallest-width Qualifier
)了电爹。
最小寬度限定符允許我們對(duì)屏幕的寬度指定一個(gè)最小指(以 dp
為單位),然后以這個(gè)最小值為臨界點(diǎn)料睛,屏幕寬度大于這個(gè)值的設(shè)備就加載一個(gè)布局丐箩,屏幕寬度小于這個(gè)值的設(shè)備就加載另一個(gè)布局。
在 res 目錄下新建 layout-sw600dp
文件夾恤煞,然后在這個(gè)文件夾下新建 activity_fragment .xml
布局屎勘,代碼與上面 layout-large/activity_fragment
中的一樣。
...
這就意味著居扒,當(dāng)程序運(yùn)行在屏幕寬度大于 600dp 的設(shè)備上時(shí)概漱,會(huì)加載 layout-sw600dp/ activity_fragment
布局,當(dāng)程序運(yùn)行在屏幕寬度小于 600dp 的設(shè)備上時(shí)喜喂,則仍然加載默認(rèn)的 layout/activity_fragment
布局瓤摧。
4.5 碎片的最佳實(shí)踐——一個(gè)簡(jiǎn)易版的新聞應(yīng)用
需求:一個(gè)簡(jiǎn)易的新聞應(yīng)用,可以同時(shí)兼容手機(jī)和平板玉吁。
首先姻灶,在 app/build.gradle
中添加后面需要用到的 RecyclerView
依賴庫(kù):
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:24.2.1'
compile 'com.android.support:recyclerview-v7:24.2.1'
testCompile 'junit:junit:4.12'
}
接下來,準(zhǔn)備好一個(gè)新聞的實(shí)體類诈茧,新建類 News
,代碼如下:
/**
* 新聞實(shí)體類
* Created by KXwon on 2016/12/12.
*/
public class News {
private String title; // 新聞標(biāo)題
private String content; // 新聞內(nèi)容
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
接著新建一個(gè) news_content_frag.xml
布局捂掰,作為新聞內(nèi)容的布局:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/visibility_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:visibility="invisible">
<TextView
android:id="@+id/news_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="20sp"
android:padding="10dp"/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#000"/>
<TextView
android:id="@+id/news_content"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:textSize="18sp"
android:padding="15dp"/>
</LinearLayout>
<View
android:layout_width="1dp"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:background="#000"/>
</RelativeLayout>
新聞內(nèi)容的布局主要分為兩個(gè)部分敢会,頭部顯示新聞標(biāo)題,正文顯示新聞內(nèi)容这嚣,中間使用一條細(xì)線分隔開鸥昏。
然后再新建一個(gè) NewsContentFragment
類,如下:
/**
* 新聞內(nèi)容fragment
* Created by KXwon on 2016/12/12.
*/
public class NewsContentFragment extends Fragment {
private View view;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
view = inflater.inflate(R.layout.news_content_frag, container, false);
return view;
}
/**
* 將新聞標(biāo)題和新聞內(nèi)容顯示在界面上
* @param newsTitle 標(biāo)題
* @param newsContent 內(nèi)容
*/
public void refresh(String newsTitle, String newsContent) {
View visibilityLayout = view.findViewById(R.id.visibility_layout);
visibilityLayout.setVisibility(View.VISIBLE);
TextView newsTitleText = (TextView) view.findViewById (R.id.news_title);
TextView newsContentText = (TextView) view.findViewById(R.id.news_content);
newsTitleText.setText(newsTitle); // 刷新新聞的標(biāo)題
newsContentText.setText(newsContent); // 刷新新聞的內(nèi)容
}
}
這樣就把新聞內(nèi)容的碎片和布局創(chuàng)建好了姐帚,但它們都是在雙頁模式下使用的吏垮,若要在單頁模式中使用,還需創(chuàng)建一個(gè)活動(dòng) NewsContentActivity
罐旗,其布局 news_content.xml
中的代碼如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="@+id/news_content_fragment"
android:name="com.wonderful.myfirstcode.chapter4.simple_news.NewsContentFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>
這里直接在布局中引入了 NewsContentFragment
膳汪,相當(dāng)于把 news_content_frag
布局的內(nèi)容自動(dòng)加了進(jìn)來。
然后編寫 NewsContentActivity
的代碼如下:
public class NewsContentActivity extends AppCompatActivity {
/**
* 構(gòu)建Intent九秀,傳遞所需數(shù)據(jù)
* @param context
* @param newsTitle
* @param newsContent
*/
public static void actionStart(Context context, String newsTitle, String newsContent) {
Intent intent = new Intent(context, NewsContentActivity.class);
intent.putExtra("news_title", newsTitle);
intent.putExtra("news_content", newsContent);
context.startActivity(intent);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.news_content);
// 獲取傳入的新聞標(biāo)題遗嗽、新聞內(nèi)容
String newsTitle = getIntent().getStringExtra("news_title");
String newsContent = getIntent().getStringExtra("news_content");
// 獲取 NewsContentFragment 實(shí)例
NewsContentFragment newsContentFragment = (NewsContentFragment) getSupportFragmentManager()
.findFragmentById(R.id.news_content_fragment);
// 刷新 NewsContentFragment 界面
newsContentFragment.refresh(newsTitle, newsContent);
}
}
上述代碼,在 onCreate()
方法中通過 Intent
獲取傳入的新聞標(biāo)題和內(nèi)容鼓蜒,然后調(diào)用 FragmentManager
的 findFragmentById()
方法得到 NewsContentFragment
的實(shí)例痹换,接著調(diào)用它的 refresh()
方法征字,并將新聞的標(biāo)題和內(nèi)容傳入,顯示數(shù)據(jù)娇豫。(關(guān)于 actionStart() 方法可以閱讀前面的探究活動(dòng)2.5.2相關(guān)筆記匙姜。)
接下來還需再創(chuàng)建顯示新聞列表的布局 news_title_frag.xml
,如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/news_title_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
新建 news_item.xml
作為 上述 RecyclerView
子項(xiàng)的布局:
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/news_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:ellipsize="end"
android:textSize="18sp"
android:padding="10dp"/>
子項(xiàng)的布局就只有一個(gè) TextView
冯痢。
新聞列表和子項(xiàng)布局都創(chuàng)建好了氮昧,接下來就需要一個(gè)用于展示新聞列表的地方。這里新建 NewsTitleFragment
作為展示新聞列表的碎片:
/**
* 新聞列表fragment
* Created by KXwon on 2016/12/12.
*/
public class NewsTitleFragment extends Fragment{
private boolean isTowPane;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.news_content_frag, container, false);
return view;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (getActivity().findViewById(R.id.news_content_layout)!= null){
// 可以找到 news_content_layout 布局時(shí)系羞,為雙頁模式
isTowPane = true;
}else {
// 找不到 news_content_layout 布局時(shí)郭计,為單頁模式
isTowPane = false;
}
}
}
為實(shí)現(xiàn)上述 onActivityCreated()
方法中判斷當(dāng)前時(shí)雙頁還是單頁模式,需要借助限定符椒振,首先修改主布局 activity_news.xml
中的代碼:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/news_title_layout"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<fragment
android:id="@+id/news_title_fragment"
android:name="com.wonderful.myfirstcode.chapter4.simple_news.NewsTitleFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
上述代碼表示昭伸,在單頁模式下只會(huì)加載一個(gè)新聞標(biāo)題的碎片。
然后在 res
目錄下新建 layout-sw600dp
文件夾澎迎,在這個(gè)文件夾下再新建一個(gè) activity_news.xml
文件庐杨,代碼如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<fragment
android:id="@+id/news_title_fragment"
android:name="com.wonderful.myfirstcode.chapter4.simple_news.NewsTitleFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<FrameLayout
android:id="@+id/news_content_layout"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="3" >
<fragment
android:id="@+id/news_content_fragment"
android:name="com.wonderful.myfirstcode.chapter4.simple_news.NewsContentFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
</LinearLayout>
上述代碼表示,在雙頁模式下會(huì)同時(shí)加載兩個(gè)碎片夹供,并將新聞內(nèi)容碎片放在 FrameLayout
布局下灵份,這個(gè)布局 id
正是 news_content_layout
。因此能找到這個(gè) id
的時(shí)候就是雙頁模式哮洽,否則就是單頁模式填渠。
現(xiàn)在已經(jīng)將絕大多數(shù)工作完成了,剩下至關(guān)重要的一點(diǎn)鸟辅,就是在 NewsTitleFragemt
中通過 RecyclerView
將新聞列表展示出來氛什。接下來在 NewsTitleFragemt
中新建一個(gè)內(nèi)部類 NewsAdapter
來作為 RecyclerView
的適配器,如下:
public class NewsTitleFragment extends Fragment{
private boolean isTowPane;
. . .
class NewsAdapter extends RecyclerView.Adapter<NewsAdapter.ViewHolder> {
private List<News> mNewsList;
class ViewHolder extends RecyclerView.ViewHolder {
TextView newsTitleText;
public ViewHolder(View view) {
super(view);
newsTitleText = (TextView) view.findViewById(R.id.news_title);
}
}
public NewsAdapter(List<News> newsList) {
mNewsList = newsList;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.news_item, parent, false);
final ViewHolder holder = new ViewHolder(view);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
News news = mNewsList.get(holder.getAdapterPosition());
if (isTwoPane) {
// 若是雙頁模式匪凉,則刷新 NewsContentFragment 中的內(nèi)容
NewsContentFragment newsContentFragment = (NewsContentFragment)
getFragmentManager().findFragmentById(R.id.news_content_fragment);
newsContentFragment.refresh(news.getTitle(), news.getContent());
} else {
// 若是單頁模式枪眉,則直接啟動(dòng) NewsContentActivity
NewsContentActivity.actionStart(getActivity(), news.getTitle(), news.getContent());
}
}
});
return holder;
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
News news = mNewsList.get(position);
holder.newsTitleText.setText(news.getTitle());
}
@Override
public int getItemCount() {
return mNewsList.size();
}
}
需要注意的是,這里把適配器寫成內(nèi)部類是為了直接訪問 NewsTitleFragment
的變量再层,比如 isTowPane
贸铜。
現(xiàn)在還剩最后一步收尾工作,就是向 RecyclerView
中填充數(shù)據(jù)了聂受。修改 NewsTitleFragment
中的代碼如下:
public class NewsTitleFragment extends Fragment{
. . .
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.news_title_frag, container, false);
RecyclerView newsTitleRecyclerView = (RecyclerView) view.findViewById(R.id.news_title_recycler_view);
LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
newsTitleRecyclerView.setLayoutManager(layoutManager);
NewsAdapter adapter = new NewsAdapter(getNews());
newsTitleRecyclerView.setAdapter(adapter);
return view;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (getActivity().findViewById(R.id.news_content_layout) != null) {
// 可以找到news_content_layout布局時(shí)蒿秦,為雙頁模式
isTwoPane = true;
} else {
// 找不到news_content_layout布局時(shí),為單頁模式
isTwoPane = false;
}
}
/**
* 初始化50條模擬新聞數(shù)據(jù)
* @return
*/
private List<News> getNews() {
List<News> newsList = new ArrayList<>();
for (int i = 1; i <= 50; i++) {
News news = new News();
news.setTitle("This is news title " + i);
news.setContent(getRandomLengthContent("新聞內(nèi)容吼吼吼" + i + "!"));
newsList.add(news);
}
return newsList;
}
/**
* 隨機(jī)生成不同長(zhǎng)度的新聞內(nèi)容
* @param content
* @return
*/
private String getRandomLengthContent(String content) {
Random random = new Random();
int length = random.nextInt(20) + 1;
StringBuilder builder = new StringBuilder();
for (int i = 0; i < length; i++) {
builder.append(content);
}
return builder.toString();
}
. . .
}
到這里蛋济,所有的代碼編寫工作就完成了渤早,運(yùn)行程序,效果如下:
點(diǎn)擊一條新聞瘫俊,會(huì)啟動(dòng)一個(gè)新的活動(dòng)來顯示新聞內(nèi)容:
接下來把程序在平板上運(yùn)行鹊杖,同樣點(diǎn)擊一條新聞悴灵,效果如下:
好了,關(guān)于碎片的內(nèi)容學(xué)習(xí)到這骂蓖,下篇文章將學(xué)習(xí)廣播機(jī)制...