版权声明:本文为HaiyuKing原创文章,转载请注明出处!
前言
使用TabLayout搭配ViewPager实现可滑动的顶部选项卡效果。
效果图
代码分析
1、演示常规的设置。
2、通过自定义ViewPager(MyCustomViewPager)解决解决切换需要经过中间页的问题、实现控制viewpager是否可滑动的功能;
3.1、通过在Fragment中的OnCreateView中判断rootView是否为空来解决viewpager+fragment来回滑动fragment重新加载的问题;
3.2、还有一个方案是在自定义的viewpager适配器类中重写destroyItem方法,来解决重新加载的问题;【核心就是不销毁fragment】
4、通过自定义Fragment基类(BaseLazyFragment)来实现配合viewpager使用时禁止懒加载的功能;
存在一个问题:那就是当选项卡比较多的时候,从首页切换到尾页,然后切换回来首页的时候,会重新请求数据,因为首页已经销毁了,执行了onDestroyView方法。
解决方案:
一、使用setOffscreenPageLimit()方法, 设置数字越大越好(可以设置总数目);
二、采用3.2方案;
使用步骤
一、项目组织结构图
注意事项:
1、 导入类文件后需要change包名以及重新import R文件路径
2、 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'
}
在colors.xml文件中添加以下代码:【后续可根据实际情况更改背景颜色、文字颜色值】
<?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>
在dimens.xml文件中添加以下代码:【后续可根据实际情况更改底部选项卡区域的高度值、文字大小值】
<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>
在styles.xml文件中添加以下代码:
<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>
在AndroidManifest.xml文件中添加网络请求的权限【demo中用到的】
<?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>
三、使用方法
在Activity布局文件中引用TabLayout【注意更改MyViewpager的完整路径】
<?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类和布局文件【后续可根据实际情况更改命名,并且需要重新import R文件】
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(){ } }
BaseLazyFragment
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; } }