Tips:
如何設(shè)置 xml 的屬性預(yù)覽可見, 運行不可見
方法: xml 根布局添加xmlns:tool="http://schemas.android.com/tools", 子控件命名空間設(shè)置tool:text等, 只在Design視圖能看見, 運行后不可見 Link
Fragment
ViewPager打開第 1 個fragment時會打開第 2 個 fragment 并執(zhí)行第 2 個的生命周期從 onCreateView
到 onResume
(點擊第 2 個時會打開第 3 個的...), 為避免在 onCreateView
里進行太多操作, 我們考慮只在 fragment可見 && 第一次加載 時 進行數(shù)據(jù)加載
- Fragment 搭配 ViewPager 時會多在生命周期奪走一個方法
setUserVisibleHint()
, 我們可手動調(diào)用getUserVisibleHint
看回調(diào)setUserVisibleHint()
用在單個 Fragment 時不會被調(diào)用setUserVisibleHint()
調(diào)用在onAttach之前
如何懶加載(兩種)
- Fragment.setUserVisibleHint
- ViewPager.addOnPageChangeListener里的
onPageSelected(int position)
第一種.setUserVisibleHint
我們需要做的判斷
- fragment可見, 我們可以根據(jù)setUserVisibleHint時判斷isVisibleToUser
- 是否加載過View, 通過自己創(chuàng)建標識位
- 是否加載過數(shù)據(jù), 通過自己創(chuàng)建標識位
寫法
- 先寫 BaseFragment
public abstract class BaseFragment extends Fragment {
protected Activity mActivity;
protected View mRootView;
private Unbinder unbinder;
/**
* 說明:在此處保存全局的Context
*
* @param context 上下文
*/
@Override
public void onAttach(Context context) {
super.onAttach(context);
mActivity = (Activity) context;
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
mRootView = inflater.inflate(getLayoutId(), container, false);
unbinder = ButterKnife.bind(this, mRootView);
init();
return mRootView;
}
/**
* @return 返回該Fragment的layout id
*/
protected abstract int getLayoutId();
/**
* 說明:創(chuàng)建視圖時的初始化操作均寫在該方法
*/
protected abstract void init();
/**
* 獲取控件對象
*
* @param id 控件id
* @return 控件對象
*/
public View findViewById(int id) {
if (getContentView() != null) {
return getContentView().findViewById(id);
} else {
return null;
}
}
/**
* 說明:返回當前View
*
* @return view
*/
protected View getContentView() {
return mRootView;
}
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
}
- 再寫 lazyFragment
/**
* 懶加載Fragment
*
* 可以加載數(shù)據(jù)的條件:
* 1.視圖已經(jīng)初始化
* 2.視圖對用戶可見
*/
public abstract class LazyLoadFragment extends BaseFragment {
public boolean isInit = false;//視圖是否已經(jīng)初始化
public boolean isLoad = false;//視圖是否已經(jīng)加載過
/**
* 初始化
*/
@Override
protected void init() {
isInit = true;
isCanLoadData();
}
/**
* 判斷是否可以加載數(shù)據(jù), 如果可以便進行數(shù)據(jù)的加載
*/
private void isCanLoadData() {
if (!isInit) {//未初始化
return;
}
if (getUserVisibleHint()) {//用戶可見
lazyLoad();
isLoad = true;
} else {
if (isLoad) {//用戶不可見, 且已經(jīng)加載過
stopLoad();
}
}
}
/**
* 當視圖初始化并且對可見時加載數(shù)據(jù)
*/
public abstract void lazyLoad();
/**
* 當該視圖對用戶不可見并且已經(jīng)加載過數(shù)據(jù)的時候, 如果需要在切換到其他頁面時停止加載數(shù)據(jù), 通過覆寫此方法實現(xiàn)
*/
public void stopLoad() {
}
/**
* 說明:當前視圖可見性發(fā)生變化時調(diào)用該方法
*
* @param isVisibleToUser 當前視圖是否可見
*/
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
isCanLoadData();
}
/**
* 視圖銷毀時將Fragment是否初始化的狀態(tài)變?yōu)閒alse
*/
@Override
public void onDestroyView() {
super.onDestroyView();
isInit = false;
isLoad = false;
}
}
第二種, 通過onPageSelected判斷
缺點: 第 1 頁第 1 次進入不調(diào)用 onPageSelected
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
// TODO
fragment.initData();
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
作者:最最最最醉人
鏈接:http://www.reibang.com/p/843088e1ad40
來源:簡書
著作權(quán)歸作者所有毡泻。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán), 非商業(yè)轉(zhuǎn)載請注明出處漾橙。
PagerAdapter
分為三種
(1)PagerAdapter 數(shù)據(jù)源:List<View> 適合輪播圖,圖片查看等
(2)FragmentPagerAdapter 數(shù)據(jù)源:List<Fragment>
(3)FragmentStatePagerAdapter 數(shù)據(jù)源:List<Fragment>
FragmentPagerAdapter 和 FragmentStatePagerAdapter區(qū)別
- FragmentPagerAdapter: fragmentTransaction()調(diào)用depatch(), 生命周期走到onDestroyView(), 適合主界面, 頁面少, 只回收view, 數(shù)據(jù)保留
- FragmentStatePagerAdapter: fragmentTransaction()調(diào)用remove(), 生命周期走到onDetach(), 適合多頁面, 數(shù)據(jù)多, view和data都回收, 但也會調(diào)onSaveInstance方法, 可存在bundle.
使用FragmentStatePagerAdapter打開多個Fragment:
-
第一次進入頁面
0: onAttach 0: onCreate 1: onAttach 1: onCreate 0: onCreateView 0: onViewCreated 0: onActivityCreated 0: onStart 0: onResume 1: onCreateView 1: onViewCreated 1: onActivityCreated 1: onStart 1: onResume
-
從第0頁到第1頁
2: onAttach 2: onCreate 2: onCreateView 2: onViewCreated 2: onStart 2: onResume
-
從第1頁到第2頁, 會把第0頁殺到
onDetach
3: onAttach 3: onCreate 0: onPause 0: onStop 0: onDestroyView 0: onDestroy 0: onDetach 3: onCreateView 3: onViewCreated 3: onActivityCreated 3: onStart 3: onResume
-
從第1頁到第0頁
2: onPause 2: onStop 2: onDestroyView 2: onDestroy 2: onDetach
-
FragmentPagerAdapter則是讓不可見的頁面其生命周期走到onDestroyView
3: onAttach 3: onCreate 0: onPause 0: onStop 0: onDestroyView 3: onCreateView 3: onViewCreated 3: onActivityCreated 3: onStart 3: onResume
adapter到底是外部還是內(nèi)部的
- adapter別持有Activity的context, 否則可能內(nèi)存泄漏OOM
- 盡量 內(nèi)部, 按照谷歌的示例
tab關(guān)聯(lián)viewpager
tabLayout.setupWithViewPager(viewpager); 傳入viewpager
-
setupWithViewPager
方法會在populateFromPagerAdapter
清空tab, 再調(diào)用getPageTitle
, 所以正確設(shè)置tab的方式是getPageTitle里拼CharSequence Link//...省略一部分代碼 //處理從方法中傳入的viewpager if (viewPager != null) { mViewPager = viewPager; //①以下這部分代碼是處理一些滑動的關(guān)聯(lián)關(guān)系忆家。例如滑動監(jiān)聽 // Add our custom OnPageChangeListener to the ViewPager if (mPageChangeListener == null) { mPageChangeListener = new TabLayoutOnPageChangeListener(this); } mPageChangeListener.reset(); viewPager.addOnPageChangeListener(mPageChangeListener); // Now we'll add a tab selected listener to set ViewPager's current item mCurrentVpSelectedListener = new ViewPagerOnTabSelectedListener(viewPager); addOnTabSelectedListener(mCurrentVpSelectedListener); //②以下這部分代碼就可以解釋為什么我們的tab設(shè)置的內(nèi)容會不見了 final PagerAdapter adapter = viewPager.getAdapter(); if (adapter != null) { // Now we'll populate ourselves from the pager adapter, adding an observer if // autoRefresh is enabled //將外部設(shè)置給viewpager的adapter取出來通過setPagerAdapter重新設(shè)置 setPagerAdapter(adapter, autoRefresh); } ... } ... } void setPagerAdapter(@Nullable final PagerAdapter adapter, final boolean addObserver) { if (mPagerAdapter != null && mPagerAdapterObserver != null) { // If we already have a PagerAdapter, unregister our observer mPagerAdapter.unregisterDataSetObserver(mPagerAdapterObserver); } //注冊observer mPagerAdapter = adapter; if (addObserver && adapter != null) { // Register our observer on the new adapter if (mPagerAdapterObserver == null) { mPagerAdapterObserver = new PagerAdapterObserver(); } adapter.registerDataSetObserver(mPagerAdapterObserver); } // Finally make sure we reflect the new adapter //關(guān)鍵代碼 populateFromPagerAdapter(); } void populateFromPagerAdapter() { //移除所有的tab //我們之前設(shè)置的tab就是在這里被移除的 removeAllTabs(); if (mPagerAdapter != null) { final int adapterCount = mPagerAdapter.getCount(); for (int i = 0; i < adapterCount; i++) { //重新添加tab, 而setText得內(nèi)容是在PagerAdapter中g(shù)etPageTitle獲取的, 因此問題的答案就已經(jīng)很明顯了勋磕。 addTab(newTab().setText(mPagerAdapter.getPageTitle(i)), false); } // Make sure we reflect the currently set ViewPager item if (mViewPager != null && adapterCount > 0) { final int curItem = mViewPager.getCurrentItem(); if (curItem != getSelectedTabPosition() && curItem < getTabCount()) { selectTab(getTabAt(curItem)); } } } } 作者:liaoweijian 鏈接:http://www.reibang.com/p/d83f10c1a765 來源:簡書 著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán), 非商業(yè)轉(zhuǎn)載請注明出處。
tab添加標題
- adapter的
getPageTitle(int position)
返回每個pager對應(yīng)的標題名
tab添加圖片
- adapter的
getPageTitle(int position)
的 CharSequence 看不見圖片;
解決: 在 style 里把 textAllCaps 置為 false
style可以設(shè)置在styles里, 也可以單獨在xml里添加style<style name="HomeTabLayout" parent = "Widget.Design.TabLayout"> <item name="tabTextAppearance">@style/HomeTabTextAppearance</item> </style> <style name="HomeTabTextAppearance" parent="TextAppearance.Design.Tab"> <!--顯示tab圖標--> <item name="textAllCaps">false</item> </style>
自定義tabItem
在activity中添加, 防止將context傳給外部Adapter, setupWithViewPager先執(zhí)行再for循環(huán)添加view, 簡單的按鈕切換用selector
@Override
protected void onCreate(Bundle savedInstanceState) {
tabLayout.setupWithViewPager(viewpager);
for (int i = 0; i < tabLayout.getTabCount(); i++){
tabLayout.getTabAt(i).setCustomView(getTabView(i));
}
}
public View getTabView(int position){
View view = LayoutInflater.from(this).inflate(R.layout.tab_home_bottom, null);
ImageView ivHomeTab = (ImageView) view.findViewById(R.id.iv_home_tab);
ivHomeTab.setImageResource(tabIcons[position]);
return view;
}
setTabMode
- TabLayout.MODE_SCROLLABLE 跟著viewpager滾動
- TabLayout.MODE_FIXED 固定的Tab, tabGravity.GRAVITY_FILL需要和 TabLayout.MODE_FIXED 一起使用才有效
下劃線Indicator
- style里通過tabIndicatorColor設(shè)置顏色, tabIndicatorHeight設(shè)置寬高
處理橫豎切換(待驗證)
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(POSITION,tabLayout.getSelectedTabPosition());
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
viewPager.setCurrentItem(savedInstanceState.getInt(POSITION));
}
ViewPager
保存多少個Pager 用 ViewPager.setOffscreenPageLimit(num)
- 保留 2 num + 1 個頁面不被銷毀, 即設(shè)置1時保留左右兩邊的
- 默認是1, 除非修改源碼
Viewpager + PagerAdapter + TabLayout
ViewPager viewpager = (ViewPager) findViewById(R.id.viewpager);
viewpager.setAdapter(new FragmentStatePagerAdapter(getSupportFragmentManager()) {
@Override
public Fragment getItem(int position) {
Fragment fragment = null;
switch (position) {
case 0:
fragment = new FragmentZero();
break;
case 1:
fragment = new FragmentOne();
break;
case 2:
fragment = new FragmentTwo();
break;
case 3:
fragment = new FragmentThree();
break;
case 4:
fragment = new FragmentFour();
break;
case 5:
fragment = new FragmentFive();
break;
}
return fragment;
}
@Override
public int getCount() {
return 6;
}
@Override
public CharSequence getPageTitle(int position) {
switch (position){
case 0:
return "0";
case 1:
return "1";
case 2:
return "2";
case 3:
return "3";
case 4:
return "4";
default:
return "0";
}
}
});
TabLayout tabLayout = (TabLayout) findViewById(R.id.tablayout);
tabLayout.setupWithViewPager(viewpager);
參考
- 通用寫法
- 通用寫法原地址
- 完善寫法
- fragment生命周期
- 哪些操作需要懶加載
- Fragment懶加載
- 10條 | Fragment 懶加載實戰(zhàn)
- 簡書 | 如何高效的使用ViewPager, 以及FragmentPagerAdapter與FragmentStatePagerAdapter的區(qū)別
- 簡書 | ViewPager使用詳解(三):FragmentStatePagerAdapter
- 簡書 | TabLayout使用詳解
- android design library提供的TabLayout的用法
- Android Fragment+ViewPager 組合, 一些你不可不知的注意事項
- 簡書 | Android TabLayout 分分鐘打造一個滑動標簽頁
- CSDN | 告訴你ListView的Adapter應(yīng)該寫在Activity外面還是里面