版權聲明:本文爲HaiyuKing原創文章,轉載請註明出處!html
使用TabLayout搭配ViewPager實現可滑動的頂部選項卡效果。java
一、演示常規的設置。android
二、經過自定義ViewPager(MyCustomViewPager)解決解決切換須要通過中間頁的問題、實現控制viewpager是否可滑動的功能;git
3.一、經過在Fragment中的OnCreateView中判斷rootView是否爲空來解決viewpager+fragment來回滑動fragment從新加載的問題;
github
3.二、還有一個方案是在自定義的viewpager適配器類中重寫destroyItem方法,來解決從新加載的問題;【核心就是不銷燬fragment】web
四、經過自定義Fragment基類(BaseLazyFragment)來實現配合viewpager使用時禁止懶加載的功能;api
存在一個問題:那就是當選項卡比較多的時候,從首頁切換到尾頁,而後切換回來首頁的時候,會從新請求數據,由於首頁已經銷燬了,執行了onDestroyView方法。緩存
解決方案:網絡
1、使用setOffscreenPageLimit()方法, 設置數字越大越好(能夠設置總數目);app
2、採用3.2方案;
注意事項:
一、 導入類文件後須要change包名以及從新import R文件路徑
二、 Values目錄下的文件(strings.xml、dimens.xml、colors.xml等),若是項目中存在,則複製裏面的內容,不要整個覆蓋
在APP的build.gradle文件中添加如下代碼【注意:版本號和com.android.support:appcompat-v7保持一致】
注意:TabLayout爲5.0以後的新控件,因此styles.xml中的主題應該使用Theme.AppCompat.Light.NoActionBar或者Theme.AppCompat.Light等Theme.AppCompat.XXX的主題
apply plugin: 'com.android.application'
android {
compileSdkVersion 25
buildToolsVersion "25.0.3"
defaultConfig {
applicationId "com.why.project.tablayoutviewpagerdemo"
minSdkVersion 15
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.3.1'
testCompile 'junit:junit:4.12'
//TabLayout compile 'com.android.support:design:25.3.1'
}
<?xml version="1.0" encoding="utf-8"?> <resources> <color name="colorPrimary">#3F51B5</color> <color name="colorPrimaryDark">#303F9F</color> <color name="colorAccent">#FF4081</color> <!-- *********************************頂部選項卡區域********************************* --> <!-- 頂部選項卡下劃線背景色 --> <color name="tab_auto_normal_top">#00ffffff</color> <color name="tab_auto_selected_top">#3090d9</color> <!-- 頂部選項卡文本顏色 --> <color name="tab_text_normal_top">#191919</color> <color name="tab_text_selected_top">#3090d9</color> </resources>
<resources> <!-- Default screen margins, per the Android Design guidelines. --> <dimen name="activity_horizontal_margin">16dp</dimen> <dimen name="activity_vertical_margin">16dp</dimen> <!-- *********************************頂部選項卡區域********************************* --> <!-- 選項卡的內邊距 --> <dimen name="tab_top_auto_padding">10dp</dimen> <!-- 選項卡標題的文字大小 --> <dimen name="tab_top_auto_title_size">18sp</dimen> <!-- 選項卡標題的下劃線高度 --> <dimen name="tab_top_auto_height">3dp</dimen> </resources>
<resources> <!-- Base application theme. --> <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <!-- Customize your theme here. --> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> </style> <!-- TabLayout的文字大小 --> <style name="TabLayoutTextStyle"> <item name="android:textSize">@dimen/tab_top_auto_title_size</item> </style> </resources>
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.why.project.tablayoutviewpagerdemo"> <!-- ======================受權訪問網絡(HttpUtil)========================== --> <!-- 容許程序打開網絡套接字 --> <uses-permission android:name="android.permission.INTERNET"/> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> </application> </manifest>
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.why.project.tablayoutviewpagerdemo.MainActivity"> <!-- 選項卡區域 --> <!--設置TabLayout的模式 app:tabMode 默認是fixed:固定的,標籤不少時候會被擠壓,不能滑動。--> <!--設置整個TabLayout的顏色 app:tabBackground--> <!--設置選中字體的顏色 app:tabSelectedTextColor--> <!--設置未選中字體的顏色 app:tabTextColor--> <!--設置指示器下標的顏色 app:tabIndicatorColor--> <!--設置指示器下標的高度 app:tabIndicatorHeight,若是設置的是0.0dp,則表明沒有下劃線--> <!--設置內容的顯示模式 app:tabGravity,center : 居中,若是是fill,則是充滿--> <android.support.design.widget.TabLayout android:id="@+id/tl_top" android:layout_width="match_parent" android:layout_height="wrap_content" app:tabBackground="@android:color/transparent" app:tabMode="scrollable" app:tabSelectedTextColor="@color/tab_text_selected_top" app:tabTextColor="@color/tab_text_normal_top" app:tabTextAppearance="@style/TabLayoutTextStyle" app:tabIndicatorColor="@color/tab_auto_selected_top" app:tabIndicatorHeight="@dimen/tab_top_auto_height" app:tabGravity="center" /> <!-- viewpager區域 --> <com.why.project.tablayoutviewpagerdemo.viewpager.MyCustomViewPager android:id="@+id/vp_tab" android:layout_width="match_parent" android:layout_height="0.0dp" android:layout_weight="1"/> </LinearLayout>
fragment_web.xml文件佈局以下
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <!-- webview --> <WebView android:id="@+id/web_view" android:layout_width="match_parent" android:layout_height="match_parent"></WebView> </LinearLayout>
WebViewFragment
package com.why.project.tablayoutviewpagerdemo.fragment; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.webkit.WebView; import android.webkit.WebViewClient; import com.why.project.tablayoutviewpagerdemo.R; /** * @Created HaiyuKing * @Used 首頁界面——碎片界面 */ public class WebViewFragment extends BaseLazyFragment{ private static final String TAG = "WebViewFragment"; /**View實例*/ private View myView; private WebView web_view; /**傳遞過來的參數*/ private String bundle_param; //重寫 public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) { //使用FragmentTabHost時,Fragment之間切換時每次都會調用onCreateView方法,致使每次Fragment的佈局都重繪,沒法保持Fragment原有狀態。 //http://www.cnblogs.com/changkai244/p/4110173.html if(myView == null){ myView = inflater.inflate(R.layout.fragment_web, container, false); //接收傳參 Bundle bundle = this.getArguments(); bundle_param = bundle.getString("param"); } //緩存的rootView須要判斷是否已經被加過parent, 若是有parent須要從parent刪除,要否則會發生這個rootview已經有parent的錯誤。 ViewGroup parent = (ViewGroup) myView.getParent(); if (parent != null) { parent.removeView(myView); } return myView; } @Override public void onActivityCreated(Bundle savedInstanceState) { //初始化控件以及設置 initView(); //初始化控件的點擊事件 initEvent(); // TODO Auto-generated method stub super.onActivityCreated(savedInstanceState); } //實現禁止預加載的功能 @Override public void fetchData() { //初始化數據 initData(); } //實現禁止預加載的功能 @Override public void onInvisible() { } @Override public void onResume() { super.onResume(); } @Override public void onPause() { super.onPause(); } @Override public void onDestroy() { super.onDestroy(); } /** * 初始化控件 */ private void initView() { web_view = (WebView) myView.findViewById(R.id.web_view); //設置支持js腳本 // web_view.getSettings().setJavaScriptEnabled(true); web_view.setWebViewClient(new WebViewClient() { /** * 重寫此方法代表點擊網頁內的連接由本身處理,而不是新開Android的系統browser中響應該連接。 */ @Override public boolean shouldOverrideUrlLoading(WebView webView, String url) { //webView.loadUrl(url); return false; } }); } /** * 初始化數據 */ public void initData() { Log.e("tag","{initData}bundle_param="+bundle_param); web_view.loadUrl(bundle_param);//加載網頁 } /** * 初始化點擊事件 * */ private void initEvent(){ } }
package com.why.project.tablayoutviewpagerdemo.fragment; import android.os.Bundle; import android.util.Log; /** * Used 主要實現配合viewpager使用時禁止懶加載 * 咱們通常在onCreateView方法初始化視圖,onActivityCreated方法初始化數據, * 經過setUserVisibleHint和getUserVisibleHint方法來設置和獲取Fragment的顯示狀態,當顯示了纔去加載數據。 * * setUserVisibleHint: isVisibleToUser = false onAttach onCreate setUserVisibleHint: isVisibleToUser = true onCreateView onActivityCreated onStart onResume onPause onStop onDestroyView onDestroy onDetach 參考資料:https://blog.csdn.net/aiynmimi/article/details/73277836 */ public abstract class BaseLazyFragment extends BaseFragment{ private static final String TAG = BaseLazyFragment.class.getSimpleName(); /** * rootView是否初始化標誌,防止回調函數在rootView爲空的時候觸發 */ protected boolean isViewInitiated;//view是否初始化 /** * 當前Fragment是否處於可見狀態標誌,防止因ViewPager的緩存機制而致使回調函數的觸發 */ protected boolean isVisibleToUser;//是否可見 protected boolean isDataInitiated;//數據是否加載完成 @Override public void setUserVisibleHint(boolean isVisibleToUser) { Log.e(TAG,"{setUserVisibleHint}isVisibleToUser="+isVisibleToUser); super.setUserVisibleHint(isVisibleToUser); this.isVisibleToUser = isVisibleToUser; if(getUserVisibleHint()) { prepareFetchData(); } else { if(isViewInitiated){ onInvisible(); } } } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); isViewInitiated = true; prepareFetchData(); } /** * 視圖銷燬的時候 Fragment是否初始化的狀態變爲false */ @Override public void onDestroyView() { Log.e(TAG,"{onDestroyView}"); super.onDestroyView(); isViewInitiated = false; isVisibleToUser = false; isDataInitiated = false; } //初始化數據 public abstract void fetchData(); //當隱藏的時候執行的方法 public abstract void onInvisible(); public boolean prepareFetchData() { return prepareFetchData(false); } /** * 請求數據 * @param forceUpdate : 是否強制請求數據*/ public boolean prepareFetchData(boolean forceUpdate) { if (isVisibleToUser && isViewInitiated && (!isDataInitiated || forceUpdate)) { fetchData(); isDataInitiated = true; return true; } return false; } }
package com.why.project.tablayoutviewpagerdemo.model; /** * Created by HaiyuKing * Used */ public class TabItemModel { private String tabTitle; private String tabUrl; public TabItemModel(String tabTitle, String tabUrl){ this.tabTitle = tabTitle; this.tabUrl = tabUrl; } public String getTabTitle() { return tabTitle; } public void setTabTitle(String tabTitle) { this.tabTitle = tabTitle; } public String getTabUrl() { return tabUrl; } public void setTabUrl(String tabUrl) { this.tabUrl = tabUrl; } }
package com.why.project.tablayoutviewpagerdemo.adapter; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentStatePagerAdapter; import android.view.ViewGroup; import com.why.project.tablayoutviewpagerdemo.model.TabItemModel; import java.util.List; /** * Created by HaiyuKing * Used 當viewpager中fragment數量多的時候用FragmentStatePagerAdapter,反之則用FragmentPagerAdapter。 */ public class ContentPagerAdapter extends FragmentStatePagerAdapter { private List<TabItemModel> tabIndicators; /**碎片集合*/ private List<Fragment> fragmentList; public ContentPagerAdapter(FragmentManager fm) { super(fm); // TODO Auto-generated constructor stub } /** * 自定義構造函數:用於傳遞碎片集合過來 * 通常都寫上*/ public ContentPagerAdapter(FragmentManager fm, List<TabItemModel> tabIndicators, List<Fragment> fragmentList) { super(fm); this.tabIndicators = tabIndicators; this.fragmentList = fragmentList; } @Override public Fragment getItem(int position) { return fragmentList.get(position); } @Override public int getCount() { return fragmentList.size(); } @Override public CharSequence getPageTitle(int position) { return tabIndicators.get(position).getTabTitle(); } @Override public void destroyItem(ViewGroup container, int position, Object object) { //viewpager+fragment來回滑動fragment從新加載的簡單解決辦法:註釋下面的代碼 //不建議使用,由於當選項卡過多的時候,若是不銷燬的是,擔憂內存溢出 //http://blog.csdn.net/qq_28058443/article/details/51519663 super.destroyItem(container, position, object); } }
package com.why.project.tablayoutviewpagerdemo.viewpager; import android.content.Context; import android.support.v4.view.ViewPager; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; /** * Created by HaiyuKing * Used 自定義的viewpager * https://www.cnblogs.com/tangs/articles/5933233.html * 解決切換須要通過中間頁的問題; * 實現控制viewpager是否可滑動; * 解決視頻播放器和viewpager滑動衝突問題【可擴展到任何view】; */ public class MyCustomViewPager extends ViewPager { /**是否能夠滑動:默承認以滑動*/ private boolean isCanScroll = true; public MyCustomViewPager(Context context) { super(context); } public MyCustomViewPager(Context context, AttributeSet attrs) { super(context, attrs); } /** * 解決切換須要通過中間頁 */ @Override public void setCurrentItem(int item) { //super.setCurrentItem(item);源碼 super.setCurrentItem(item,false);//smoothScroll false表示切換的時候,不通過兩個頁面的中間頁 } /** * 讓ViewPager不能左右滑動 */ @Override public boolean onTouchEvent(MotionEvent ev) { if(isCanScroll){ return super.onTouchEvent(ev); }else{ return false; } } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if(isCanScroll){ return super.onInterceptTouchEvent(ev); }else{ return false; } } /** * 暴露出去的方法,屏蔽ViewPager的滑動,默認不可滑動 * @param isCanScroll 爲true能夠左右滑動,爲false不可滑動 */ public void setIsCanScroll(boolean isCanScroll){ this.isCanScroll = isCanScroll; } @Override protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) { /*if (v instanceof IjkVideoView) {//解決視頻播放器和viewpager滑動衝突問題 return true; }*/ return super.canScroll(v, checkV, dx, x, y); } }
package com.why.project.tablayoutviewpagerdemo; import android.os.Bundle; import android.support.design.widget.TabLayout; import android.support.v4.app.Fragment; import android.support.v7.app.AppCompatActivity; import com.why.project.tablayoutviewpagerdemo.adapter.ContentPagerAdapter; import com.why.project.tablayoutviewpagerdemo.fragment.WebViewFragment; import com.why.project.tablayoutviewpagerdemo.model.TabItemModel; import com.why.project.tablayoutviewpagerdemo.viewpager.MyCustomViewPager; import java.util.ArrayList; import java.util.List; public class MainActivity extends AppCompatActivity { private TabLayout mTabLayout; private MyCustomViewPager mTabViewPager; private List<TabItemModel> tabIndicators; private List<Fragment> tabFragments; private ContentPagerAdapter contentAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initViews(); initDatas(); initEvents(); } private void initViews() { mTabLayout = (TabLayout) findViewById(R.id.tl_top); mTabViewPager = (MyCustomViewPager) findViewById(R.id.vp_tab); mTabViewPager.setOffscreenPageLimit(3);//禁止預加載【若是想要延遲首個選項卡的銷燬時間,那麼就須要設置這個數值高點】 } private void initDatas() { //初始化選項卡子項的文本、超連接model集合 tabIndicators = new ArrayList<TabItemModel>(); tabIndicators.add(new TabItemModel("百度","http://www.baidu.com")); tabIndicators.add(new TabItemModel("CSDN","http://blog.csdn.net")); tabIndicators.add(new TabItemModel("博客園","http://www.cnblogs.com")); tabIndicators.add(new TabItemModel("極客頭條","http://geek.csdn.net/mobile")); tabIndicators.add(new TabItemModel("優設","http://www.uisdc.com/")); tabIndicators.add(new TabItemModel("玩Android","http://www.wanandroid.com/index")); tabIndicators.add(new TabItemModel("掘金","https://juejin.im/")); //初始化碎片集合 tabFragments = new ArrayList<>(); for(int i=0;i<tabIndicators.size();i++){ TabItemModel tabItemModel = tabIndicators.get(i); Bundle bundle = new Bundle(); bundle.putString("param", tabItemModel.getTabUrl()); tabFragments.add(WebViewFragment.getInstance(WebViewFragment.class,bundle)); } //實例化Adapter contentAdapter = new ContentPagerAdapter(getSupportFragmentManager(),tabIndicators,tabFragments); mTabViewPager.setAdapter(contentAdapter); //TabLayout和ViewPager相關聯 mTabLayout.setupWithViewPager(mTabViewPager); } private void initEvents() { mTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { @Override public void onTabSelected(TabLayout.Tab tab) { //選中了tab的邏輯 } @Override public void onTabUnselected(TabLayout.Tab tab) { //未選中了tab的邏輯 } @Override public void onTabReselected(TabLayout.Tab tab) { //再次選中了tab的邏輯 } }); } }
無
Android TabLayout 分分鐘打造一個滑動標籤頁
【Android】Fragment懶加載和ViewPager的坑
TabLayout-ViewPager填充Fragment和懶加載
ViewPager+Fragment LazyLoad懶加載最優解
Android中ViewPager+Fragment取消(禁止)預加載延遲加載(懶加載)問題解決方案
viewPaper+Fragment的佈局,在初始化時會致使fragment的佈局加載和網絡請求數據(懶加載)