Markdown版本筆記 | 我的GitHub首頁 | 我的博客 | 我的微信 | 我的郵箱 |
---|---|---|---|---|
MyAndroidBlogs | baiqiantao | baiqiantao | bqt20094 | baiqiantao@sina.com |
目錄
簡介
PagerAdapter
FragmentPagerAdapter
FragmentStatePagerAdapter
ViewPager 的預加載
getCount 的返回值
如何自定義指示器
代碼
Activity
Fragment
Activity 的布局
Fragment 的布局
測試
啟動后
滑到第二個頁面
滑到第一個頁面
最小化后再回到前台
再次滑到第二個頁面
在第二個頁面橫豎屏切換
在第一個頁面橫豎屏切換
簡介
PagerAdapter 是android.support.v4包中的類,它的子類有FragmentPagerAdapter, FragmentStatePagerAdapter,相比通用的 PagerAdapter,后兩個adapter 專注於每一頁均為 Fragment 的情況。
android.support.v4.app.Fragment;
android.support.v4.app.FragmentActivity;
android.support.v4.app.FragmentManager;
android.support.v4.app.FragmentPagerAdapter;
android.support.v4.app.FragmentStatePagerAdapter;
android.support.v4.view.PagerAdapter;
android.support.v4.view.ViewPager;
android.support.v4.view.ViewPager.OnPageChangeListener;
FragmentPagerAdapter
類內的每一個生成的 Fragment 都將保存在內存之中,因此適用於那些相對靜態、數量也比較少的那種。比如說一個有3-5個tab標簽的fragment滑動界面,FragmentPagerAdapter 會對我們瀏覽過所有Fragment進行緩存
,保存這些界面的臨時狀態,這樣當我們左右滑動的時候,界面切換更加的流暢。但是,這樣也會增加程序占用的內存。
注意,上面所說的FragmentPagerAdapter只是會把【Fragment本身】保存在內存中,而不會把【Fragment中的View】也保存在內存中。實際上,除了當前頁面及左右相鄰兩個頁面中的Fragment中的【View】會被保存到內存中外,其余Fragment中的【View】都會被銷毀。也即,所有瀏覽過的Fragment的【onDestroy】方法正常情況下都不會被調用,但是除了當前頁面及左右相鄰兩個頁面外,Fragment的【onDestroyView】方法都會被調用。
如果需要處理有很多頁,並且數據動態性較大、占用內存較多的情況,應該使用 FragmentStatePagerAdapter
。
FragmentStatePagerAdapter也是PagerAdapter的子類,這個適配器對實現多個Fragment界面的滑動是非常有用的。它的工作方式和listview是非常相似的,當Fragment對用戶不可見的時候,整個Fragment會被銷毀
(當然,為了保證滑動時流暢,和所有PagerAdapter一樣,左右兩個頁面的Fragment會被保留),而不像FragmentPagerAdapter那樣,只是銷毀Fragment中的View。
PagerAdapter
PageAdapter 是 ViewPager 的支持者,ViewPager 將調用它來取得所需顯示的頁,而 PageAdapter 也會在數據變化時通知 ViewPager。這個類也是 FragmentPagerAdapter 以及 FragmentStatePagerAdapter 的基類。如果繼承自該類,至少需要實現 instantiateItem(), destroyItem(), getCount() 以及 isViewFromObject()。
getItemPosition()
- 該函數用以返回給定對象的位置,給定對象是由 instantiateItem() 的返回值。
- 在 ViewPager.dataSetChanged() 中將對該函數的返回值進行判斷,以決定是否最終觸發 PagerAdapter.instantiateItem() 函數。
- 在 PagerAdapter 中的實現是直接返回 POSITION_UNCHANGED,如果該函數不被重載,則會一直返回 POSITION_UNCHANGED,從而導致 ViewPager.dataSetChanged() 被調用時,認為不必觸發 PagerAdapter.instantiateItem()。很多人因為沒有重載該函數,而導致調用 PagerAdapter.notifyDataSetChanged() 后什么都沒有發生。
Called when the host view is attempting to determine確定 if an item's position has changed. Returns POSITION_UNCHANGED if the position of the given item has not changed or POSITION_NONE if the item is no longer present存在 in the adapter.
The default implementation assumes that items will never change position and always returns POSITION_UNCHANGED.
@param object Object representing an item, previously returned by a call to instantiateItem(View, int).
@return object's new position index from [0, getCount()-1], POSITION_UNCHANGED if the object's position has not changed, or POSITION_NONE if the item is no longer present.
public int getItemPosition(@NonNull Object object) {
return POSITION_UNCHANGED;
}
instantiateItem()
在每次 ViewPager 需要一個用以顯示的 Object 的時候,該函數都會被 ViewPager.addNewItem() 調用。
這個方法雖然不是抽象方法,但是必須重寫,否則默認實現是拋出異常。
Create the page for the given position.
The adapter is responsible負責 for adding the view to the container given here, although it only must ensure this is done by the time it returns from finishUpdate(ViewGroup).
@param container The containing View in which the page will be shown.
@param position The page position to be instantiated.
@return Returns an Object representing the new page. This does not need to be a View, but can be some other container of the page.
public Object instantiateItem(ViewGroup container, int position) {
return instantiateItem((View) container, position);
}
public Object instantiateItem(View container, int position) {
throw new UnsupportedOperationException("Required method instantiateItem was not overridden");
}
destroyItem
用於銷毀一個 page。
這個方法雖然不是抽象方法,但是必須重寫,否則默認實現是拋出異常。
Remove a page for the given position.
The adapter is responsible負責 for removing the view from its container, although it only must ensure this is done by the time it returns from finishUpdate(ViewGroup).
@param container The containing View from which the page will be removed.
@param position The page position to be removed.
@param object The same object that was returned by instantiateItem(View, int).
public void destroyItem(ViewGroup container, int position, Object object) {
destroyItem((View) container, position, object);
}
public void destroyItem(View container, int position, Object object) {
throw new UnsupportedOperationException("Required method destroyItem was not overridden");
}
notifyDataSetChanged()
在數據集發生變化的時候,一般 Activity 會調用 PagerAdapter.notifyDataSetChanged(),以通知 PagerAdapter,而 PagerAdapter 則會通知在自己這里注冊過的所有 DataSetObserver,其中之一就是在 ViewPager.setAdapter() 中注冊過的 PageObserver。PageObserver 則進而調用 ViewPager.dataSetChanged(),從而導致 ViewPager 開始觸發更新其內含 View 的操作。
FragmentPagerAdapter
FragmentPagerAdapter 繼承自 PagerAdapter。相比通用的 PagerAdapter,該類更專注於每一頁均為 Fragment 的情況。如文檔所述, 該類內的每一個生成的 Fragment 都將保存在內存之中,因此適用於那些相對靜態的頁,數量也比較少的那種;如果需要處理有很多頁,並且數據動態性較大、占用內存較多的情況,應該使用FragmentStatePagerAdapter。FragmentPagerAdapter 重載實現了幾個必須的函數,因此來自 PagerAdapter 的函數,我們只需要實現 getCount() 即可。且由於 FragmentPagerAdapter.instantiateItem() 的實現中,調用了一個新增的虛函數 getItem(),因此,我們還至少需要實現一個 getItem()。因此,總體上來說,相對於繼承自 PagerAdapter,更方便一些。
getItem()
該類中新增的一個虛函數
,函數的目的為生成新的 Fragment 對象,重載該函數時需要注意這一點。在需要時,該函數將被 instantiateItem() 所調用。
虛函數是C++中的概念,簡單地說,那些被virtual關鍵字修飾的成員函數,就是虛函數。虛函數的作用,用專業術語來解釋就是實現多態性(將接口與實現進行分離);用形象的語言來解釋就是實現以共同的方法,但因個體差異而采用不同的策略。
如果需要向 Fragment 對象傳遞相對靜態的數據時,我們一般通過 Fragment.setArguments() 來進行,這部分代碼應當放到 getItem(),它們只會在新生成 Fragment 對象時執行一遍
。
如果需要在
生成 Fragment 對象后
,將數據集里面一些動態的數據傳遞給該 Fragment,那么,這部分代碼不適合放到 getItem() 中。因為當數據集發生變化時,往往對應的 Fragment 已經生成,如果傳遞數據部分代碼放到了 getItem() 中,這部分代碼將不會被調用
。這也是為什么很多人發現調用 PagerAdapter.notifyDataSetChanged() 后,getItem() 沒有被調用的一個原因。
//Return the Fragment associated with a specified position.
public abstract Fragment getItem(int position);
instantiateItem()
函數中判斷一下要生成的 Fragment 是否已經生成過了,如果生成過了,就使用舊的,舊的將被 attach;如果沒有,就調用 getItem() 生成一個新的,新的對象將被 FragmentTransation.add()。
FragmentPagerAdapter 會將所有生成的 Fragment 對象通過 FragmentManager 保存起來
備用,以后需要該 Fragment 時,都會從 FragmentManager 讀取,而不會再次調用 getItem() 方法
。
如果需要在生成 Fragment 對象后,將數據集中的一些數據傳遞給該 Fragment,這部分代碼應該放到這個函數的重載里。在我們繼承的子類中,重載該函數,並調用 FragmentPagerAdapter.instantiateItem() 取得該函數返回 Fragment 對象,然后,我們通過該 Fragment 對象中對應的方法,將數據傳遞過去,然后返回該對象。否則,如果將這部分傳遞數據的代碼放到 getItem()中,在 PagerAdapter.notifyDataSetChanged() 后,這部分數據設置代碼將不會被調用。
public Object instantiateItem(ViewGroup container, int position) {
if (mCurTransaction == null) mCurTransaction = mFragmentManager.beginTransaction();
final long itemId = getItemId(position);
// Do we already have this fragment?
String name = makeFragmentName(container.getId(), itemId);
Fragment fragment = mFragmentManager.findFragmentByTag(name);
if (fragment != null) {
mCurTransaction.attach(fragment); //非首次初始化為attach
} else {
fragment = getItem(position); //首次初始化為add
mCurTransaction.add(container.getId(), fragment, makeFragmentName(container.getId(), itemId));
}
if (fragment != mCurrentPrimaryItem) {
fragment.setMenuVisibility(false);
FragmentCompat.setUserVisibleHint(fragment, false); //非常重要的 setUserVisibleHint 冒出來了
}
return fragment;
}
destroyItem()
該函數被調用后,會對 Fragment 進行 FragmentTransaction.detach()。這里不是 remove(),只是 detach()
,因此 Fragment 還在 FragmentManager 管理中,Fragment 所占用的資源不會被釋放。
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
if (this.mCurTransaction == null) {
this.mCurTransaction = this.mFragmentManager.beginTransaction();
}
this.mCurTransaction.detach((Fragment)object); //為毛要調用detach?
}
FragmentStatePagerAdapter
FragmentStatePagerAdapter 和前面的 FragmentPagerAdapter 一樣,是繼承自 PagerAdapter。但是,和 FragmentPagerAdapter 不一樣的是,正如其類名中的 'State' 所表明的含義一樣,該 PagerAdapter 的實現將只保留當前頁面,當頁面離開視線后,就會被消除,釋放其資源
;而在頁面需要顯示時,生成新的頁面(就像 ListView 的實現一樣)。這么實現的好處就是當擁有大量的頁面時,不必在內存中占用大量的內存。
getItem()
一個該類中新增的虛函數,函數的目的為生成新的 Fragment 對象。Fragment.setArguments() 這種只會在新建 Fragment 時執行一次的參數傳遞代碼,可以放在這里。
由於 FragmentStatePagerAdapter.instantiateItem() 在大多數情況下,都將調用 getItem() 來生成新的對象,因此如果在該函數中放置與數據集相關的 setter 代碼,基本上都可以在 instantiateItem() 被調用時執行,但這和設計意圖不符。畢竟還有部分可能是不會調用 getItem() 的。因此這部分代碼應該放到 instantiateItem() 中。
instantiateItem()
除非碰到 FragmentManager 剛好從 SavedState 中恢復了對應的 Fragment 的情況外,該函數將會調用 getItem() 函數,生成新的 Fragment 對象。新的對象將被 FragmentTransaction.add()。
FragmentStatePagerAdapter 就是通過這種方式,每次都創建一個新的 Fragment,而在不用后就立刻釋放其資源,來達到節省內存占用的目的的。
destroyItem()
將 Fragment 移除,即調用 FragmentTransaction.remove()
,並釋放其資源。
public void destroyItem(ViewGroup container, int position, Object object) {
Fragment fragment = (Fragment) object;
if (mCurTransaction == null) mCurTransaction = mFragmentManager.beginTransaction();
while (mSavedState.size() <= position) {
mSavedState.add(null);
}
mSavedState.set(position, fragment.isAdded() ? mFragmentManager.saveFragmentInstanceState(fragment) : null);
mFragments.set(position, null);
mCurTransaction.remove(fragment); //移除
}
ViewPager 的預加載
ViewPager里面定義了一個mOffscreenPageLimit
:
private int mOffscreenPageLimit = DEFAULT_OFFSCREEN_PAGES;
默認值是1,這表示你的預加載的頁面數量是1。在使用ViewPager與Fragment的時候,這個值的效果是,ViewPager會自動緩存1頁內的數據,當我們當前處在頁面2的時候,頁面1和頁面3的View實際上已經創建好了,所以在我們拖動的時候是可以看見他們的界面的。但是當我們的頁面處在1的時候,頁面3實際上就已經銷毀了(這句話一定要正確理解),直到跳轉到頁面2的時候,頁面3才會創建(這句話也一定要正確理解)。
如果想預加載更多頁面,可以通過setOffscreenPageLimit
設置你要預加載頁面的數量。特別對於應用的首頁來說,我們為了更好的用戶體驗,可以設置 mOffscreenPageLimit 的值為 tab數量-1
以實現預加載全部tab的效果()。
public void setOffscreenPageLimit(int limit) {
if (limit < 1) {
Log.w("ViewPager", "Requested offscreen page limit " + limit + " too small; defaulting to " + 1);
limit = 1;
}
if (limit != this.mOffscreenPageLimit) {
this.mOffscreenPageLimit = limit;
this.populate();
}
}
getCount 的返回值
返回值意義
- 若getCount()返回為0,則viewpage上不會顯示任何內容(不會報錯),也不能滑動(因為沒有Item)
- 若getCount()返回為1,則viewpage上只顯示第一個Item,也不能滑動(因為只有一個Item)
- 若getCount()返回為2,則viewpage上只顯示前兩個Item,且只能在兩者間滑動
怎么無限循環
正常情況下,滑動到最后一個Item時是不能再向前划到第一個Item的,當然在第一個Item時也不能向后划到最后一個Item,而要想首尾可以無限制的前后滑動,需滿足三個條件
- getCount()的返回值要足夠大(否則滑動到一定次數后向前就划不動了)
- 要保證首先加載的那個Item的position足夠大,可使用
viewpage.setCurrentItem(3000000)
設置 - 當然也要保證首先加載的是第一個Item,使用
position=position % mList.size()
替換所有用到的position參數的值
如何一次滑動多頁
class ViewPagerAdapter extends PagerAdapter {
@Override
public int getCount() {
int size = anchorList.size();
return size % 3 == 0 ? size / 3 : (size / 3 + 1);
}
@Override
public int getItemPosition(Object object) {
return POSITION_NONE; // -2
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
LinearLayout linearLayout;
firstBean = anchorList.get(3 * position + 0); //第一個子View的數據
secondBean = anchorList.get(3 * position + 1); //第二個子View的數據
thirdBean = anchorList.get(3 * position + 2); //第三個子View的數據
container.addView(linearLayout);
return linearLayout;
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
}
如何自定義指示器
根據需求,要么重寫onPageScrolled方法,要么重寫onPageSelected方法。
重寫onPageScrolled可以實現跟隨手指滑動
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
//參數為:當前條目位置,當前條目移動的百分比(0-100%),移動距離(0-item的width)
int leftMargin; //小紅點動態移動的距離
//最后一個條目時,不再繼續向后移動
if (position == mList.size() - 1) {
leftMargin = position * mPointWidth + MARGIN_BETWEEN_POINTS;
} else {
leftMargin = (int) (mPointWidth * positionOffset) + position * mPointWidth + MARGIN_BETWEEN_POINTS;
}
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) view_red_point.getLayoutParams();
params.leftMargin = leftMargin;
view_red_point.setLayoutParams(params);
}
重寫onPageSelected可以實現切換后平滑的滑動到目標位置
@Override
public void onPageSelected(int position) {
tv_right.setTextColor(Color.GRAY);//根據位置設置字體顏色
tv_left.setTextColor(Color.RED);
Animation animation = new TranslateAnimation(currIndex * one, position * one, 0, 0);
currIndex = position; //將當前位置設置為目標位置
animation.setFillAfter(true);//停留在動畫結束時的狀態
animation.setDuration(100);//動畫切換時間
iv_red.startAnimation(animation); //根據當前位置和將要移動到的位置創建動畫
}
代碼
Activity
public class MainActivity extends FragmentActivity {
private ViewPager mViewPager;
private TextView tvTitle, tvWeixin, tvFriend, tvContact, tvSetting;
private String[] titles = {"微信", "通訊錄", "發現", "我"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
mViewPager.setAdapter(getPagerAdapter(2));
mViewPager.addOnPageChangeListener(new MyOnPageChangeListener());
//mViewPager.setOffscreenPageLimit(titles.length - 1); //這個方法的殺傷力巨大,豈會導致所有緩存的Fragment都不會被銷毀掉
reset(tvWeixin, 0); // 使用 mViewPager.setCurrentItem(0); 並不會回調 onPageSelected
}
private PagerAdapter getPagerAdapter(int type) {
switch (type) {
case 0:
return new MyPagerAdapter();
case 1:
return new MyFragmentPagerAdapter(getSupportFragmentManager());
case 2:
return new MyFragmentStatePagerAdapter(getSupportFragmentManager());
default:
return new MyPagerAdapter();
}
}
private void initView() {
mViewPager = findViewById(R.id.id_viewpager);
tvTitle = findViewById(R.id.tv_title);
tvWeixin = findViewById(R.id.tv_tab_bottom_weixin);
tvFriend = findViewById(R.id.tv_tab_bottom_friend);
tvContact = findViewById(R.id.tv_tab_bottom_contact);
tvSetting = findViewById(R.id.tv_tab_bottom_setting);
tvTitle.setOnClickListener(v -> {
for (Fragment fragment : getSupportFragmentManager().getFragments()) {
Log.i("bqt", "【Fragment】" + ((SimpleTabFragment) fragment).getName());
}
});
tvWeixin.setOnClickListener(v -> reset(v, 0));
tvFriend.setOnClickListener(v -> reset(v, 1));
tvContact.setOnClickListener(v -> reset(v, 2));
tvSetting.setOnClickListener(v -> reset(v, 3));
}
private void reset(View v, int position) {
tvWeixin.setSelected(false);
tvFriend.setSelected(false);
tvContact.setSelected(false);
tvSetting.setSelected(false);
v.setSelected(true);
mViewPager.setCurrentItem(position);
tvTitle.setText(titles[position]);
}
class MyPagerAdapter extends PagerAdapter {
@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
Log.i("bqt", "【isViewFromObject】");
return view == object;
}
@Override
public int getCount() {
return titles.length;
}
@NonNull
public Object instantiateItem(@NonNull ViewGroup container, int position) {
Log.i("bqt", "【PagerAdapter-instantiateItem】" + position); //這個並非抽象方法
TextView tv = new TextView(container.getContext());
tv.setText(titles[position] + ":" + new SimpleDateFormat("HH:mm:ss SSS", Locale.getDefault()).format(new Date()));
tv.setBackgroundColor(0xFF000000 + new Random().nextInt(0xFFFFFF));
container.addView(tv);
return tv;
}
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
Log.i("bqt", "【destroyItem】" + position); //這個並非抽象方法
container.removeView((View) object);
}
}
class MyFragmentPagerAdapter extends FragmentPagerAdapter {
MyFragmentPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
Log.i("bqt", "【----------------FragmentPagerAdapter-getItem:" + position + "----------------】");
return SimpleTabFragment.newInstance(titles[position]);
}
@Override
public int getCount() {
return titles.length;
}
}
class MyFragmentStatePagerAdapter extends FragmentStatePagerAdapter {
MyFragmentStatePagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public int getCount() {
return titles.length;
}
@Override
public Fragment getItem(int position) {
Log.i("bqt", "【----------------FragmentStatePagerAdapter-getItem:" + position + "----------------】");
return SimpleTabFragment.newInstance(titles[position]);
}
}
class MyOnPageChangeListener implements OnPageChangeListener {
@Override
public void onPageScrolled(int i, float v, int i1) {
}
@Override
public void onPageSelected(int position) {
Log.i("bqt", "【onPageSelected】" + position);
switch (position) {
case 0:
reset(tvWeixin, position);
break;
case 1:
reset(tvFriend, position);
break;
case 2:
reset(tvContact, position);
break;
case 3:
reset(tvSetting, position);
break;
default:
break;
}
}
@Override
public void onPageScrollStateChanged(int i) {
}
}
}
Fragment
public class SimpleTabFragment extends Fragment {
private String name;
public static final String ARGUMENT = "name";
public static SimpleTabFragment newInstance(String name) {
SimpleTabFragment fragment = new SimpleTabFragment();
Bundle bundle = new Bundle();
bundle.putString(ARGUMENT, name);
fragment.setArguments(bundle);
return fragment;
}
public SimpleTabFragment() {
Log.i("bqt", "【----------------構造方法" +hashCode() +"----------------】");
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
Log.i("bqt", "【--------onAttach--------】");
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle bundle = getArguments();
name = bundle != null ? bundle.getString(ARGUMENT) : "unknown";
Log.i("bqt", "【--------onCreate:" + name + "--------】");
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.i("bqt", "【----onCreateView" + name + "----】");
View view = inflater.inflate(R.layout.fragment_tab, container, false);
TextView tv = view.findViewById(R.id.tv);
tv.setText(name + ":" + new SimpleDateFormat("HH:mm:ss SSS", Locale.getDefault()).format(new Date()));
tv.setBackgroundColor(0xFF000000 + new Random().nextInt(0xFFFFFF));
return view;
}
@Override
public void onResume() {
super.onResume();
Log.i("bqt", "【onResume:" + name + "】");
}
@Override
public void onDestroyView() {
super.onDestroyView();
Log.i("bqt", "【----onDestroyView:" + name + "------】");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i("bqt", "【--------onDestroy:" + name + "--------】");
}
@Override
public void onDetach() {
super.onDetach();
Log.i("bqt", "【--------onDetach:" + name + "--------】");
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
Activity 的布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/tv_title"
android:layout_width="fill_parent"
android:layout_height="45dp"
android:gravity="center"
android:text="微信"
android:textColor="#000"
android:textSize="20sp"
android:textStyle="bold"/>
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:background="#dbdbdb"/>
<android.support.v4.view.ViewPager
android:id="@+id/id_viewpager"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="2016"/>
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:background="#dbdbdb"/>
<LinearLayout
android:id="@+id/ly_main_tab_bottom"
android:layout_width="fill_parent"
android:layout_height="55dp">
<TextView
android:id="@+id/tv_tab_bottom_weixin"
android:layout_width="0dp"
android:layout_height="fill_parent"
android:layout_weight="1"
android:gravity="center"
android:text="微信"
android:textColor="@drawable/text_color_sel"/>
<TextView
android:id="@+id/tv_tab_bottom_friend"
android:layout_width="0dp"
android:layout_height="fill_parent"
android:layout_weight="1"
android:gravity="center"
android:text="通訊錄"
android:textColor="@drawable/text_color_sel"/>
<TextView
android:id="@+id/tv_tab_bottom_contact"
android:layout_width="0dp"
android:layout_height="fill_parent"
android:layout_weight="1"
android:gravity="center"
android:text="發現"
android:textColor="@drawable/text_color_sel"/>
<TextView
android:id="@+id/tv_tab_bottom_setting"
android:layout_width="0dp"
android:layout_height="fill_parent"
android:layout_weight="1"
android:gravity="center"
android:text="我"
android:textColor="@drawable/text_color_sel"/>
</LinearLayout>
</LinearLayout>
Fragment 的布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<TextView
android:id="@+id/tv"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center"
android:text="第一個頁面"
android:textColor="#000"
android:textSize="20sp"/>
</LinearLayout>
測試
16個短杠----------------
的有getItem
和構造方法
方法,代表的是Fragment對象的創建,是其他所有聲明周期的開始
8個短杠--------
的有onAttach
、onCreate
和onDestroy
、onDetach
方法,代表的是Fragment聲明周期的開始
4個短杠----
的有onCreateView
和onDestroyView
方法,代表的是Fragment中View的創建和銷毀
啟動后
FragmentPagerAdapter
【----------------FragmentPagerAdapter-getItem:0----------------】
【----------------構造方法215908580----------------】
【----------------FragmentPagerAdapter-getItem:1----------------】
【----------------構造方法173705549----------------】
【--------onAttach--------】
【--------onCreate:微信--------】
【--------onAttach--------】
【--------onCreate:通訊錄--------】
【----onCreateView微信----】
【onResume:微信】
【----onCreateView通訊錄----】
【onResume:通訊錄】
首先是構造了兩個Fragment,然后兩個Fragment被綁定到Activity上,然后通過onCreateView創建Fragment的View,最后都onResume
注意,雖然此時第二個Fragment對用戶是不可見的,但仍會回調其onResume方法
FragmentStatePagerAdapter
和FragmentPagerAdapter行為完全一致
滑到第二個頁面
【onPageSelected】1
【----------------FragmentPagerAdapter-getItem:2----------------】
【----------------構造方法88702934----------------】
【--------onAttach--------】
【--------onCreate:發現--------】
【----onCreateView發現----】
【onResume:發現】
此時會完整的創建第三個Fragment,同時第一個Fragment會保留
FragmentStatePagerAdapter
和FragmentPagerAdapter行為完全一致
滑到第一個頁面
【onPageSelected】0
【----onDestroyView:發現------】
此時第三個Fragment的View會被銷毀
注意,此時第三個Fragment並沒有回調onDestroy
、onDetach
方法,所以第三個Fragment並沒有被銷毀
FragmentStatePagerAdapter
【onPageSelected】0
【----onDestroyView:發現------】
【--------onDestroy:發現--------】
【--------onDetach:發現--------】
重大區別來了,除了會回調
onDestroyView
銷毀View外,FragmentStatePagerAdapter還會回調onDestroy
、onDetach
方法銷毀Fragment
最小化后再回到前台
【onResume:微信】
【onResume:通訊錄】
可以發現,前兩個Fragment都回調了onResume
注意:雖然第三個Fragment沒有被銷毀,但因為其View被銷毀了,所以不可能再回調其onCreateView
之后的聲明周期方法了,這其中當然包括onResume方法。
FragmentStatePagerAdapter
和FragmentPagerAdapter行為完全一致
再次滑到第二個頁面
【onPageSelected】1
【----onCreateView發現----】
【onResume:發現】
可以發現,並沒有重建第三個Fragment,而只是通過
onCreateView
重建了Fragment的View
FragmentStatePagerAdapter
【onPageSelected】1
【----------------FragmentStatePagerAdapter-getItem:2----------------】
【----------------構造方法62405875----------------】
【--------onAttach--------】
【--------onCreate:發現--------】
【----onCreateView發現----】
【onResume:發現】
和前面的重大區別對應,這里會完全重建第三個Fragment
在第二個頁面橫豎屏切換
來個大招,橫豎屏切換:
【----onDestroyView:微信------】
【--------onDestroy:微信--------】
【--------onDetach:微信--------】
【----onDestroyView:通訊錄------】
【--------onDestroy:通訊錄--------】
【--------onDetach:通訊錄--------】
【----onDestroyView:發現------】
【--------onDestroy:發現--------】
【--------onDetach:發現--------】
【----------------構造方法207117498----------------】
【----------------構造方法264001387----------------】
【----------------構造方法227934920----------------】
【--------onAttach--------】
【--------onCreate:微信--------】
【--------onAttach--------】
【--------onCreate:通訊錄--------】
【--------onAttach--------】
【--------onCreate:發現--------】
【----onCreateView微信----】
【----onCreateView通訊錄----】
【----onCreateView發現----】
【onPageSelected】1
【onResume:微信】
【onResume:通訊錄】
【onResume:發現】
可以發現,完全銷毀了之前的所有Fragment,並且重建了所有Fragment
FragmentStatePagerAdapter
和FragmentPagerAdapter行為完全一致
在第一個頁面橫豎屏切換
【----onDestroyView:微信------】
【--------onDestroy:微信--------】
【--------onDetach:微信--------】
【----onDestroyView:通訊錄------】
【--------onDestroy:通訊錄--------】
【--------onDetach:通訊錄--------】
//注意,這里並不會調用第三個Fragment的onDestroyView,因為這個Fragment的View早就被銷毀了
【--------onDestroy:發現--------】
【--------onDetach:發現--------】
【----------------構造方法262171070----------------】
【----------------構造方法242817567----------------】
【----------------構造方法41927020----------------】
【--------onAttach--------】
【--------onCreate:微信--------】
【--------onAttach--------】
【--------onCreate:通訊錄--------】
【--------onAttach--------】
【--------onCreate:發現--------】
【----onCreateView微信----】
【----onCreateView通訊錄----】
//同樣,這里並不會調用第三個Fragment的onCreateView,因為這個Fragment的View目前並不需要創建
【onResume:微信】
【onResume:通訊錄】
和上一種情況沒什么區別,唯一的區別就是,並沒有調用第三個Fragment的
onDestroyView
銷毀View,或調用其onCreateView
重建View
FragmentStatePagerAdapter
【----onDestroyView:微信------】
【--------onDestroy:微信--------】
【--------onDetach:微信--------】
【----onDestroyView:通訊錄------】
【--------onDestroy:通訊錄--------】
【--------onDetach:通訊錄--------】
//這里完全沒有調用第三個Fragment的任何回調方法,因為在切換到第一個Fragment時,第三個Fragment已經完全銷毀了
【----------------構造方法41927020----------------】
【----------------構造方法48001845----------------】
//這里也不會重新創建第三個Fragment
【--------onAttach--------】
【--------onCreate:微信--------】
【--------onAttach--------】
【--------onCreate:通訊錄--------】
【----onCreateView微信----】
【----onCreateView通訊錄----】
【onResume:微信】
【onResume:通訊錄】
和前面的重大區別對應,因為在切換到第一個Fragment時,第三個Fragment已經完全銷毀了,所以這里不會回調第三個Fragment的任何回調方法,同樣,這里也不會重新創建第三個Fragment。
2018-2-11