自定义控件之-横线指示器
来源:互联网 发布:php提权 编辑:程序博客网 时间:2024/06/01 09:42
前言
其实指示器的自定义控件太多了,但是需求时刻在变,总有不满足的时候,所以就得自己来绘制
因为博主遇到了横线形式的指示器,所以特地分享一下,同时也教一下不会自定义的童鞋
效果图
可以看到可以和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都是包裹作用,是一样的,使用的时候注意了哦,支持内边距!
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); // calculate width of no selected index int width = indicatorCount * indicatorWidth + (indicatorCount - 1) * indicatorHorizontalSpace; // get the bigger height int height = Math.max(indicatorHeight, selectedIndicatorHeight); // if have selected indicator if (indicatorIndex >= 0 && indicatorIndex < indicatorCount) { width += selectedIndicatorWidth - indicatorWidth; } // add the padding width += getPaddingLeft() + getPaddingRight(); height += getPaddingTop() + getPaddingBottom(); // only have wrap // save the width and height setMeasuredDimension(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.根据计算出来的坐标数据,绘制出效果
@Override protected 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.XIndicator android: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" />
- 自定义控件之-横线指示器
- Android 自定义控件之圆点指示器 View (IndicateDotView)
- 悬浮指示器布局-自定义控件
- 自定义横线样式的密码输入控件
- Android 自定义控件ViewPager 指示器 ViewPagerIndicator
- 【笔记】自定义控件——ViewPager指示器
- viewpager的下横线指示器
- 安卓基础:自定义控件实现ViewPager指示器
- 自定义控件——视图树绘制指示器
- 轻松实现分页指示器 ViewPagerIndicator Android自定义控件
- 自定义控件——万能的圆形指示器Indictor
- 11-常用UI控件之 UIProgressView 进度指示器
- Android精选完整源码之控件指示器视频压缩日历源码!
- cell自定义横线
- Android自定义View之超简单圆形数字指示器
- Android自定义view之ViewPager指示器——1
- Android自定义view之ViewPager指示器——2
- 【UIActivityIndicator活动指示器控件】
- 互联网协议(2)——数据链路层
- 用C语言编程实现“判断某年某月有几天”
- Codeforces Gym100935
- Mysql索引详解
- RegexSyntax
- 自定义控件之-横线指示器
- 属性动画系列之ViewPropertyAnimator
- c++学习笔记 -- 继承(2)
- windbg调试句柄泄漏
- Qt窗口的删除、析构
- GPIO
- ETL利器Kettle实战应用解析系列三 【ETL后台进程执行配置方式】
- Robotium中webview源码分析
- ffmpeg基本用法(转)介绍