说到 ViewPager 指示器,想必大家都不陌生,绝大部分应用中都有这个。使用频率非常之高。但系统对它的支持并不好,自带的 PagerTabStrip 和 PagerTitleStrip 太弱,很难满足需求。因此往往程序中需要用到指示器的时候,大家基本上都是选择自己写。当然也有第三方框架诸如 Jake Wharton 大神的ViewPagerIndicator
等,我曾经尝试着用它们,但还是被它们的可定制能力给吓退了。

magicindicator.gif

背景


近期交互改版,需要在指示器上增加吸附效果(类似上图第7个),刚开始我有点懵逼,因为之前的指示器只是简单的使用了 HorizontalScrollView + 横向 LinearLayout ,向 LinearLayout 里面添加一些 TextView 当做标题,选中的时候只是简单的改变 TextView 的颜色,没有任何动画,因此实现起来相对简单(项目前期时间紧迫)。这估计也是大部分应用的做法吧。

考虑到后面如果交互再改版,那我又会懵逼了,所以干脆自己来打造一个可扩展、可定制能力 很强 真TM强 的 ViewPager 指示器框架 ——MagicIndicator

关于命名


之所以叫 MagicIndicator,是因为 鸿神 之前搞了一个 MagicViewPager, 我觉得这两个可以很好的搭配使用,并且正如大家看到的,它确实比较 Magic。

如何使用


这期就不打算给大家讲原理性文章了,只讲如何集成(主要是这两天都在写这个,被媳妇骂惨了,没时间写了),后面我会有一个系列的文章来讲这个,涉及到的内容大概有:

  • MagicIndicator 原理

  • 如何扩展 MagicIndicator 打造任意的切换效果

  • 如何使用 MagicIndicator 来打造主流应用(诸如微信主页的切换)的指示器效果

好,我们开始。

首先,你需要从我的 Github 上将工程代码 check 下来,直接导入即可运行,里面包含源码和 demo。工程中有个 Module 叫做 magicindicator,直接拷到你的项目中即可。包结构如下:

Paste_Image.png

不要忘了添加依赖哦:

dependencies {
    compile project(':magicindicator')
}

导入成功后,就可以使用啦。你需要先在布局文件里引入 MagicIndicator:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="net.lucode.hackware.magicindicatordemo.MainActivity">

    <net.lucode.hackware.magicindicator.MagicIndicator
        android:id="@+id/magic_indicator"
        android:layout_width="match_parent"
        android:layout_height="@dimen/navigator_common_height"
        android:background="#d43d3d" />

    <android.support.v4.view.ViewPager
        android:id="@+id/view_pager"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:background="@android:color/white" />

</LinearLayout>

再在代码里面找到它,并进行简单设置:

final MagicIndicator magicIndicator = (MagicIndicator) findViewById(R.id.magic_indicator);
final CommonNavigator commonNavigator = new CommonNavigator(this);
commonNavigator.setAdapter(new CommonNavigatorAdapter() {

    @Override
    public int getCount() {
        return mDataList == null ? 0 : mDataList.size();
    }

    @Override
    public IPagerTitleView getItemView(Context context, final int index) {
        ClipPagerTitleView clipPagerTitleView = new ClipPagerTitleView(context);

        clipPagerTitleView.setText(mDataList.get(index));
        clipPagerTitleView.setTextColor(Color.parseColor("#f2c4c4"));
        clipPagerTitleView.setClipColor(Color.WHITE);

        clipPagerTitleView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mPager.setCurrentItem(index);
            }
        });

        return clipPagerTitleView;
    }

    @Override
    public IPagerIndicator getIndicator(Context context) {
        return null;    // 没有指示器,因为title的指示作用已经很明显了
    }
});
magicIndicator.setNavigator(commonNavigator);

这样,你就可以轻松的实现效果图中 今日头条 式(效果图中第一个)切换效果。

额,上面这代码明显没有和 ViewPager 相关联,因此滑动 ViewPager 时是看不到切换效果的,我们给 ViewPager 添加一个监听器:

mPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        magicIndicator.onPageScrolled(position, positionOffset, positionOffsetPixels);
    }

    @Override
    public void onPageSelected(int position) {
        magicIndicator.onPageSelected(position);
    }

    @Override
    public void onPageScrollStateChanged(int state) {
        magicIndicator.onPageScrollStateChanged(state);
    }
});

mPager.setCurrentItem(1);

这样, MagicIndicator 就算成功引入项目啦。

  • 注意,以上代码明显没有和 ViewPager 强关联,因此在不使用 ViewPager 的情况下,仍然可以使用 MagicIndicator。只是需要你手动调用 onPageXXX 系列方法回调即可。当然,后续我会提供帮助类来简化这个过程。
  • 等 MagicIndicator 基本稳定、成型后,我会把它提交到 MavenCenter 和 JCenter 中,方便大家使用。

内建的指示器


MagicIndicator 目前内建了好几种指示器,基本可以满足绝大部分需求,并且每一种指示器都支持通过 插值器(Interpolator) 来微调效果(如果你还不会巧用插值器,可以参考我的前一篇博文Android水波纹特效的简单实现 ),后面我还会不定期的往里面添加更多炫酷的效果,敬请期待。下面演示一下使用内建的指示器实现效果图中的小尖角
式切换效果:

final MagicIndicator magicIndicator = (MagicIndicator) findViewById(R.id.magic_indicator);
final CommonNavigator commonNavigator = new CommonNavigator(this);
commonNavigator.setAlwaysScrollToCenter(true);
commonNavigator.setAdapter(new CommonNavigatorAdapter() {

    @Override
    public int getCount() {
        return mDataList == null ? 0 : mDataList.size();
    }

    @Override
    public IPagerTitleView getItemView(Context context, final int index) {
        SimplePagerTitleView simplePagerTitleView = new SimplePagerTitleView(context);

        simplePagerTitleView.setText(mDataList.get(index));
        simplePagerTitleView.setNormalColor(Color.parseColor("#333333"));
        simplePagerTitleView.setSelectedColor(Color.parseColor("#e94220"));

        simplePagerTitleView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mPager.setCurrentItem(index);
            }
        });

        return simplePagerTitleView;
    }

    @Override
    public IPagerIndicator getIndicator(Context context) {
        TriangularPagerIndicator indicator = new TriangularPagerIndicator(context);
        indicator.setLineColor(Color.parseColor("#e94220"));
        return indicator;
    }
});
magicIndicator.setNavigator(commonNavigator);

看到没有,如果你想换一个效果,只需在 getItemView 里返回不同的指示器标题(IPagerTitleView),在getIndicator 里返回不同的指示器(IPagerIndicator)就可以啦。

如何扩展


当内建的指示器不能满足你的需求时,你可以轻易的扩展,如果你的需求貌似可以使用 HorizontalScrollView + 横向 LinearLayout 方式实现,建议继承IPagerTitleViewIPagerIndicator 即可,如果不行,那就完全自定义吧,继承IPagerNavigator
(导航器 —— 我觉得上面的那些效果本质是一个导航器,指示器只是包含在导航器中的一个元素而已) 吧。效果图中的最后一个效果就是如此:

final MagicIndicator magicIndicator = (MagicIndicator) findViewById(R.id.magic_indicator);
final CircleNavigator circleNavigator = new CircleNavigator(this);
circleNavigator.setCount(mDataList.size());
circleNavigator.setCircleColor(Color.RED);
magicIndicator.setNavigator(circleNavigator);

当然,我可不希望每当内置指示器达不到你的需求时,你总是去继承这些个类,为了简化,我在最新的代码里增加了 CommonPagerTitleView,使用方法如下:

