前言

其实指示器的自定义控件太多了,但是需求时刻在变,总有不满足的时候,所以就得自己来绘制
因为博主遇到了横线形式的指示器,所以特地分享一下,同时也教一下不会自定义的童鞋


效果图

可以看到可以和ViewPager一起联动,下面就写出实现的过程

首先我们需要弄明白几个点
1. 绘制每一个指示器通过canvas的绘制圆角矩形就行
2. 每一个指示器Item都需要一个Rect对象来描述绘制的位置
3. 和ViewPager联动需要监听ViewPager的滑动事件就能实现

大致实现流程
1. 测量出自身的宽高
2. 根据各个数值算出每一个指示器的Item的位置
3. 根据位置绘制出所有的指示器Item

代码实现

准备:绘制此控件需要的变量属性


    /*** 指示器的宽度,单位是dp*/private int indicatorWidth = 10;/*** 指示器的高度,单位是dp*/private int indicatorHeight = 4;/*** 被选中的指示器的宽度,单位是dp*/private int selectedIndicatorWidth = 20;/*** 被选中的指示器的高度,单位是dp*/private int selectedIndicatorHeight = 4;/*** 每一个指示器之间的间距,单位是dp*/private int indicatorHorizontalSpace = 2;/*** 被选中的指示器的颜色*/private int selectedIndicatorColor = Color.parseColor("#ED5C55");/*** 没被选中的指示器的颜色*/private int unSelectedIndicatorColor = Color.WHITE;/*** 指示器的个数*/private int indicatorCount = 0;/*** 当前选中的指示器下标*/private int indicatorIndex = 0;/*** 偏移的比例,只有>0的,因为ViewPager中如果从0滑动到1,那么这个值就是这么变化的:* 0->0.999->0* 下标变化:* 0->0->1* 如果从1滑动到0,那么这个值就是这么变化的:* 0->0.999->0* 下标变化:* 1->0->0*/private float offSet = 0f;

上述变量中都不需要解释,就最后一个offSet需要解释一下,因为我们肉眼看上去是和ViewPager联动的,但是对于此控件来说,是不断的绘制产生的效果,所以这个变量是记录从
index -> index + 1 或者 index -> index - 1 的时候的进度,所以才能在ViewPager联动的时候改变此值来改变计算出来的位置,下面会用到

2.测量自身的宽高


由于我们此控件基本全部都是包裹的效果,所以这里只处理包裹的效果,无论你写wrap_content或者match_parent都是包裹作用,是一样的,使用的时候注意了哦,支持内边距!

    @Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);// calculate width of no selected indexint width = indicatorCount * indicatorWidth + (indicatorCount - 1) * indicatorHorizontalSpace;// get the bigger heightint height = Math.max(indicatorHeight, selectedIndicatorHeight);// if have selected indicatorif (indicatorIndex >= 0 && indicatorIndex < indicatorCount) {width += selectedIndicatorWidth - indicatorWidth;}// add the paddingwidth += getPaddingLeft() + getPaddingRight();height += getPaddingTop() + getPaddingBottom();// only have wrap// save the width and heightsetMeasuredDimension(width, height);}

测量自己的宽和高,因为允许没选中和选中的指示器有不同的高度,所以在高度方面是采取两者的较大值,因为在效果上我们可以看见选中的那个明显比较高
另外测量的时候,我们绘制的区域算出来之后,加上了内边距的值,这也就支持了内边距的设置效果

