前言
ViewPager实现横向轮播在很多App的主页面上都会有,通常在底部会有一排小圆点用来标识当前被选中的条目和总条目,这些小圆点有时候也会做一些简单的动画效果,这里来简单实现两种常见的底部切换指示器。
实现效果
宽度缩放
先来看第一种的实现方式,选中的那个圆点会被拉伸并且将旁边的小圆点挤到右边,这里考虑用上属性动画来实现拉伸小圆点的宽度。当然之前还有一个被选中的会因为切换导致宽度变窄,最终变成一个小圆点。
首先定义基本的背景图片,一个有圆角的长方形,不固定它的宽高,选中下颜色为白色,未选中为黑色。
// 未选中背景
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="10dp" />
<solid android:color="@android:color/black" />
</shape>
// 选中的背景
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="10dp" />
<solid android:color="@android:color/white" />
</shape>
自定义的Indicator可以继承自横向的LinearLayout对象,当用户将ViewPager对象初始化好之后传入Indicator根据ViewPager内部的数量添加ImageView,最后需要监听ViewPager的页面切换动作:
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
int count = mViews.size();
for (int i = 0; i < count; i++) {
ViewWrapper viewWrapper = mViews.get(i);
if (i == position) {
viewWrapper.setBackgroundResource(R.drawable.normal_dot);
// 放大选中的指示图
ObjectAnimator animator = ObjectAnimator.ofInt(viewWrapper,
"width", viewWrapper.getWidth(), SELECTED_WIDTH)
.setDuration(300);
animator.start();
} else {
viewWrapper.setBackgroundResource(R.drawable.black_dot);
// 缩小选中的指示图
ObjectAnimator animator = ObjectAnimator.ofInt(viewWrapper,
"width", viewWrapper.getWidth(), UNSELECTED_WIDTH)
.setDuration(300);
animator.start();
}
}
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
因为View类的getWidth和setWidth没有实际修改布局的大小,这里需要对View做包装操作,让属性动画实际执行在包装对象上,包装对象内部修改布局参数来实现View宽度的缩放。
public static class ViewWrapper {
private View view;
public ViewWrapper(View view) {
this.view = view;
}
public void setBackgroundResource(int resource) {
this.view.setBackgroundResource(resource);
}
public void setWidth(int width) {
view.getLayoutParams().width = width;
view.requestLayout();
}
public int getWidth() {
return view.getLayoutParams().width;
}
}
交换位置
想要修改ImageView在LinearLayout中的横向位置可以设置translationX属性,不过View提供了一个简单的setX方法来实现水平方向的位移操作。交换动画不过是将之前选中的小点和当前选中小点之间的位置做交换,这个动作也可以用属性动画来做。
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
// 保存小点的ImageView的List
View view = mViews.get(position);
View preView = mViews.get(mPreSelected);
// 记录下之前选中小点的位置
float preX = preView.getX();
// 当前被选中小点的位置
float curX = view.getX();
// 之前被选中小点移动到当前小点位置
ObjectAnimator pre = ObjectAnimator.ofFloat(preView, "x", preX, curX);
// 当前选中小点移动到之前选中小点位置
ObjectAnimator cur = ObjectAnimator.ofFloat(view, "x", curX, preX);
AnimatorSet set = new AnimatorSet();
set.setDuration(300).play(pre).with(cur);
set.start();
// 同时交换它们在List中的位置
Collections.swap(mViews, position, mPreSelected);
mPreSelected = position;
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
这两种实现都比较简单,不过对于理解属性动画的使用有很不错的示范效果,这里简单记录一下。