final MagicIndicator magicIndicator = (MagicIndicator) findViewById(R.id.magic_indicator);
CommonNavigator commonNavigator = new CommonNavigator(this);
commonNavigator.setAdapter(new CommonNavigatorAdapter() {

    @Override
    public int getCount() {
        return mDataList == null ? 0 : mDataList.size();
    }

    @Override
    public IPagerTitleView getItemView(Context context, final int index) {
        CommonPagerTitleView commonPagerTitleView = new CommonPagerTitleView(MainActivity.this);
        commonPagerTitleView.setContentView(R.layout.simple_pager_title_layout);

        // 初始化
        final ImageView titleImg = (ImageView) commonPagerTitleView.findViewById(R.id.title_img);
        titleImg.setImageResource(R.mipmap.ic_launcher);
        final TextView titleText = (TextView) commonPagerTitleView.findViewById(R.id.title_text);
        titleText.setText(mDataList.get(index));

        commonPagerTitleView.setOnPagerTitleChangeListener(new CommonPagerTitleView.OnPagerTitleChangeListener() {

            @Override
            public void onSelected(int index) {
                titleText.setTextColor(Color.RED);
            }

            @Override
            public void onDeselected(int index) {
                titleText.setTextColor(Color.BLACK);
            }

            @Override
            public void onLeave(int index, float leavePercent, boolean leftToRight) {
                titleImg.setScaleX(1.3f + (0.8f - 1.3f) * leavePercent);
                titleImg.setScaleY(1.3f + (0.8f - 1.3f) * leavePercent);
            }

            @Override
            public void onEnter(int index, float enterPercent, boolean leftToRight) {
                titleImg.setScaleX(0.8f + (1.3f - 0.8f) * enterPercent);
                titleImg.setScaleY(0.8f + (1.3f - 0.8f) * enterPercent);
            }
        });

        commonPagerTitleView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mPager.setCurrentItem(index);
            }
        });

        return commonPagerTitleView;
    }

    @Override
    public IPagerIndicator getIndicator(Context context) {
        return null;
    }
});
magicIndicator.setNavigator(commonNavigator);

效果图:

custom.gif

关于扩展 MagicIndicator,后面会有详细的博文来讲解,敬请留意。如果你扩展 MagicIndicator 实现了很炫酷的指示器效果,欢迎共享出来。

更多


MagicIndicator 同样考虑到了 ViewPager 内容变化的情况,当你的 ViewPager 内容发生变化时,除了调用 PagerAdapter.notifyDataSetChanged ,还记得先调用 IPagerNavigator.notifyDataSetChanged 哦:

mDataList.clear();
mDataList.add("欢迎关注");
mDataList.add("我的博客");
mDataList.add("hackware.lucode.net");
commonNavigator.notifyDataSetChanged();
mAdapter.notifyDataSetChanged();

源码下载:http://download.csdn.net/detail/xiangzhihong8/9571280

著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

Android万能的指示器的更多相关文章

  1. Android 基于ijkplayer+Rxjava+Rxandroid+Retrofit2.0+MVP+Material Design的android万能播放器aaa

    MDPlayer万能播放器 MDPlayer,基于ijkplayer+Rxjava+Rxandroid+Retrofit2.0+MVP+Material Design的android万能播放器,可以播 ...

  2. Android进阶笔记10:Android 万能适配器

    1. Android 万能适配器      项目中Listview GridView几乎是必用的组件,Android也提供一套机制,为这些控件绑定数据,那就是Adapter.用起来虽然还不错,但每次都 ...

  3. 使用Vitamio打造自己的Android万能播放器(6)——在线播放(播放列表)

    前言 新版本的VPlayer由设计转入开发阶段,预计开发周期为一个月,这也意味着新版本的Vitamio将随之发布,开发者们可以和本系列文章一样,先开发其他功能.本章内容为"在线视频播放列表& ...

  4. 使用Vitamio打造自己的Android万能播放器(5)——在线播放(播放优酷视频)

    前言 为了保证每周一篇的进度,又由于Vitamio新版本没有发布, 决定推迟本地播放的一些功能(截图.视频时间.尺寸等),跳过直接写在线播放部分的章节.从Vitamio的介绍可以看得出,其支持http ...

  5. 使用Vitamio打造自己的Android万能播放器(4)——本地播放(快捷搜索、数据存储)

    前言 关键字:Vitamio.VPlayer.Android播放器.Android影音.Android开源播放器 本章节把Android万能播放器本地播放的主要功能(缓存播放列表和A-Z快速查询功能) ...

  6. 使用Vitamio打造自己的Android万能播放器(3)——本地播放(主界面、播放列表)

    前言 打造一款完整可用的Android播放器有许多功能和细节需要完成,也涉及到各种丰富的知识和内容,本章将结合Fragment.ViewPager来搭建播放器的主界面,并实现本地播放基本功能.系列文章 ...

  7. 使用Vitamio打造自己的Android万能播放器(2)—— 手势控制亮度、音量、缩放

    前言 本章继续完善播放相关播放器的核心功能,为后续扩展打好基础.   声明 欢迎转载,但请保留文章原始出处:)  博客园:http://www.cnblogs.com 农民伯伯: http://ove ...

  8. 使用Vitamio打造自己的Android万能播放器(1)——准备

    前言 虽然Android已经内置了VideoView组件和MediaPlayer类来支持开发视频播放器,但支持格式.性能等各方面都十分有限,这里与大家一起利用免费的Vitamio来打造属于自己的And ...

  9. 使用Vitamio打造自己的Android万能播放器(7)——在线播放(下载视频)

    前言 本章将实现非常实用的功能——下载在线视频.涉及到多线程.线程更新UI等技术,还需思考产品的设计,如何将新加的功能更好的融入到现有的产品中,并不是简单的加一个界面就行了,欢迎大家交流产品设计和技术 ...