3.根据所有可影响的属性值计算出绘制的坐标


    /*** 记录所有指示器的位置信息,包括选中的那个*/private void calculateRectFs() {if (rectFs == null || rectFs.length != indicatorCount) {rectFs = new RectF[indicatorCount];}int startLeft = getPaddingLeft();int startTop = getPaddingTop();int startBottom = getHeight() - getPaddingBottom();//除去内边距可绘制的区域的高度int drawHeight = getHeight() - getPaddingTop() - getPaddingBottom();/* 绘制的时候,因为绘制的指示器有高度不同的情况所以如果高度小一点的,那么就有一个绘制的top起点就有一个dy需要加上去才能保证绘制所有的指示器都在中间*/int dy;int currentIndicatorWidth = indicatorWidth;// 循环计算for (int i = 0; i < indicatorCount; i++) {//如果是选中的那个指示器if (i == indicatorIndex) {dy = (drawHeight - selectedIndicatorHeight) / 2;currentIndicatorWidth = selectedIndicatorWidth;} else {dy = (drawHeight - indicatorHeight) / 2;currentIndicatorWidth = indicatorWidth;}// 创建矩形,填入数据RectF rectF = new RectF(startLeft, startTop + dy,startLeft + currentIndicatorWidth, startBottom - dy);startLeft += currentIndicatorWidth + indicatorHorizontalSpace;rectFs[i] = rectF;}// 这是就是根据偏移的百分比,在正常基础上左右偏移坐标// 但是这里不针对选择的下标是最后一个的情况if (indicatorIndex != indicatorCount - 1) {// 拿到选中那个位置信息和选中的下一个的位置信息RectF rectFSelected = rectFs[indicatorIndex];RectF next = rectFs[indicatorIndex + 1];// 计算两者的偏移量float selectedOffsetPx = (next.right - rectFSelected.right) * offSet;float nextOffsetPx = (rectFSelected.left - next.left) * offSet;// 在原来的基础上加上偏移量rectFSelected.left += selectedOffsetPx;rectFSelected.right += selectedOffsetPx;next.left += nextOffsetPx;next.right += nextOffsetPx;}}

在测量的时候我们在自己绘制的宽高基础上加上了内边距
所以这里计算的时候,也需要考虑内边距的问题,所以绘制的时候起点不是0

int startLeft = getPaddingLeft();
int startTop = getPaddingTop();
int startBottom = getHeight() - getPaddingBottom();

也就是这三句代码,然后接下来的代码都写了注释应该是没问题的,总得一句话就是根据所有可影响的属性计算出每一个指示器Item的绘制区域

4.根据计算出来的坐标数据,绘制出效果


    @Overrideprotected void onDraw(Canvas c) {super.onDraw(c);calculateRectFs();//绘制的起点Paint p = new Paint();p.setColor(Color.GREEN);p.setAntiAlias(true);// 设置画笔的锯齿效果if (rectFs == null) {return;}p.setColor(unSelectedIndicatorColor);for (int i = 0; i < rectFs.length; i++) {//不绘制选中的if (indicatorIndex == i) {continue;}//拿到位置信息RectF r = rectFs[i];//计算出半径float radius = Math.min(Math.abs((r.bottom - r.top) / 2), Math.abs((r.right - r.left) / 2));//绘制圆角矩形c.drawRoundRect(r, radius, radius, p);}if (indicatorIndex >= 0 && indicatorIndex < indicatorCount) {p.setColor(selectedIndicatorColor);//拿到位置信息RectF r = rectFs[indicatorIndex];//计算出半径float radius = Math.min(Math.abs((r.bottom - r.top) / 2), Math.abs((r.right - r.left) / 2));//绘制圆角矩形c.drawRoundRect(r, radius, radius, p);}}

这段代码就很简单了,在每次绘制之前都调用计算的方法,然后根据计算出来的数据直接循环绘制
这里有一点注意了,我们展示的效果中,选中的那个指示器Item是永远在上层的,这也就是绘制的时候必须在最后绘制,先绘制出其他非选中的那些Item

到这里,整个自定义控件就完工了,下面是和ViewPager的滑动进行绑定

这里的代码就是实现了ViewPager的滑动监听接口,然后在滑动的时候不断的改变下标和偏移的百分比,然后重新绘制,那么还差最后一点就是和ViewPager的绑定操作

提供一个方法出来设置ViewPager

    /*** 和ViewPager的滑动事件绑定* 此方法必须在ViewPager设置适配器之后** @param vp*/public void setUpViewPager(@NonNull ViewPager vp) {PagerAdapter adapter = vp.getAdapter();if (adapter != null) {indicatorCount = adapter.getCount();}indicatorIndex = vp.getCurrentItem();vp.removeOnPageChangeListener(this);vp.addOnPageChangeListener(this);}

代码很简单,就不解释了,这样就完成了所有的工作了

源码下载

如果想直接用,直接依赖

在主配置build文件中配置

allprojects {repositories {jcenter()//就是添加上这句maven { url 'https://jitpack.io' }}
}

//在项目模块中直接依赖就能使用了
compile ‘com.github.xiaojinzi123:widget:v1.1.3.2’

就可以使用了,贴出一个完整用法

<com.move.widget.XIndicatorandroid:id="@+id/indicator"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignBottom="@+id/vp"android:layout_centerHorizontal="true"android:layout_marginBottom="10dp"app:indicatorHeight="6dp"app:indicatorSpace="6dp"app:indicatorWidth="20dp"app:index="1"app:count="4"app:selectedIndicatorHeight="8dp"app:selectedIndicatorWidth="32dp" />

自定义控件之-横线指示器相关推荐

  1. Android自定义ViewPager图片指示器,兼容实现底部横线指示器

    前言  记得以前自己使用过的ViewPager Indicator有JakeWharton大神的开源库ViewPagerIndicator,v4包自带的PagerTitleStrip以及Android ...

  2. Android 自定义控件 ViewPager头部指示器控件 ViewPagerBelowIndicator

    效果 演示 说明 为了实现 ViewPager 切换 Fragment 时的标签效果(类似新闻客户端导航的效果) 代码 package com.demo.view;import android.con ...

  3. Android指示器的使用总结

    一.Android指示器的基础知识 Android指示器实现的三种基础方法: 1.使用PagerTitleStrip 2.使用PagerTabStrip 3.使用自定义的View设计宽度 (一)Pag ...

  4. Android 自定义控件之圆形页面指示器CirclePageIndicator带划动效果

    Android 自定义控件之圆形页面指示器CirclePageIndicator带划动效果 前言 感谢 效果图 目标 流程 自定义属性 自定义默认属性 自定义接口 创建控件类继承View 声明属性变量 ...

  5. Android 自定义控件玩转字体变色 打造炫酷ViewPager指示器

    转载请标明出处: http://blog.csdn.net/lmj623565791/article/details/44098729,本文出自: [张鸿洋的博客] 1.概述 本篇博客的产生呢,是因为 ...

  6. Android 自定义控件玩转字体变色 打造炫酷ViewPager指示器

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 转载请标 ...

  7. qt获取当前系统音量值_Qt编写自定义控件50-迷你仪表盘

    一.前言 这个控件取名叫迷你仪表盘,是以为该控件可以缩小到很小很小的区域显示,非常适合小面积区域展示仪表数据使用,还可以手动触摸调节进度,是我个人觉得最漂亮小巧的一个控件.初次看到类似的控件是在一个音 ...

  8. 3指南针旋转_Qt编写自定义控件6-指南针仪表盘

    前言 指南针仪表盘,主要用来指示东南西北四个方位,双向对称两个指针旋转,其实就是360度打转,功能属于简单型,可能指针的绘制稍微难一点,需要计算多个点构成多边形,本系列控件文章将会连续发100+篇,一 ...

  9. Android自定义控件之轮播图控件

    背景 最近要做一个轮播图的效果,网上看了几篇文章,基本上都能找到实现,效果还挺不错,但是在写的时候感觉每次都要单独去重新在Activity里写一堆代码.于是自己封装了一下.这里只是做了下封装成一个控件 ...

最新文章

  1. linux cpu 使用
  2. 轻松学习Ionic (二) 为Android项目集成Crosswalk(更新官方命令行工具)
  3. Flex 学习笔记------as 与 js 的通信
  4. 【壹刊】Azure AD(三)Azure资源的托管标识
  5. java 异常对象_在java中的异常处理中的异常对象是什么
  6. Hadoop完全分布式安装
  7. python-50: 验证码
  8. python修饰器执行步骤_Python修饰器学习总结
  9. 29. PHP 错误控制
  10. ati 缺少关键性文件_win10重装系统缺少计算机所需的介质驱动程序的解决方法
  11. freemarker的下拉框回显
  12. The Fifty-eighth Of Word-Day
  13. 【转帖】计算机编程语言
  14. openpyxl详解
  15. 如何选择靠谱的游戏音乐外包呢?
  16. 穹顶之下的出行利器:百度地图热力
  17. 将32位浮点音频转换为16位
  18. AI就业前景越来越严峻了,AI 开发者能怎样提升个人竞争优势?
  19. Rubber Translator一款更人性化的免费文献翻译工具,支持多翻译引擎
  20. 手把手教你快速搭建个人博客 Hexo + Github

热门文章

  1. 思考:你的工作是否有反脆弱性?
  2. C# BlockingCollection 简单用法
  3. Android内核开发 Goldfish Linux Kernel编译及安卓虚拟机测试
  4. Python Print打印计时器功能
  5. Ethereum智能合约迁移到PlatON教程
  6. DiscuzX3.1数据库字典(含之前)
  7. 系统部署服务器位置,室内定位系统在实际项目中的部署
  8. 银行定期存三个月利息计算机公式,银行存款利息如何计算?如定期三个月,半年、一年、二年 爱问知识人...
  9. MySQL SHOW INDEX 的语法解析
  10. C++的反思(知乎精华)