最新更新的Flyme6整体效果不错,动画效果增加了很多了,看了看flyme6的Viewpager指示器,觉得有点意思,就模仿写了一下,整体效果如下:
github地址:https://github.com/Dawish/FlymeTabStrip
Gradle
allprojects {
repositories {
maven { url 'https://jitpack.io' }
}
}
dependencies {
compile 'com.github.Dawish:FlymeTabStrip:v1.0.2'
}
Attrs
<declare-styleable name="FlymeTabStrip">
<!-- 指示器高度 -->
<attr name="indicatorHeight" format="dimension" />
<!-- 指示器滑动条颜色 -->
<attr name="indicatorColor" format="color" />
<!-- 指示器左右间距 -->
<attr name="indicatorMargin" format="dimension" />
<!-- 指示器文字颜色 -->
<attr name="indicatorTextColor" format="color" />
<!-- 指示器文字大小 -->
<attr name="indicatorTextSize" format="dimension" />
<!-- 指示器文字被选中后的大小 -->
<attr name="selectedIndicatorTextSize" format="dimension" />
</declare-styleable>
Sample
Demo地址:https://github.com/Dawish/FlymeTabStrip/tree/master/samples
代码解释
指示器的动画效果,主要依赖Viewpager的滑动监听器,在Viewpager的滑动过程中不断重绘只是控件就可以实现指示器的位移和缩放动画效果。首先我们讲解一下ViewPager的三个滚动监听方法:
/**
* viewPager状态改变监听
*
*/
private class PagerStateChangeListener implements OnPageChangeListener {
/**
* viewpager状态监听
* @param state
*/
@Override
public void onPageScrollStateChanged(int state) {
if (state == ViewPager.SCROLL_STATE_IDLE) { // 0 空闲状态 pager处于空闲状态
scrollToChild(viewPager.getCurrentItem(), 0);
}else if(state == ViewPager.SCROLL_STATE_SETTLING){ // 2 正在自动沉降,相当于松手后,pager恢复到一个完整pager的过程
}else if(state == ViewPager.SCROLL_STATE_DRAGGING){ // 1 viewpager正在被滑动,处于正在拖拽中
}
}
/**
* viewpager正在滑动,会回调一些偏移量
* 滚动时,只要处理指示器下方横线的滚动
* @param position 当前页面
* @param positionOffset 当前页面偏移的百分比
* @param positionOffsetPixels 当前页面偏移的像素值
*/
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
currentPosition = position;
currentPositionOffset = positionOffset;
// 处理指示器下方横线的滚动,scrollToChild会不断调用ondraw方法,绘制在重绘下划线,这就是移动动画效果
scrollToChild(position, (int) (positionOffset * container.getChildAt(position).getWidth()));
invalidate();
}
/**
* page滚动结束
* @param position 滚动结束后选中的页面
*/
@Override
public void onPageSelected(int position) {
// 滚动结束后的未知
selectedPosition = position;
// 更新指示器状态
updateTabStyle();
}
}
其中最主要的方法就是:
/**
* viewpager正在滑动,会回调一些偏移量
* 滚动时,只要处理指示器下方横线的滚动
* @param position 当前页面
* @param positionOffset 当前页面偏移的百分比
* @param positionOffsetPixels 当前页面偏移的像素值
*/
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
currentPosition = position;
currentPositionOffset = positionOffset;
// 处理指示器下方横线的滚动,scrollToChild会不断调用ondraw方法,绘制在重绘下划线,这就是移动动画效果
scrollToChild(position, (int) (positionOffset * container.getChildAt(position).getWidth()));
invalidate();
}
onPageScrolled方法图片说明:
onDraw主要的绘制工作:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 如果指示器个数为0,直接结束绘画
if (tabCount == 0) {
return;
}
// 获取onMeasure后的高
final int height = getHeight();
/*
* 画指示器下方的线
*/
// 设置颜色
paint.setColor(indicatorColor);
// 当前指示tab位置
View currentTab = container.getChildAt(currentPosition);
// 当前tab的左边相对父容器的左边距
float leftPadding = currentTab.getLeft();
// 当前tab的右边相对于父容器左边距
float rightPadding = currentTab.getRight();
float tempPadding = 20;
// 如果出现位移
float centerPosition = 0.0f;
if (currentPositionOffset >= 0f && currentPosition < tabCount - 1) {
View nextTab = container.getChildAt(currentPosition + 1);
final float nextTabLeft = nextTab.getLeft();
final float nextTabRight = nextTab.getRight();
// 位置是在滑动过程中不断变化的
leftPadding = (currentPositionOffset * nextTabLeft + (1f - currentPositionOffset) * leftPadding);
rightPadding = (currentPositionOffset * nextTabRight + (1f - currentPositionOffset) * rightPadding);
}
centerPosition = (rightPadding - leftPadding)/2 + leftPadding;
float left = centerPosition - tempPadding - formatPercent(currentPositionOffset)*tempPadding;
float right = centerPosition + tempPadding + formatPercent(currentPositionOffset)*tempPadding;
// 绘制
canvas.drawRect(left, height - indicatorHeight, right, height, paint);
}