1、基本概念
Fragment,簡稱碎片,是Android 3.0(API 11)提出的,為了兼容低版本,support-v4庫中也開發了一套Fragment API,最低兼容Android 1.6,
Fragment官方的定義
A Fragment represents a behavior or a portion of user interface in an Activity. You can combine multiple fragments in a single activity to build a multi-pane UI and reuse a fragment in multiple activities. You can think of a fragment as a modular section of an activity, which has its own lifecycle, receives its own input events, and which you can add or remove while the activity is running.
根據上面的定義可知:
- Fragment是依賴于Activity的,不能獨立存在的,
- 一個Activity里可以有多個Fragment,
- 一個Fragment可以被多個Activity重用,
- Fragment有自己的生命周期,并能接收輸入事件,
- 我們能在Activity運行時動態地添加或洗掉Fragment,
Fragment的優勢
- 模塊化(Modularity):我們不必把所有代碼全部寫在Activity中,而是把代碼寫在各自的Fragment中,
- 可重用(Reusability):多個Activity可以重用一個Fragment,
- 可適配(Adaptability):根據硬體的螢屏尺寸、螢屏方向,能夠方便地實作不同的布局,這樣用戶體驗更好,
Fragment核心類
- Fragment:Fragment的基類,任何創建的Fragment都需要繼承該類,
- FragmentManager:管理和維護Fragment,他是抽象類,具體的實作類是FragmentManagerImpl,
- FragmentTransaction:對Fragment的添加、洗掉等操作都需要通過事務方式進行,他是抽象類,具體的實作類是BackStackRecord,
Nested Fragment(Fragment內部嵌套Fragment的能力)是Android 4.2提出的,support-fragment庫可以兼容到1.6,通過getChildFragmentManager()能夠獲得子Fragment的FragmentManager,在子Fragment中可以通過getParentFragment()獲得父Fragment,
Fragment基本使用
這里給出Fragment最基本的使用方式,首先,創建繼承Fragment的類,名為Fragment1:
public class Fragment1 extends Fragment{
private static String ARG_PARAM = "param_key";
private String mParam;
private Activity mActivity;
public void onAttach(Context context) {
mActivity = (Activity) context;
mParam = getArguments().getString(ARG_PARAM); //獲取引數
}
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_1, container, false);
TextView view = root.findViewById(R.id.text);
view.setText(mParam);
return root;
}
public static Fragment1 newInstance(String str) {
Fragment1 frag = new Fragment1();
Bundle bundle = new Bundle();
bundle.putString(ARG_PARAM, str);
fragment.setArguments(bundle); //設定引數
return fragment;
}
}
Fragment有很多可以復寫的方法,其中最常用的就是onCreateView(),該方法回傳Fragment的UI布局,需要注意的是inflate()的第三個引數是false,因為在Fragment內部實作中,會把該布局添加到container中,如果設為true,那么就會重復做兩次添加,則會拋如下例外:
Caused by: java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
如果在創建Fragment時要傳入引數,必須要通過setArguments(Bundle bundle)方式添加,setArguments方法必須在fragment創建以后,添加給Activity前完成,而不建議通過為Fragment添加帶引數的建構式,因為通過setArguments()方式添加,在由于記憶體緊張導致Fragment被系統殺掉并恢復(re-instantiate)時能保留這些資料,官方建議如下:
It is strongly recommended that subclasses do not have other constructors with parameters, since these constructors will not be called when the fragment is re-instantiated.
我們可以在Fragment的onAttach()中通過getArguments()獲得傳進來的引數,并在之后使用這些引數,
如果要獲取Activity物件,不建議呼叫getActivity(),而是在onAttach()中將Context物件強轉為Activity物件,
創建完Fragment后,接下來就是把Fragment添加到Activity中,在Activity中添加Fragment的方式有兩種:
1.、靜態添加:在xml中通過的方式添加,缺點是一旦添加就不能在運行時洗掉,
<?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="vertical">
<fragment
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.samples.Fragment1"/>
</LinearLayout>
2、動態添加:運行時添加,這種方式比較靈活,因此建議使用這種方式, 雖然Fragment能在XML中添加,但是這只是一個語法糖而已,Fragment并不是一個View,而是和Activity同一層次的,
首先Activity需要有一個容器存放Fragment,一般是FrameLayout,因此在Activity的布局檔案中加入FrameLayout:
<FrameLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
然后在onCreate()中,通過以下代碼將Fragment添加進Activity中,
if (bundle == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, Fragment1.newInstance("hello world"),"f1")
//.addToBackStack("fname")
.commit();
}
這里需要注意幾點:
-
因為我們使用了support庫的Fragment,因此需要使用getSupportFragmentManager()獲取FragmentManager,通過getSupportFragmentManager()獲取的FragmentManager只能管理Activity里面嵌入的所有一級fragment,
-
add()是對Fragment眾多操作中的一種,還有remove(), replace()等,第一個引數是根容器的id(FrameLayout的id,即”@id/container”),第二個引數是Fragment物件,第三個引數是fragment的tag名,指定tag的好處是后續我們可以通過Fragment1 frag = getSupportFragmentManager().findFragmentByTag(“f1”)從FragmentManager中查找Fragment物件,
在一次事務中,可以做多個操作,比如同時做add().remove().replace(), -
commit()操作是異步的,內部通過mManager.enqueueAction()加入處理佇列,對應的同步方法為commitNow(),commit()內部會有checkStateLoss()操作,如果開發人員使用不當(比如commit()操作在onSaveInstanceState()之后),可能會拋出例外,而commitAllowingStateLoss()方法則是不會拋出例外版本的commit()方法,但是盡量使用commit(),而不要使用commitAllowingStateLoss(),
-
addToBackStack(“fname”)是可選的,FragmentManager擁有回退堆疊(BackStack),類似于Activity的任務堆疊,如果添加了該陳述句,就把該事務加入回退堆疊,當用戶點擊回傳按鈕,會回退該事務(回退指的是如果事務是add(frag1),那么回退操作就是remove(frag1));如果沒添加該陳述句,用戶點擊回傳按鈕會直接銷毀Activity,
-
Fragment有一個常見的問題,即Fragment重疊問題,這是由于Fragment被系統殺掉,并重新初始化時再次將fragment加入activity,因此通過在外圍加if陳述句能判斷此時是否是被系統殺掉并重新初始化的情況,解決方法有三種:
1、在 Activity 提供的 onAttachFragment() 方法中處理:@Override public void onAttachFragment(Fragment fragment) { super.onAttachFragment(fragment); if (fragment instanceof OneFragment){ oneFragment = (OneFragment) fragment; } }2、在創建 Fragment 前添加判斷,判斷是否已經存在:
Fragment tempFragment = getSupportFragmentManager() .findFragmentByTag("OneFragment"); if (tempFragment==null) { oneFragment = OneFragment.newInstance(); ft.add(R.id.fl_content, oneFragment, "OneFragment"); }else { oneFragment = (OneFragment) tempFragment; }3、直接利用 savedInstanceState 判斷即可:
if (savedInstanceState==null) { oneFragment = OneFragment.newInstance(); ft.add(R.id.fl_content, oneFragment, "OneFragment"); }else { oneFragment = (OneFragment) getSupportFragmentManager().findFragmentByTag("OneFragment"); }
Fragment有個常見的例外:
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1341)
at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1352)
at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:595)
at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:574)
該例外出現的原因是:commit()在onSaveInstanceState()后呼叫,首先,onSaveInstanceState()是在Activity有可能被系統回收的情況下,而且是在onPause()之后,onStop()之前呼叫,onRestoreInstanceState()在onStart()之后,onResume()之前,
解釋一下:onSaveInstanceState()是當activity有可能被系統回收的情況下,而且是在onStop()之前,注意是有可能,如果是已經確定會被銷毀,比如用戶按下了回傳鍵,或者呼叫了finish()方法銷毀activity,則onSaveInstanceState不會被呼叫,或者也可以說,此方法只有在activity被例外終止的情況下會被呼叫,
onRestoreInstanceState(BundlesavedInstanceState)只有在activity確實是被系統回收,重新創建activity情況下才會被呼叫,并且是在onStart()之后,onResume()之前的,
因此避免出現該例外的方案有:
- 不要把Fragment事務放在異步執行緒的回呼中,比如不要把Fragment事務放在AsyncTask的onPostExecute(),因此onPostExecute()可能會在onSaveInstanceState()之后執行,
- 逼不得已時使用commitAllowingStateLoss(),
2. Fragment的生命周期
Fragment必須是依存與Activity而存在的,因此Activity的生命周期會直接影響到Fragment的生命周期,

