Android開發(fā)學(xué)習(xí)教程(26)- Android Fragment(碎片)
—— 盡吾志也,而不能至者,所以無悔矣
什么是單窗格或多窗格布局?
面板或窗格代表用戶界面的一部分 。術(shù)語窗格是一個(gè)通用術(shù)語,用于描述根據(jù)實(shí)際可用空間將多個(gè)視圖組合成一個(gè)復(fù)合視圖的概念。
如果沒有足夠的可用空間,則僅顯示一個(gè)面板。這通常稱為單窗格布局。
如果有更多可用空間,可以顯示多個(gè)面板。
什么是Fragment?
Fragment是 Android 中的一個(gè)組件,F(xiàn)ragment依賴于Activity運(yùn)行,但有自己的生命周期,通常有自己的用戶界面。
Android設(shè)備存在各種屏幕尺寸和密度。Fragment簡化了組件在不同布局及其邏輯中的重用。你可以為手機(jī)構(gòu)建單窗格布局,為平板電腦構(gòu)建多窗格布局。你還可以使用Fragment來支持智能手機(jī)上橫向和縱向的不同布局。
因?yàn)榭梢詮腁ctivity中動(dòng)態(tài)添加和刪除Fragment。所以Fragment的使用允許設(shè)計(jì)非常靈活的用戶界面。
典型示例是Activity中的項(xiàng)目列表。在平板電腦上,如果單擊項(xiàng)目,你會(huì)立即在右側(cè)的同一屏幕上看到詳細(xì)信息。而在手機(jī)上,你需要跳轉(zhuǎn)到新的詳細(xì)信息屏幕。這在下圖中進(jìn)行了描述。
Fragment的用法
有兩種方法可以將Fragmtn添加到Activity中:動(dòng)態(tài)添加和靜態(tài)添加。
靜態(tài)添加
要靜態(tài)添加Fragment,只需將Fragment嵌入到活動(dòng)的 xml 布局文件中:
<?
xml
?version
=
"1.0"
?encoding
=
"utf-8"
?>
<
LinearLayout
?xmlns:android
=
"http://schemas.android.com/apk/res/android"
????
xmlns:tools
=
"http://schemas.android.com/tools"
????
android:layout_width
=
"fill_parent"
????
android:layout_height
=
"fill_parent"
????
android:baselineAligned
=
"false"
????
android:orientation
=
"horizontal"
>
????
<
fragment
????????
android:id
=
"@+id/listFragment"
????????
class
=
"com.example.myapplication1.MyListFragment"
????????
android:layout_width
=
"0dp"
????????
android:layout_height
=
"match_parent"
????????
android:layout_weight
=
"1"
????????
android:tag
=
"listFragment"
????????
tools:layout
=
"@layout/fragment_rsslist_overview"
?/>
????
<
fragment
????????
android:id
=
"@+id/detailFragment"
????????
class
=
"com.example.myapplication1.DetailFragment"
????????
android:layout_width
=
"0dp"
????????
android:layout_height
=
"match_parent"
????????
android:layout_weight
=
"2"
????????
android:tag
=
"detailFragment"
????????
tools:layout
=
"@layout/fragment_rssitem_detail"
?/>
</
LinearLayout
>
import
?android.os.Bundle;
import
?androidx.appcompat.app.AppCompatActivity;
public
?class
?MainActivityByXml?
extends
?AppCompatActivity?
implements
?MyListFragment.OnItemSelectedListener {
??
@Override
??
protected
?void
?onCreate(Bundle savedInstanceState) {
??????
super
.onCreate(savedInstanceState);
??????
setContentView(R.layout.activity_main_byxml);
??
}
??
@Override
??
public
?void
?onRssItemSelected(String str) {
??????
// DetailFragment fragment = (DetailFragment) getSupportFragmentManager().findFragmentById(R.id.detailFragment);
??????
DetailFragment fragment = (DetailFragment) getSupportFragmentManager().findFragmentByTag(
"detailFragment"
);
??????
fragment.setText(str);
??
}
}
動(dòng)態(tài)添加
動(dòng)態(tài)添加允許你在Activity中添加、刪除和替換片段。
<?
xml
?version
=
"1.0"
?encoding
=
"utf-8"
?>
<
LinearLayout
?xmlns:android
=
"http://schemas.android.com/apk/res/android"
????
android:layout_width
=
"match_parent"
????
android:layout_height
=
"match_parent"
????
android:orientation
=
"horizontal"
>
????
<
FrameLayout
????????
android:id
=
"@+id/listcontainer"
????????
android:layout_width
=
"0dp"
????????
android:layout_height
=
"match_parent"
????????
android:layout_weight
=
"1"
?/>
????
<
FrameLayout
????????
android:id
=
"@+id/detailscontainer"
????????
android:layout_width
=
"0dp"
????????
android:layout_height
=
"match_parent"
????????
android:layout_weight
=
"1"
?/>
</
LinearLayout
>
import
?android.os.Bundle;
import
?androidx.appcompat.app.AppCompatActivity;
import
?androidx.fragment.app.FragmentManager;
import
?androidx.fragment.app.FragmentTransaction;
public
?class
?MainActivityByManager?
extends
?AppCompatActivity?
implements
?MyListFragment.OnItemSelectedListener {
????
private
?FragmentManager fragmentManager;
????
@Override
????
protected
?void
?onCreate(Bundle savedInstanceState) {
????????
super
.onCreate(savedInstanceState);
????????
setContentView(R.layout.activity_main_bymanager);
????????
instanceFragmentManager();
????
}
????
private
?void
?instanceFragmentManager() {
????????
// 獲取FragmentManger
????????
fragmentManager = getSupportFragmentManager();
????????
// 把Fragment add到FrameLayout容器中,并設(shè)置此add進(jìn)來的Fragment的Tag為MyListFragment
????????
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
????????
fragmentTransaction.add(R.id.listcontainer,?
new
?MyListFragment(),?
"MyListFragment"
);
????????
fragmentTransaction.add(R.id.detailscontainer,?
new
?DetailFragment(),?
"DetailFragment"
);
????????
fragmentTransaction.commit();
//??????? // replace Fragment
//??????? FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
//??????? fragmentTransaction.replace(R.id.detailscontainer, new DetailFragment());
//??????? fragmentTransaction.commit();
//
//??????? // remove Fragment
//??????? Fragment fragment = fragmentManager.findFragmentById(R.id.detailscontainer);
//??????? FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
//??????? fragmentTransaction.remove(fragment);
//??????? fragmentTransaction.commit();
????
}
????
@Override
????
public
?void
?onRssItemSelected(String str) {
????????
// 使用 findFragmentById 或者 findFragmentByTag都行,建議還是使用 findFragmentByTag,避免邏輯層與視圖層耦合
????????
DetailFragment fragment = (DetailFragment) getSupportFragmentManager().findFragmentById(R.id.detailscontainer);
//??????? DetailFragment fragment = (DetailFragment) getSupportFragmentManager().findFragmentByTag("DetailFragment");
????????
fragment.setText(str);
????
}
}
import
?android.content.Context;
import
?android.os.Bundle;
import
?android.view.LayoutInflater;
import
?android.view.View;
import
?android.view.ViewGroup;
import
?android.widget.Button;
import
?androidx.fragment.app.Fragment;
public
?class
?MyListFragment?
extends
?Fragment?
implements
?View.OnClickListener {
????
private
?OnItemSelectedListener listener;
????
@Override
????
public
?View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
????????
View view = inflater.inflate(R.layout.fragment_rsslist_overview, container,?
false
);
????????
view.findViewById(R.id.button1).setOnClickListener(
this
);
????????
view.findViewById(R.id.button2).setOnClickListener(
this
);
????????
view.findViewById(R.id.button3).setOnClickListener(
this
);
????????
view.findViewById(R.id.button4).setOnClickListener(
this
);
????????
view.findViewById(R.id.button5).setOnClickListener(
this
);
????????
return
?view;
????
}
????
@Override
????
public
?void
?onClick(View v) {
????????
updateDetail(((Button) v).getText().toString());
????
}
????
public
?interface
?OnItemSelectedListener {
????????
void
?onRssItemSelected(String str);
????
}
????
@Override
????
public
?void
?onAttach(Context context) {
????????
super
.onAttach(context);
????????
if
?(context?
instanceof
?OnItemSelectedListener) {
????????????
listener = (OnItemSelectedListener) context;
????????
}?
else
?{
????????????
throw
?new
?ClassCastException(context.toString() +?
" must implemenet MyListFragment.OnItemSelectedListener"
);
????????
}
????
}
????
@Override
????
public
?void
?onDetach() {
????????
super
.onDetach();
????????
listener =?
null
;
????
}
????
private
?void
?updateDetail(String str) {
????????
listener.onRssItemSelected(str);
????
}
}
import
?android.os.Bundle;
import
?android.view.LayoutInflater;
import
?android.view.View;
import
?android.view.ViewGroup;
import
?android.widget.TextView;
import
?androidx.fragment.app.Fragment;
public
?class
?DetailFragment?
extends
?Fragment {
????
@Override
????
public
?View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
????????
View view = inflater.inflate(R.layout.fragment_rssitem_detail, container,?
false
);
????????
return
?view;
????
}
????
public
?void
?setText(String text) {
????????
TextView view = (TextView) getView().findViewById(R.id.detailsText);
????????
view.setText(text);
????
}
}
查找Fragment實(shí)例
通常我們需要在Activity中查找Fragment實(shí)例用于操作Fragment。有幾種方法可以查找現(xiàn)有的Fragment實(shí)例:
1. 通過ID查找即findFragmentById
2. 通過Tag查找即findFragmentByTag
下面更詳細(xì)地概述了每種方法。
按ID查找即findFragmentById
如果是通過靜態(tài)添加的,這里的ID就表示靜態(tài)xml中fragment標(biāo)簽的id,例如上面示例中的findFragmentById(R.id.detailFragment)。如果是通過動(dòng)態(tài)添加的(大部分時(shí)候都是此方式),ID則表示Fragment被add上去的那個(gè)布局的ID,例如上面示例中的findFragmentById(R.id.detailscontainer)。
按Tag查找即findFragmentById
Tag可以理解為每一個(gè)Fragment的唯一標(biāo)識(shí)符。如果是通過靜態(tài)添加的,這里的Tag就表示靜態(tài)xml中fragment標(biāo)簽的android:tag值,例如上面示例中的findFragmentByTag(“detailFragment”)。如果是通過動(dòng)態(tài)添加的(大部分時(shí)候都是此方式),ID則表示Fragment被add上去時(shí)指定的Tag字符串,例如上面示例中的fragmentTransaction.add(R.id.detailscontainer, new DetailFragment(), “DetailFragment”)、findFragmentByTag(“DetailFragment”)。
Activity Fragment通信
Fragment通常應(yīng)該只與它們的父Activity通信。通過父Activity來管理與其他Fragment的交互。Activity可以看做是每個(gè)Fragment之間交互的控制器。
Fragment和Activity可以通過三種方式進(jìn)行通信:
1. 通過Bundle傳參 – Activity 可以構(gòu)造一個(gè)片段并設(shè)置參數(shù)
2. 通過調(diào)用Fragment成員方法 – Activity 可以調(diào)用Fragment實(shí)例上的方法
3. 通過接口回調(diào) – Fragment可以通過接口在Activity上觸發(fā)偵聽器事件
1. 通過Bundle傳參
在某些情況下,F(xiàn)ragment可能希望接受某些參數(shù)。一種常見的模式是創(chuàng)建一個(gè)靜態(tài)newInstance方法來創(chuàng)建帶有參數(shù)的Fragment:
public
?class
?DemoFragment?
extends
?Fragment {
????
public
?static
?DemoFragment newInstance(
int
?someInt, String someTitle) {
????????
DemoFragment fragmentDemo =?
new
?DemoFragment();
????????
Bundle args =?
new
?Bundle();
????????
args.putInt(
"someInt"
, someInt);
????????
args.putString(
"someTitle"
, someTitle);
????????
fragmentDemo.setArguments(args);
????????
return
?fragmentDemo;
????
}
????
}
這會(huì)將某些參數(shù)設(shè)置到 Fragment 中,之后可以在 onCreate 中使用以下方法訪問參數(shù):
public
?class
?DemoFragment?
extends
?Fragment {
???
@Override
???
public
?void
?onCreate(Bundle savedInstanceState) {
???????
super
.onCreate(savedInstanceState);
???????
int
?SomeInt = getArguments().getInt(
"someInt"
,?
0
);??
???????
String someTitle = getArguments().getString(
"someTitle"
,?
""
);???
???
}
???
}
在 Activity 中動(dòng)態(tài)加載片段:
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
DemoFragment fragmentDemo = DemoFragment.newInstance(
5
,?
"my title"
);
ft.replace(R.id.your_placeholder, fragmentDemo);
ft.commit();
2. 通過調(diào)用Fragment成員方法
如上面示例中的fragment.setText(str)
3. 通過接口回調(diào)
如上面示例中的onRssItemSelected回調(diào)
了解FragmentManager
FragmentManager負(fù)責(zé)Fragment的所有運(yùn)行時(shí)管理,包括添加、刪除、隱藏、顯示或以其他方式在Fragment之間導(dǎo)航。重要的可用方法概述如下:
addOnBackStackChangedListener:為片段返回堆棧的更改添加一個(gè)新的偵聽器。
beginTransaction():創(chuàng)建一個(gè)新事務(wù)以在運(yùn)行時(shí)更改片段。
findFragmentById(
int
?id):通過通常從活動(dòng) XML 布局中擴(kuò)展的 id 查找片段。
findFragmentByTag(String tag):通常為運(yùn)行時(shí)添加的片段按標(biāo)簽查找片段。
popBackStack():從 backstack 中刪除一個(gè)片段。
executePendingTransactions():強(qiáng)制應(yīng)用已提交的事務(wù)。
Fragment生命周期
上面我們說過,F(xiàn)ragment雖然依賴于Activity運(yùn)行,但是Fragment有它自己的生命周期
onAttach():當(dāng)片段連接到活動(dòng)時(shí)調(diào)用。
onCreate():被調(diào)用來進(jìn)行片段的初始創(chuàng)建。
onCreateView():一旦 Fragment 應(yīng)該膨脹視圖,Android 就會(huì)調(diào)用它。
onViewCreated():在之后調(diào)用onCreateView()并確保片段的根視圖是non-
null
. 任何視圖設(shè)置都應(yīng)該在這里進(jìn)行。例如,查看查找、附加偵聽器。
onActivityCreated():當(dāng)宿主活動(dòng)完成其onCreate()方法時(shí)調(diào)用。
onStart():一旦片段準(zhǔn)備好顯示在屏幕上,就會(huì)被調(diào)用。
onResume():分配“昂貴”的資源,例如注冊(cè)位置、傳感器更新等。
onPause():釋放“昂貴”的資源。提交任何更改。
onDestroyView():當(dāng)片段的視圖被銷毀時(shí)調(diào)用,但片段仍然保留在周圍。
onDestroy():當(dāng)片段不再使用時(shí)調(diào)用。
onDetach():當(dāng)片段不再連接到活動(dòng)時(shí)調(diào)用。
通常在onCreateView中設(shè)置視圖,onCreate用于任何數(shù)據(jù)初始化,onActivityCreated用于設(shè)置只有在 Activity 完全創(chuàng)建后才能發(fā)生的事情。
這是一個(gè)如何使用各種片段生命周期事件的示例:
public
?class
?SomeFragment?
extends
?Fragment {
????
ThingsAdapter adapter;
????
FragmentActivity listener;
????????
????
// 此事件在創(chuàng)建Fragment或任何視圖之前最先觸發(fā),當(dāng)Fragment與Activity關(guān)聯(lián)時(shí),將調(diào)用onAttach方法。
????
@Override
????
public
?void
?onAttach(Context context) {
????????
super
.onAttach(context);
????????
if
?(context?
instanceof
?Activity){
????????????
this
.listener = (FragmentActivity) context;
????????
}
????
}
???????
????
// 此事件在為Fragment創(chuàng)建視圖之前觸發(fā),在創(chuàng)建或重新創(chuàng)建Fragment時(shí),會(huì)調(diào)用onCreate方法。
????
@Override
????
public
?void
?onCreate(Bundle savedInstanceState) {
????????
super
.onCreate(savedInstanceState);
????????
ArrayList<Thing> things =?
new
?ArrayList<Thing>();
????????
adapter =?
new
?ThingsAdapter(getActivity(), things);
????
}
????
// 當(dāng)Fragment應(yīng)該動(dòng)態(tài)地或通過XML布局創(chuàng)建其視圖對(duì)象層次結(jié)構(gòu)時(shí),調(diào)用onCreateView方法。
????
@Override
????
public
?View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
????????
return
?inflater.inflate(R.layout.fragment_some, parent,?
false
);
????
}
????
????
// 此事件在onCreateView()之后不久觸發(fā)。只有從onCreateView()返回的視圖非空時(shí),才會(huì)調(diào)用onViewCreated()。任何視圖設(shè)置都建議在這里進(jìn)行。
????
@Override
????
public
?void
?onViewCreated(View view, Bundle savedInstanceState) {
????????
super
.onViewCreated(view, savedInstanceState);
????????
ListView lv = (ListView) view.findViewById(R.id.lvSome);
????????
lv.setAdapter(adapter);
????
}
????????
????
// 此方法在父Activity的onCreate()方法完成后調(diào)用。
????
@Override
????
public
?void
?onActivityCreated(Bundle savedInstanceState) {
????????
super
.onActivityCreated(savedInstanceState);
????
}
????
// 當(dāng)Fragment與Activity失去關(guān)聯(lián)時(shí),將調(diào)用此方法。保存在onAttach中的任何引用都應(yīng)在此處為null,以防止內(nèi)存泄漏。
????
@Override
????
public
?void
?onDetach() {
????????
super
.onDetach();
????????
this
.listener =?
null
;
????
}
}
源碼鏈接:https://yunjunet.cn/876809.html