該文章是一個系列文章,是本人在Android開發的漫漫長途上的一點感想和記錄,我會盡量按照先易后難的順序進行編寫該系列。該系列引用了《Android開發藝術探索》以及《深入理解Android 卷Ⅰ,Ⅱ,Ⅲ》中的相關知識,另外也借鑒了其他的優質博客,在此向各位大神表示感謝,膜拜!!!
前言
上一篇文章中我們使用底部導航+Fragment的方式實現了Android主流App中大都存在的設計。並命名其為“Fragment最佳實踐”,作為想到單獨使用Fragment的用戶來說,這個說法並不誇大,它解決了許多用戶在使用Fragment時產生的這樣那樣可見或不可見的問題。不過Fragment還有其他的使用方式,就是我們本章要介紹的。(本來是介紹ListView的,等着ListView的讀者不好意思了,我會很快更新的。)
注:為什么臨時插入這一章,因為有讀者在上一篇文章中評論了,我覺得大有道理,感謝
這里我就不打碼了,,哈哈哈哈
TabLayout
TabLayout的靜態使用##
TabLayout是Android 5.0之后Google提供的一系列Material Design設計規范中的一個控件。我們在布局文件中可以這樣使用
。
<android.support.design.widget.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
app:tabIndicatorHeight="0dp"
app:tabSelectedTextColor="@color/colorPrimary"
>
<android.support.design.widget.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Tab 1"/>
<android.support.design.widget.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Tab 2"/>
<android.support.design.widget.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Tab 3"/>
</android.support.design.widget.TabLayout>
TabLayout間接繼承於ViewGroup,其內可包含0到n個TabItem,這個TabItem就是我們經常使用的標簽,其是個自定義View
,這樣我們就定義了一個包含3個標簽頁的TabLayout。其運行結果如下圖:
TabLayout的動態使用##
在布局文件中我們可以很方便定義頂部/底部 導航的布局。我們來看一下在代碼中的使用
public class TabActivity extends AppCompatActivity {
@BindView(R.id.tab_layout)
TabLayout mTabLayout;
@BindView(R.id.view_pager)
ViewPager mViewPager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tab);
ButterKnife.bind(this);
mTabLayout.addTab(mTabLayout.newTab().setText("Tab 1"));
mTabLayout.addTab(mTabLayout.newTab().setText("Tab 2"));
mTabLayout.addTab(mTabLayout.newTab().setText("Tab 3"));
//為TabLayout添加Tab選擇事件監聽
mTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {//當標簽被選擇時回調
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {//當標簽從選擇變為非選擇時回調
}
@Override
public void onTabReselected(TabLayout.Tab tab) {//當標簽被重新選擇時回調
}
});
}
}
關於運行結果我就不上圖了,跟上面的運行結果是一樣的。
TabLayout的更多屬性##
關於TabLayout的更多屬性以及使用的說明請查看其官方文檔。在這里我們只關心TabLayout+ViewPager的化學反應,這個組合也是我們平常在開發中使用最多的。在此之前我們先介紹ViewPager
ViewPager
先看看官方對ViewPager的說明
/*
Layout manager that allows the user to flip left and right
through pages of data. You supply an implementation of a
{@link PagerAdapter} to generate the pages that the view shows.
ViewPager is most often used in conjunction with {@link android.app.Fragment}
There are standard adapters implemented for using fragments with the ViewPager,
which cover the most common use cases. These are
{@link android.support.v4.app.FragmentPagerAdapter} and
{@link android.support.v4.app.FragmentStatePagerAdapter};*/
public class ViewPager extends ViewGroup {
}
上面英文的大致意思是ViewPager是一個布局管理類,這個類呢允許用戶左右翻轉頁面。你必須實現一個PagerAdapter來生成這些顯示的頁面。ViewPager經常和Fragment一起使用。而且呢Google非常貼心的提供了兩個類FragmentPagerAdapter和FragmentStatePagerAdapter來應付那些一般場景。
其實從ViewPager的說明中,我們基本上就能知道ViewPager是什么以及如何使用了。
PagerAdapter
ViewPager繼承於ViewGroup,官方指導中就說了,你要自己實現PagerAdapter來生成顯示的頁面,那么我們來看看這個PagerAdapter
/**
* Base class providing the adapter to populate pages inside of
* a {@link ViewPager}. You will most likely want to use a more
* specific implementation of this, such as
* {@link android.support.v4.app.FragmentPagerAdapter} or
* {@link android.support.v4.app.FragmentStatePagerAdapter}.
*
* <p>When you implement a PagerAdapter, you must override the following methods
* at minimum:</p>
* <ul>
* <li>{@link #instantiateItem(ViewGroup, int)}</li>
* <li>{@link #destroyItem(ViewGroup, int, Object)}</li>
* <li>{@link #getCount()}</li>
* <li>{@link #isViewFromObject(View, Object)}</li>
* </ul>
* /
public abstract class PagerAdapter {
}
其實我們在看一個不太了解的類的時候,通過源碼上的關於這個類的說明就可以知道很多信息了。關於PagerAdapter的說明就是如此。
先說了一下PagerAdapter的作用,是一個基類提供適配器給ViewPager中的頁面,如果你想使用特定的實現類,那么你可以看兩個類FragmentPagerAdapter和FragmentStatePagerAdapter,這兩個類繼承了PagerAdapter,並實現了其抽象方法。
后面一段的意思是你如果想自定義你自己的PagerAdapter,那么你最少要實現這4個方法
-
instantiateItem(ViewGroup, int)
-
destroyItem(ViewGroup, int, Object)
-
getCount()
-
isViewFromObject(View, Object)
下面我們以代碼的形式,說明這4個方法的含義以及如何使用
private class MyViewPagerAdapter extends PagerAdapter {
/**
* 獲取View的總數
*
* @return View總數
*/
@Override
public int getCount() {
return 0;
}
/**
* 為給定的位置創建相應的View。創建View之后,需要在該方法中自行添加到container中。
*
* @param container ViewPager本身
* @param position 給定的位置
* @return 提交給ViewPager進行保存的實例對象
*/
@Override
public Object instantiateItem(ViewGroup container, int position) {
return super.instantiateItem(container, position);
}
/**
* 給定的位置移除相應的View。
*
* @param container ViewPager本身
* @param position 給定的位置
* @param object 在instantiateItem中提交給ViewPager進行保存的實例對象
*/
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
super.destroyItem(container, position, object);
}
/**
* 確認View與實例對象是否相互對應。ViewPager內部用於獲取View對應的ItemInfo。
*
* @param view ViewPager顯示的View內容
* @param object 在instantiateItem中提交給ViewPager進行保存的實例對象
* @return 是否相互對應
*/
@Override
public boolean isViewFromObject(View view, Object object) {
return false;
}
}
這4個方法是必須的,,另外還有一些不是必須,但是可能會用到的
/**
* 當ViewPager的內容有所變化時,進行調用。
*
* @param container ViewPager本身
*/
@Override
public void startUpdate(ViewGroup container) {
super.startUpdate(container);
}
/**
* ViewPager調用該方法來通知PageAdapter當前ViewPager顯示的主要項,提供給用戶對主要項進行操作的方法。
*
* @param container ViewPager本身
* @param position 給定的位置
* @param object 在instantiateItem中提交給ViewPager進行保存的實例對象
*/
@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
super.setPrimaryItem(container, position, object);
}
/**
* 較多的用於Design庫中的TabLayout與ViewPager進行綁定時,提供顯示的標題。
*
* @param position 給定的位置
* @return 顯示的標題
*/
@Override
public CharSequence getPageTitle(int position) {
return super.getPageTitle(position);
}
FragmentPagerAdapter
上面呢只是列舉說明了一下PagerAdapter,看起來有些枯燥,都是些說明,那么我們來看一下實踐,ViewPager通暢跟Fragment一起使用,即其所管理的頁面通暢是Fragment,所以Google提供了兩個適配器FragmentPagerAdapter和FragmentStatePagerAdapter,我們這節分析FragmentPagerAdapter。
/**
*真是不看不知道,一看嚇一跳。FragmentPagerAdapter也是個抽象類,
*
*/
public abstract class FragmentPagerAdapter extends PagerAdapter {
private static final String TAG = "FragmentPagerAdapter";
private static final boolean DEBUG = false;
private final FragmentManager mFragmentManager;
private FragmentTransaction mCurTransaction = null;
private Fragment mCurrentPrimaryItem = null;
public FragmentPagerAdapter(FragmentManager fm) {
mFragmentManager = fm;
}
/**
*抽象方法,看來這個函數要子類自己實現了
*
* @param position ViewPager中Item的位置
* @return 位置相關聯的Fragment
*/
public abstract Fragment getItem(int position);
@Override
public void startUpdate(ViewGroup container) {
if (container.getId() == View.NO_ID) {
throw new IllegalStateException("ViewPager with adapter " + this
+ " requires a view id");
}
}
/**
* 為給定的位置創建相應的fragment。創建fragment之后,需要在該方法中自行添加到container中。
*
* @param container ViewPager本身
* @param position 給定的位置
* @return 提交給ViewPager進行保存的實例對象,這里是Fragment
*/
@Override
public Object instantiateItem(ViewGroup container, int position) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
final long itemId = getItemId(position);
String name = makeFragmentName(container.getId(), itemId);
Fragment fragment = mFragmentManager.findFragmentByTag(name);
if (fragment != null) {
if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
mCurTransaction.attach(fragment);
} else {
fragment = getItem(position);
if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
mCurTransaction.add(container.getId(), fragment,
makeFragmentName(container.getId(), itemId));
}
if (fragment != mCurrentPrimaryItem) {
fragment.setMenuVisibility(false);
fragment.setUserVisibleHint(false);
}
return fragment;
}
/**
* 移除給定的位置相應的fragment。
*
* @param container ViewPager本身
* @param position 給定的位置
* @param object 在instantiateItem中提交給ViewPager進行保存的實例對象
*/
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
if (DEBUG) Log.v(TAG, "Detaching item #" + getItemId(position) + ": f=" + object
+ " v=" + ((Fragment)object).getView());
mCurTransaction.detach((Fragment)object);
}
@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
Fragment fragment = (Fragment)object;
if (fragment != mCurrentPrimaryItem) {
if (mCurrentPrimaryItem != null) {
mCurrentPrimaryItem.setMenuVisibility(false);
mCurrentPrimaryItem.setUserVisibleHint(false);
}
if (fragment != null) {
fragment.setMenuVisibility(true);
fragment.setUserVisibleHint(true);
}
mCurrentPrimaryItem = fragment;
}
}
@Override
public void finishUpdate(ViewGroup container) {
if (mCurTransaction != null) {
mCurTransaction.commitNowAllowingStateLoss();
mCurTransaction = null;
}
}
@Override
public boolean isViewFromObject(View view, Object object) {
return ((Fragment)object).getView() == view;
}
@Override
public Parcelable saveState() {
return null;
}
@Override
public void restoreState(Parcelable state, ClassLoader loader) {
}
/**
* @param position ViewPager中Item的位置
* @return 唯一的ItemID
*/
public long getItemId(int position) {
return position;
}
private static String makeFragmentName(int viewId, long id) {
return "android:switcher:" + viewId + ":" + id;
}
}
代碼比較少,總共也就100多行,邏輯也比較清晰明了,我們來着重分析instantiateItem和destroyItem
/**
* 為給定的位置創建相應的fragment。創建fragment之后,需要在該方法中自行添加到container中。
*
* @param container ViewPager本身
* @param position 給定的位置
* @return 提交給ViewPager進行保存的實例對象,這里是Fragment
*/
@Override
public Object instantiateItem(ViewGroup container, int position) {
//開啟事務
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
//得到指定位置Item的ID
final long itemId = getItemId(position);
//根據id和ViewPager的ID生成item的name
String name = makeFragmentName(container.getId(), itemId);
//以name為Tag查找對應的Fragment
Fragment fragment = mFragmentManager.findFragmentByTag(name);
if (fragment != null) {//如果找到了
if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
//調用事務的attach
mCurTransaction.attach(fragment);
} else {//沒找到
//通過我們重寫的getItem方法得到相應fragment
fragment = getItem(position);
if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
//調用事務的add方法,並設置Tag
mCurTransaction.add(container.getId(), fragment,
makeFragmentName(container.getId(), itemId));
}
//如果frament不等於當前主要的Item
if (fragment != mCurrentPrimaryItem) {
//設置其Menu不可見
fragment.setMenuVisibility(false);
//設置其不可見
fragment.setUserVisibleHint(false);
}
return fragment;
}
instantiateItem方法主要功能是為ViewPager生成Item。
那么destroyItem方法的主要功能是銷毀ViwePager內的Item
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
//調用事務的detach方法
mCurTransaction.detach((Fragment)object);
}
FragmentStatePagerAdapter
關於FragmentStatePagerAdapter,讀者可自行分析,代碼也不長。需要注意的地方是,兩者對於destroyItem的不同實現
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
Fragment fragment = (Fragment) object;
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
if (DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + object
+ " v=" + ((Fragment)object).getView());
while (mSavedState.size() <= position) {
mSavedState.add(null);
}
mSavedState.set(position, fragment.isAdded()
? mFragmentManager.saveFragmentInstanceState(fragment) : null);
mFragments.set(position, null);
//調用事務的remove方法
mCurTransaction.remove(fragment);
}
小結
ViewPager是個ViewGroup,與其他布局LinearLayout或者其他任意的ViewGroup並無本質的不同,它被Google建議與Fragment結伴使用,也是說ViewPager所包裹的是Fragment布局。ViewPager需要適配器PagerAdapter操作Fragment,這一點就像ListView需要適配器操作其內部的Item一樣。
適配器PagerAdapter是個抽象類,並且依照官方說明,我們必須至少實現其4個重要方法。4個方法可能太多,所以Google提供了FragmentPagerAdapter以及FragmentStatePagerAdapter,這兩個也是抽象類,不過我們的自定義Adapter只需要實現其中的getItem(int position)方法即可。
關於FragmentPagerAdapter以及FragmentStatePagerAdapter的不同,我這里再總結一下。FragmentPagerAdapter銷毀item的時候最終調用FragmentTransaction的detach()方法,使用detach()會將view從viewtree中刪除,和FragmentStatePagerAdapter中使用的remove()不同,此時fragment的狀態依然保持着,在使用attach()時會再次調用onCreateView()來重繪視圖,注意使用detach()后fragment.isAdded()方法將返回false。
實例##
更改后的TabActivity對應的布局文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
<!--ViewPager-->
<android.support.v4.view.ViewPager
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
>
</android.support.v4.view.ViewPager>
<!--分割線-->
<ImageView
android:id="@+id/image_1"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#919292"
android:layout_above="@+id/tab_layout"/>
<!--TabLayout-->
<android.support.design.widget.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
app:tabIndicatorHeight="0dp"
app:tabSelectedTextColor="@color/colorPrimary"
>
</android.support.design.widget.TabLayout>
</RelativeLayout>
更改后的TabActivity
public class TabActivity extends AppCompatActivity {
@BindView(R.id.tab_layout)
TabLayout mTabLayout;
@BindView(R.id.view_pager)
ViewPager mViewPager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tab);
ButterKnife.bind(this);
mTabLayout.addTab(mTabLayout.newTab().setText("Tab 1"));
mTabLayout.addTab(mTabLayout.newTab().setText("Tab 2"));
mTabLayout.addTab(mTabLayout.newTab().setText("Tab 3"));
//自定義的Adapter繼承自FragmentPagerAdapter
final PagerAdapter adapter = new PagerAdapter
(getSupportFragmentManager(), mTabLayout.getTabCount());
//ViewPager設置Adapter
mViewPager.setAdapter(adapter);
//為ViewPager添加頁面改變監聽
mViewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(mTabLayout));
//為TabLayout添加Tab選擇監聽
mTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
mViewPager.setCurrentItem(tab.getPosition());
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
}
}
而我們自定義的MyPagerAdapter也非常簡單
public class MyPagerAdapter extends FragmentPagerAdapter {
//fragment的數量
int nNumOfTabs;
public MyPagerAdapter(FragmentManager fm, int nNumOfTabs)
{
super(fm);
this.nNumOfTabs=nNumOfTabs;
}
/**
* 重寫getItem方法
*
* @param position 指定的位置
* @return 特定的Fragment
*/
@Override
public Fragment getItem(int position) {
switch(position)
{
case 0:
GoodsFragment tab1=new GoodsFragment();
return tab1;
case 1:
CategoryFragment tab2=new CategoryFragment();
return tab2;
case 2:
TaskFragment tab3=new TaskFragment();
return tab3;
}
return null;
}
/**
* 重寫getCount方法
*
* @return fragment的數量
*/
@Override
public int getCount() {
return nNumOfTabs;
}
}
ViewPager預加載與網絡請求#
ViewPager的預加載機制##
ViewPager可通過setOffscreenPageLimit(int limit)函數設置ViewPager預加載的View數目
public void setOffscreenPageLimit(int limit) {
//DEFAULT_OFFSCREEN_PAGES=1
if (limit < DEFAULT_OFFSCREEN_PAGES) {
Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to "
+ DEFAULT_OFFSCREEN_PAGES);
limit = DEFAULT_OFFSCREEN_PAGES;
}
if (limit != mOffscreenPageLimit) {
mOffscreenPageLimit = limit;
populate();
}
}
可以看到該函數的源碼,當我們傳入的limit<1時,limit還是被設置為1,當limit與成員變量mOffscreenPageLimit的值不同時(成員變量mOffscreenPageLimit的默認值為1),更新成員變量mOffscreenPageLimit的值,然后調用populate()函數。
而這個populate()函數就是給我們的ViewPager准備緩存頁面並顯示當前頁面用的。
假如說我采用下面的方法調用setOffscreenPageLimit(2),此時ViewPager的簡單示意圖
注:從上面的代碼也可以看出ViewPager最少會預加載一個頁面。在本例中,也是我們在顯示TAB1的時候,ViewPager已經加載了TAB2,具體方式是通過instantiateItem方法,該方法內部調用了我們重寫的getItem方法,TAB2所表示的Fragment的onCreateView等相關生命周期方法會被回調。
ViewPager的網絡請求##
ViewPager的預加載機制其實在某些時候是個很讓人不爽的問題,比如我們在Fragment做網絡請求數據的時候,我們網絡請求的代碼通常會放在onCreateView中,我們只是打開第1個Fragment,但是由於ViewPager會加載第2個Fragment,可能也執行了第2個Fragment的網絡請求代碼。
而避免上述問題的主要依靠
public void setUserVisibleHint(boolean isVisibleToUser)
setUserVisibleHint(boolean isVisibleToUser)是Fragment中的一個回調函數。當前Fragment可見時,setUserVisibleHint()回調,其中isVisibleToUser=true。當前Fragment由可見到不可見或實例化時,setUserVisibleHint()回調,其中isVisibleToUser=false。
setUserVisibleHint(boolean isVisibleToUser)調用時機
-
在Fragment實例化,即在ViewPager中,由於ViewPager默認會預加載左右兩個頁面。此時預加載頁面回調的生命周期流程:setUserVisibleHint() -->onAttach() --> onCreate()-->onCreateView()--> onActivityCreate() --> onStart() --> onResume()
此時,setUserVisibleHint() 中的參數為false,因為不可見。
-
在Fragment可見時,即ViewPager中滑動到當前頁面時,因為已經預加載過了,之前生命周期已經走到onResume() ,所以現在只會回調:setUserVisibleHint()。
此時,setUserVisibleHint() 中的參數為true,因為可見。
-
在Fragment由可見變為不可見,即ViewPager由當前頁面滑動到另一個頁面,因為還要保持當前頁面的預加載過程,所以只會回調:setUserVisibleHint()。
此時,setUserVisibleHint() 中的參數為false,因為不可見。
-
由TabLayout直接跳轉到一個未預加載的頁面,此時生命周期的回調過程:setUserVisibleHint() -->setUserVisibleHint() -->onAttach() --> onCreate()-->onCreateView()--> onActivityCreate() --> onStart()
--> onResume()此時回調了兩次setUserVisibleHint() ,一次代表初始化時,傳入參數是false,一次代表可見時,傳入參數是true。這種情況比較特殊。
總結:無論何時,setUserVisibleHint()都是先於其他生命周期的調用,並且初始化時調用,可見時調用,由可見轉換成不可見時調用,一共三次時機。
ViewPager的網絡請求的優化實現###
我們在使用ViewPager+Fragment顯示數據的時候,我們通常會把網絡請求的操作放在onCreateView->onResume之間的生命周期內。這可能帶來的問題我們上面已經探討了。那么怎么解決這個問題呢?
本篇總結
我們在本篇博客中比較詳細的探討了TabLayout+ViewPager+Fragment的使用,我們在許多主流App中都能看到這種頂部、底部導航的效果,並且在此基礎上我們探討了TabLayout+ViewPager+Fragment網絡數據加載問題。
我們希望Fragment可見時加載網絡數據,不可見時不進行或者取消網絡請求。
public abstract class BaseFragment extends Fragment {
protected View rootView;
private Unbinder mUnbinder;
//當前Fragment是否處於可見狀態標志,防止因ViewPager的緩存機制而導致回調函數的觸發
private boolean isFragmentVisible;
//是否是第一次開啟網絡加載
public boolean isFirst;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if (rootView == null)
rootView = inflater.inflate(getLayoutResource(), container, false);
mUnbinder = ButterKnife.bind(this, rootView);
initView();
//可見,但是並沒有加載過
if (isFragmentVisible && !isFirst) {
onFragmentVisibleChange(true);
}
return rootView;
}
//獲取布局文件
protected abstract int getLayoutResource();
//初始化view
protected abstract void initView();
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
isFragmentVisible = true;
}
if (rootView == null) {
return;
}
//可見,並且沒有加載過
if (!isFirst&&isFragmentVisible) {
onFragmentVisibleChange(true);
return;
}
//由可見——>不可見 已經加載過
if (isFragmentVisible) {
onFragmentVisibleChange(false);
isFragmentVisible = false;
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
mUnbinder.unbind();
}
/**
* 當前fragment可見狀態發生變化時會回調該方法
*
* 如果當前fragment是第一次加載,等待onCreateView后才會回調該方法,其它情況回調時機跟 {@link #setUserVisibleHint(boolean)}一致
* 在該回調方法中你可以做一些加載數據操作,甚至是控件的操作.
*
* @param isVisible true 不可見 -> 可見
* false 可見 -> 不可見
*/
protected void onFragmentVisibleChange(boolean isVisible) {
}
}
我們設計抽象基類BaseFragment,所有的公共行為我們都可以在這個基類中定義,那么我們的Fragment是否可見就是其中的一種行為,所以我們上面重寫了Fragment的setUserVisibleHint方法。
public class GoodsFragment extends BaseFragment {
@Override
protected void onFragmentVisibleChange(boolean isVisible) {
if(isVisible){
//可見,並且是第一次加載
lazyLoad();
}else{
//取消加載
}
}
private void lazyLoad() {
if (!isFirst) {
isFirst = true;
}
}
@Override
protected int getLayoutResource() {
return R.layout.fragment_goods;
}
@Override
protected void initView() {
}
}
我們設計GoodsFragment繼承BaseFragment並重寫其onFragmentVisibleChange以控制自身的網絡請求。
本篇總結
本篇為讀者介紹了另外一種導航頁切換的實現,我們使用TabLayout+ViewPager+Fragment的方式,其中讀者需要重點理解以下幾點
- ViewPager是個ViewGroup,它所關機的布局就是通常是我們的Fragment布局。
- ViewPager的預加載機制、可能帶來的問題及如何解決。
- 理解PagerAdapter,以及如何實現它
- 理解Google提供了兩個特定場景的PagerAdapter實現類FragmentPagerAdapter以及FragmentStatePagerAdapter,以及他們的主要區別
下篇預告
下篇打算往Fragment中加點東西,ListView
此致,敬禮