随机推荐

  1. 粗糙的es6 -> es5转换正则集

    (r'() => {}','function () {return {}}'), # (r'\{\.\.\.(.+?)\}','Object.assign({}, \\1)') , # (r'( ...

  2. 计算机网络之文件传送协议FTP

    FTP 文件传送协议FTP(File Transfer Protocol)是因特网上使用最广泛的文件传送协议. FTP 提供交互式的访问,允许客户指明文件的类型与格式,并允许文件具有存取权限.FTP ...

  3. Android音频处理——通过AudioRecord去保存PCM文件进行录制,播放,停止,删除功能

    Android音频处理--通过AudioRecord去保存PCM文件进行录制,播放,停止,删除功能 音频这方面很博大精深,我这里肯定讲不了什么高级的东西,最多也只是一些基础类知识,首先,我们要介绍一下 ...

  4. 制作pypi上的安装库

    下载地址 如何制作分发工具呢 setuppy 源码包 其他文件 制作过程 首先上场的肯定是setuppy了如下 然后是LICENCE 注册 测试 总结 自从接触Python以来也有几个月了,虽然主要的 ...

  5. OpenCV Python 录制视频

    调用摄像头 引入库支持 初始化 调整界面大小 实时显示 录制视频并保存 fourcc问题解决 代码实现 效果展示 总结 学到实用OpenCV调用笔记本电脑的摄像头,并录制视频保存到本地硬盘的时候,出现 ...

  6. Python实现数据库一键导出为Excel表格

    依赖 Python2711 xlwt MySQLdb 数据库相关 连接 获取字段信息 获取数据 Excel基础 workbook sheet 案例 封装 封装之后 测试结果 总结 数据库数据导出为ex ...

  7. 使用 纯JQuery 进行 表单 验证

    对于JavaScript而言,进行表单数据的验证可谓是很有必要的,而且一般我们都会在网页上先进行一下表单验证,然后服务器端再次进行验证,来确保用户提交数据的准确性.下面就来分享一个JQuery实现的表 ...

  8. SQL 数据库语言分析总结(一)

    SQL语言是被广泛采用的数据库的学习语言,之前在本科的时候已经学习过了,但是后来又忘记了,所以这次简单的总结一下. 分类 交互式sql语言,交互式语言主要是利用一些数据库工具,比如mysql的终端工具 ...

  9. UE4利用Save Game创建全局变量

    因为盲目的做了一个UE4的项目,没有用到UE4的无缝加载,我只能在一个个关卡中手动切换,然后每次的数据都会重置,这对于项目来说,造成了体验感的极度下降. 然而我查了一下怎样在UE4中创建全局变量,找到 ...

  10. Redis主从和HA配置

    1同步原理 摘自:http://www.cnblogs.com/stephen-liu74/archive/2012/03/30/2364717.html "下面的列表清楚的解释了Redis ...