可以看到Fragment比Activity多了幾個額外的生命周期回呼方法:
- onAttach(Context context):Fragment和Activity相關聯時呼叫,如果不是一定要使用具體的宿主 Activity 物件的話,可以使用這個方法或者getContext()獲取 Context 物件,用于解決Context背景關系參考的問題,同時還可以在此方法中可以通過getArguments()獲取到需要在Fragment創建時需要的引數,
- onCreate():Fragment被創建時呼叫,
- onCreateView():創建Fragment的布局,
- onActivityCreated():當Activity完成onCreate()時呼叫,
- onStart():當Fragment可見時呼叫,
- onResume():當Fragment可見且可互動時呼叫,
- onPause():當Fragment不可互動但可見時呼叫,
- onStop():當Fragment不可見時呼叫,
- onDestroyView():當Fragment的UI從視圖結構中移除時呼叫,
- onDestroy():銷毀Fragment時呼叫,
- onDetach():當Fragment和Activity解除關聯時呼叫,
上面的方法中,只有onCreateView()在重寫時不用寫super方法,其他都需要,
因為Fragment是依賴Activity的,因此為了講解Fragment的生命周期,需要和Activity的生命周期方法一起講,即Fragment的各個生命周期方法和Activity的各個生命周期方法的關系和順序,如圖:


