ViewPager的用途很广,像滚动广告、引导页等就常用这个控件实现,而在这些控件里常常会需要一个指示器来表示当前所显示的是第几张广告或第几页。
不堪回首的过去
以前在滚动广告时常常在布局文件中放一个LinearLayout,然后在代码中动态创建ImageView,添加到容器中,
dotList = new ArrayList<ImageView>();
for (int i=0;i<list.size();i++) {
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(10,10);
lp.setMargins(5,5,5,5);
ImageView imageView = new ImageView(mContext);
imageView.setLayoutParams(lp);
imageView.setImageResource(R.drawable.dot_normal);
//添加到指示器列表中
dotList.add(imageView);
//添加到LinearLayout容器中
llDot.addView(imageView);
}
每当广告滚动就去改变一下选中的指示点
for (int i=0;i<dotList.size();i++){
dotList.get(i).setImageResource(R.drawable.dot_normal);
}
dotList.get(position%dotList.size()).setImageResource(R.drawable.dot_light);
自定义ViewPager指示器
根据原来的做法是在代码中动态创建指示点,然后加入到LinearLayout容器中,自定义的控件也通过继承LinearLayout来实现。
定义一个选中的指示点。 class SelectView extends View {
private Paint mPaint;
public SelectView(Context context) {
super(context);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(colorSelected);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(radius * 2, radius * 2);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
paint.setColor(colorSelected);
canvas.drawCircle(radius, radius, radius, mPaint);
}
}
为什么只定义选中的指示点呢?
这里把指示点分为选中指示点和未选中指示点,未选中指示点无论是颜色和位置在控件显示之后都不需要改变,在这里可以当作背景,而选中指示点则需要随着ViewPager的onPageScrolled而移动。
/**
* 绘制背景指示器
*
* @param canvas
*/
private void drawBackground(Canvas canvas) {
paint.setColor(colorNormal);
for (int i = 0; i < num; i++) {
canvas.drawCircle(
padding + radius + i * (2 * radius + padding),
padding + radius,
radius,
paint
);
}
}
根据ViewPager的onPageScrolled显示选中点的位置
/**
* 设置页数和偏移
*
* @param pager
* @param offset
*/
public void setPagerAndOffset(int pager, float offset) {
this.pager = pager;
this.offset = offset;
//调用会使View测量、布局、重绘
requestLayout();
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
//设置选中点的位置
selectView.layout(
(int) (padding + (padding + 2 * radius) * (pager + offset)),
padding,
(int) (padding + (padding + 2 * radius) * (pager + 1 + offset)),
padding + radius * 2
);
}
完整实现
public class ViewPagerIndicator extends LinearLayout {
public ViewPagerIndicator(Context context) {
this(context, null);
}
public ViewPagerIndicator(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ViewPagerIndicator(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs);
}
//指示器半径
private int radius = 10;
//画笔
private Paint paint;
//普通指示器颜色
private int colorNormal = Color.GRAY;
//选中指示器颜色
private int colorSelected = Color.RED;
//间距
private int padding = 20;
//个数
private int num = 3;
//选中的指示器
private SelectView selectView;
//当前选中页
private int pager;
//偏移
private float offset;
/**
* 初始化
*
* @param attrs
*/
private void init(AttributeSet attrs) {
//初始化属性
initAttrs(attrs);
//初始化工具
initTools();
//不调用自身onDraw
setWillNotDraw(false);
}
/**
* 初始化属性
*
* @param attrs
*/
private void initAttrs(AttributeSet attrs) {
TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.ViewPagerIndicator);
radius = typedArray.getInt(R.styleable.ViewPagerIndicator_radius, 5);
colorNormal = typedArray.getColor(R.styleable.ViewPagerIndicator_colorNormal, Color.GRAY);
colorSelected = typedArray.getColor(R.styleable.ViewPagerIndicator_colorSelected, Color.RED);
padding = typedArray.getInt(R.styleable.ViewPagerIndicator_padding, 10);
num = typedArray.getInt(R.styleable.ViewPagerIndicator_num, 3);
typedArray.recycle();
}
/**
* 初始化工具
*/
private void initTools() {
//初始化画笔
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(colorNormal);
//初始化选中点
selectView = new SelectView(getContext());
addView(selectView);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//计算设置控件的宽高
setMeasuredDimension(padding + (padding + 2 * radius) * num, (padding + radius) * 2);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawBackground(canvas);
}
/**
* 绘制背景指示器
*
* @param canvas
*/
private void drawBackground(Canvas canvas) {
paint.setColor(colorNormal);
for (int i = 0; i < num; i++) {
//绘制每个点的位置
canvas.drawCircle(
padding + radius + i * (2 * radius + padding),
padding + radius,
radius,
paint
);
}
}
public void setNum(int num) {
this.num = num;
}
/**
* 设置页数和偏移
*
* @param pager
* @param offset
*/
public void setPagerAndOffset(int pager, float offset) {
this.pager = pager;
this.offset = offset;
//调用会使View测量、布局、重绘
requestLayout();
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
//设置选中点的位置
selectView.layout(
(int) (padding + (padding + 2 * radius) * (pager + offset)),
padding,
(int) (padding + (padding + 2 * radius) * (pager + 1 + offset)),
padding + radius * 2
);
}
/**
* 选中的指示器
*/
class SelectView extends View {
private Paint mPaint;
public SelectView(Context context) {
super(context);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(colorSelected);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(radius * 2, radius * 2);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
paint.setColor(colorSelected);
canvas.drawCircle(radius, radius, radius, mPaint);
}
}
}