• TabLayoutViewPagerDemo【TabLayout+ViewPager可滑动】


    版权声明:本文为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;
        }
    }
    BaseLazyFragment.java

    创建选项卡子项model类TabItemModel

    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;
        }
    }

    创建viewpager的适配器

    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);
        }
    }

    MyCustomViewPager

    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);
        }
    }
    MyCustomViewPager.java

    在Activity中使用如下【继承FragmentActivity或者其子类

    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 分分钟打造一个滑动标签页

    Design库-TabLayout属性详解

    Design库-TabLayout仿京东商品详情Tab

    TabLayout

    Fragment 懒加载实战

    【Android】Fragment懒加载和ViewPager的坑

    TabLayout-ViewPager填充Fragment和懒加载

    ViewPager+Fragment LazyLoad懒加载最优解

    Android中ViewPager+Fragment取消(禁止)预加载延迟加载(懒加载)问题解决方案

    viewPaper+Fragment的布局,在初始化时会导致fragment的布局加载和网络请求数据(懒加载)

    项目demo下载地址

    https://github.com/haiyuKing/TabLayoutViewPagerDemo

  • 相关阅读:
    Oracle Data Integrator 12c-----场景(Scenario)和调度(Schedule)
    Oracle 12c 用户创建、角色、权限
    CDB和PDB的创建、连接、启动、关闭
    Oracle Data Integrator 12c----包(Package)
    Oracle Data Integrator 12c-第一个映射
    Oracle Data Integrator 12c-模型(Model)和 数据存储(DataStore)
    解锁scott账户方法
    ORA-12514 TNS 监听程序当前无法识别连接描述符中请求服务
    VirtualBox内Linux系统怎样与Windows共享文件夹
    Changing the Language Used in ODI Studio
  • 原文地址:https://www.cnblogs.com/whycxb/p/7788725.html
Copyright © 2020-2023  润新知