我們這里舉個例子來理解Fragment生命周期方法,功能如下:共有兩個Fragment:FragmentTest和FragmentTestReplace,FragmentTest通過點擊按鈕初時加入Activity,點擊F1中的按鈕呼叫replace替換為F2,
activity的初始化時,日志如下:

當點擊Activity中的按鈕在Activity中添加FragmentTest時,日志如下:

在Activity的onCreate()中添加,日志如下:

可以看出:Fragment的onAttach()->onCreate()->onCreateView()->onActivityCreated()->onStart()都是在Activity的onStart()中呼叫的,
接下去分兩種情況,分別是不加addToBackStack()和加addToBackStack(),
- 當點擊按鈕,呼叫replace()將FragmentTest替換為FragmentTestReplace,且不加addToBackStack()時,日志如下:

可以看到,F1最后呼叫了onDestroy()和onDetach(),
- 當點擊按鈕,呼叫replace()將FragmentTest替換為FragmentTestReplace,加addToBackStack()時,日志如下:

可以看到,F1被替換時,最后只調到了onDestroyView(),并沒有呼叫onDestroy()和onDetach(),當用戶點回傳按鈕回退事務時,F1會調onCreateView()->onStart()->onResume(),因此在Fragment事務中加不加addToBackStack()會影響Fragment的生命周期,
FragmentTransaction有一些基本方法,下面給出呼叫這些方法時,Fragment生命周期的變化:
- add(): onAttach()->onCreate()->onCreateView()->onActivityCreated()->onStart()->onResume(),在執行add()時,同一個Fragment不允許被add()兩次
- remove(): onPause()->onStop()->onDestroyView()->onDestroy()->onDetach(),
- replace():相當于新Fragment呼叫add(),舊Fragment呼叫remove(), replace() 方法不會保留 Fragment 的狀態,也就是說諸如 EditText 內容輸入等用戶操作在 remove() 時會消失,分為兩種情況
- 不加addToBackStack(): new onAttach() -> new onCreate() -> old onPause()-> old onStop()-> old onDestroyView()-> old onDestroy()-> old onDetach() -> new onCreateView() -> new onActivityCreated() -> new onStart(),
- 加addToBackStack(): new onAttach() -> new onCreate() -> old onPause()-> old onStop()-> old onDestroyView() -> new onCreateView -> new onActivityCreated() -> new onStart(),
- show(): 不呼叫任何生命周期方法,呼叫該方法的前提是要顯示的Fragment已經被添加到容器,只是純粹把Fragment UI的setVisibility為true,
- hide(): 不呼叫任何生命周期方法,呼叫該方法的前提是要顯示的Fragment已經被添加到容器,只是純粹把Fragment UI的setVisibility為false,
- detach(): onPause()->onStop()->onDestroyView(),
Detach the given fragment from the UI. This is the same state as when it is put on the back stack: the fragment is removed from the UI, however its state is still being actively managed by the fragment manager. When going into this state its view hierarchy is
destroyed.,從中可以看出detach僅僅是銷毀fragment的UI,Fragment還被fragmentManager管理,
-
attach(): onCreateView() -> onActivityCreated -> onStart() -> onResume(),
Re-attach a fragment after it had previously been detached from the UI with detach(android.app.Fragment).意思就是attach重新添加是被detach銷毀的fragment. -
commit():提交事務·每次提交之前,必須通過mFragmentManager.beginTransaction()重新開始一個事務,
Fragment的回傳堆疊
我們知道Activity有任務堆疊,用戶通過startActivity將Activity加入堆疊,點擊回傳按鈕將Activity出堆疊,Fragment也有類似的堆疊,稱為回退堆疊(Back Stack),回退堆疊是由FragmentManager管理的,默認情況下,Fragment事務是不會加入回退堆疊的,如果想將Fragment事務加入回退堆疊,則可以加入addToBackStack(""),如果沒有加入回退堆疊,則用戶點擊回傳按鈕會直接將Activity出堆疊;如果加入了回退堆疊,則用戶點擊回傳按鈕會回滾Fragment事務,
3. Fragment通信
Fragment向Activity傳遞資料
首先,在Fragment中定義介面,并讓Activity實作該介面(具體實作省略):
public interface OnFragmentInteractionListener {
void onItemClick(String str); //將str從Fragment傳遞給Activity
}
在Fragment的onAttach()中,將引數Context強轉為OnFragmentInteractionListener物件:
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnFragmentInteractionListener) {
mListener = (OnFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnFragmentInteractionListener");
}
}
并在Fragment合適的地方呼叫mListener.onItemClick(“hello”)將”hello”從Fragment傳遞給Activity,
FABridge
由于通過介面的方式從Fragment向Activity進行資料傳遞比較麻煩,需要在Fragment中定義interface,并讓Activity實作該interface,FABridge通過@FCallbackId注解的形式免去了這些定義,
在module的build.gradle中添加依賴:
annotationProcessor 'com.zhy.fabridge:fabridge-compiler:1.0.0'
implementation 'com.zhy.fabridge:fabridge-api:1.0.0'
在需要進行Fragment向Activity進行資料傳遞Fragment中定義方法ID,這里為FAB_CLICK,接著在Activity中定義介面,介面必須為public:
@FCallbackId(id = FAB_CLICK)
public void onFabClick(String str) { //方法名任意
mTextView.setText(str + "小哥哥");
}
在Fragment中,通過以下形式呼叫ID=FAB_CLICK的方法(該方法可能在Activity中,也可能在任何類中):
Fabridge.call(mActivity,FAB_ITEM_CLICK,"我是"); //呼叫ID對應的方法,"data"為引數值
Activity向Fragment傳遞資料
如果在創建Fragment時要傳入引數,必須要通過setArguments(Bundle bundle)方式添加,setArguments方法必須在fragment創建以后,添加給Activity前完成,而不建議通過為Fragment添加帶引數的建構式,因為通過setArguments()方式添加,在由于記憶體緊張導致Fragment被系統殺掉并恢復(re-instantiate)時能保留這些資料,
public static Fragment newInstance(String str) {
FragmentTest fragment = new FragmentTest();
Bundle bundle = new Bundle();
bundle.putString(ARG_PARAM, str);
fragment.setArguments(bundle);//設定引數
return fragment;
}
Activity向Fragment傳遞資料比較簡單,獲取Fragment物件,并呼叫Fragment的方法即可,比如要將一個字串傳遞給Fragment,則在Fragment中定義方法:
public void setString(String str) {
this.str = str;
}
并在Activity中呼叫fragment.setString(“hello”)即可,
Fragment之間通信
由于Fragment之間是沒有任何依賴關系的,因此如果要進行Fragment之間的通信,建議通過Activity作為中介,不要Fragment之間直接通信,
4. DialogFragment
DialogFragment是Android 3.0提出的,代替了Dialog,用于實作對話框,他的優點是:即使旋轉螢屏,也能保留對話框狀態,
如果要自定義對話框樣式,只需要繼承DialogFragment,并重寫onCreateView(),該方法回傳對話框UI,這里我們舉個例子,實作進度條樣式的圓角對話框,
public class ProgressDialogFragment extends DialogFragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE); //消除Title區域
getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); //將背景變為透明
setCancelable(false); //點擊外部不可取消
View root = inflater.inflate(R.layout.fragment_progress_dialog, container);
return root;
}
public static ProgressDialogFragment newInstance() {
return new ProgressDialogFragment();
}
}
進度條影片我們使用Lottie實作,Lottie影片從這里找到,使用非常方便,只需要下載JSON影片檔案,然后在XML中寫入:
<com.airbnb.lottie.LottieAnimationView
android:layout_width="wrap_content" //大小根據JSON檔案確定
android:layout_height="wrap_content"
app:lottie_fileName="loader_ring.json" //JSON檔案
app:lottie_loop="true" //回圈播放
app:lottie_autoPlay="true" /> //自動播放
然后通過下面代碼顯示對話框:
ProgressDialogFragment fragment = ProgressDialogFragment.newInstance();
fragment.show(getSupportFragmentManager(), "tag");
//fragment.dismiss();
為了實作圓角,除了在onCreateView()中把背景設為透明,還需要對UI加入背景:
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#ffffff"/>
<corners
android:radius="20dp"/>
</shape>
5. Fragment 處理回傳鍵
雖然Activity作為Fragment的載體,但是在有些特殊(例如按下回傳鍵,Fragment 直接把結果發送到 Activity載體)的情況下,Fragment也不得不處理回傳鍵事件,如果是Activity的話還好說,直接覆寫Activity的onBackPressed即可,但是Fragment卻沒有這個onBackPressed方法,需要自己進行處理,其實在開發的時候都會封裝Fragment和Activity的用于實作自己的邏輯,
新建一個介面:
OnBackPressedpublic interface OnBackPressed {
boolean onBackPressed();
}
封裝一個片段:BaseFragment 實作 OnBackPressedpublic class BaseFragment extends
Fragment implements OnBackPressed {
/**
* fragment 中的回傳鍵
* 默認回傳 false,交給Activity 處理
* 回傳 true:執行fragment中需要執行的邏輯
* 回傳 false:執行Activity中的onBackPressed
*/
@Override
public boolean onBackPressed() {
return false;
}
}
在Activity中的onBackPressed進行判斷即可:
@Override
public void onBackPressed() {
List<Fragment> fragments = getSupportFragmentManager().getFragments();
for (Fragment f : fragments) {
if (f != null && f instanceof BaseFragment&& ((BaseFragment) f).onBackPressed()) {
/*在Fragment中處理回傳事件*/
return;
}
}
super.onBackPressed();
}
自己的Fragment繼承BaseFragment并重寫onBackPressed方法
@Override
public boolean onBackPressed() {
super.onBackPressed();
/*處理回傳事件*/
return true;
}
6.懶加載
懶加載主要用于ViewPager且每頁是Fragment的情況,場景為微信主界面,底部有4個tab,當滑到另一個tab時,先顯示”正在加載”,過一會才會顯示正常界面,
默認情況,ViewPager會快取當前頁和左右相鄰的界面,實作懶加載的主要原因是:用戶沒進入的界面需要有一系列的網路、資料庫等耗資源、耗時的操作,預先做這些資料加載是不必要的,
這里懶加載的實作思路是:用戶不可見的界面,只初始化UI,但是不會做任何資料加載,等滑到該頁,才會異步做資料加載并更新UI,
這里就實作類似微信那種效果,整個UI布局為:底部用PagerBottomTabStrip專案實作,上面是ViewPager,使用FragmentPagerAdapter,邏輯為:當用戶滑到另一個界面,首先會顯示正在加載,等資料加載完畢后(這里用睡眠1秒鐘代替)顯示正常界面,
ViewPager默認快取左右相鄰界面,為了避免不必要的重新資料加載(重復呼叫onCreateView()),因為有4個tab,因此將離線快取的半徑設定為3,即setOffscreenPageLimit(3),
懶加載主要依賴Fragment的setUserVisibleHint(boolean isVisible)方法,當Fragment變為可見時,會呼叫setUserVisibleHint(true);當Fragment變為不可見時,會呼叫setUserVisibleHint(false),且該方法呼叫時機:
- onAttach()之前,呼叫setUserVisibleHint(false),
- onCreateView()之前,如果該界面為當前頁,則呼叫setUserVisibleHint(true),否則呼叫- setUserVisibleHint(false),
- 界面變為可見時,呼叫setUserVisibleHint(true),
- 界面變為不可見時,呼叫setUserVisibleHint(false),
懶加載Fragment的實作:
public class LazyFragment extends Fragment {
private View mRootView;
private boolean mIsInited;
private boolean mIsPrepared;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mRootView = inflater.inflate(R.layout.fragment_lazy, container, false);
mIsPrepared = true;
lazyLoad();
return mRootView;
}
public void lazyLoad() {
if (getUserVisibleHint() && mIsPrepared && !mIsInited) {
//異步初始化,在初始化后顯示正常UI
loadData();
}
}
private void loadData() {
new Thread() {
public void run() {
//1. 加載資料
//2. 更新UI
//3. mIsInited = true
}
}.start();
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
lazyLoad();
}
}
public static LazyFragment newInstance() {
return new LazyFragment();
}
}
注意點:
- 在Fragment中有兩個變數控制是否需要做資料加載:
- mIsPrepared:表示UI是否準備好,因為資料加載后需要更新UI,如果UI還沒有inflate,就不需要做資料加載,因為setUserVisibleHint()會在onCreateView()之前呼叫一次,如果此時呼叫,UI還沒有inflate,因此不能加載資料,
- mIsInited:表示是否已經做過資料加載,如果做過了就不需要做了,因為setUserVisibleHint(true)在界面可見時都會呼叫,如果滑到該界面做過資料加載后,滑走,再滑回來,還是會呼叫setUserVisibleHint(true),此時由于mIsInited=true,因此不會再做一遍資料加載,
- lazyLoad():懶加載的核心類,在該方法中,只有界面可見(getUserVisibleHint()true)、UI準備好(mIsPreparedtrue)、過去沒做過資料加載(mIsInited==false)時,才需要調loadData()做資料加載,資料加載做完后把mIsInited置為true,
布局XML主要分兩個container,一個是初始顯示的狀態,即R.id.container_empty,當資料加載完成,就顯示R.id.container:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:id="@+id/container_empty"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="正在加載"
/>
</RelativeLayout>
<RelativeLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
>
...
</RelativeLayout>
</FrameLayout>
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/277055.html
標籤:其他
