自定义ViewPager的导航indecator(非常实用和主流)
现在很多App的欢迎页或者主页的轮播下面都有indicator(就是那个随着viewpager滚动而跟着滚动的小圆点);然后很多显示效果基本就是放一个选中的图片和一个未选择的图片,让这两个图片不断的轮换,这个效果都是烂大街了。而有一种效果就是那个选中的小圆点是随着viewpager的滑动而滑动有明显的动画效果,我就琢磨着怎样做这样一个效果,果不起然,功夫不负有心人,终于弄出来了,效果如下:
先制作一个草稿图如下:
要考虑的技术点:
1、小圆点的个数怎么确定;
2、小圆点的背景色、圆半径和他们之间的间距margin可不可以自定义;
3、小圆点的摆放位置;
4、小圆点对象的创建;
5、被选中的小原点是怎样在图层上面滑动显示的;
想到了问题,那么就开始着手找解决方法,看看没有有不会的,就要查查资料。想想总是有办法的,下面就是个人的一些解决办法:
1、小圆点个数要与ViewPager的内容个数相同,用viewPager.getAdapter().getCount()获取个数;
2、小圆点的背景色,和半径,margin可以使用自定义属性来设置;
3、小圆点的位置可以使用自定义view中的layout方法获取父容器宽度 - 小圆点总宽度,这就水平居中了,然后取容器高度的一半-半径就是小圆点y开始的位置;
4、小圆点要动态的保存它的宽度,绘制背景色,X,Y坐标等信息,这个时候就要开始选好对象,采用ShapeDrawable;
5、最重要的一点就是,小圆点要随着Viewpager移动,它是绘制在图层上面的,这个时候就要设置好Paint的属性,非常关键,如下:
//在目标图片顶部绘制源图像,从命名上也可以看出来就是把源图像绘制在上方,把选中的小原点绘制在上面 paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));
上面分析完成后,那么我们就开始动手了:
1、创建自定义属性:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="CircleIndicator">
<attr name="indicator_radio" format="dimension" />
<attr name="indicator_margin" format="dimension" />
<attr name="indicator_background" format="color|integer" />
<attr name="indicator_selected_background" format="color|integer" />
</declare-styleable>
</resources>
2、在main_activity.xml使用自定义View
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:indicator="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.world.hello.circleindicator.MainActivity">
<android.support.v4.view.ViewPager
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.world.hello.circleindicator.CircleIndicator
android:id="@+id/pager_indicator"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_alignParentBottom="true"
android:layout_marginBottom="40dp"
indicator:indicator_background="@android:color/white"
indicator:indicator_margin="20dp"
indicator:indicator_radio="10dp"
indicator:indicator_selected_background="@android:color/holo_red_light" />
</RelativeLayout>
3、MainActivity.class,为ViewPager配置几张图片
package com.world.hello.circleindicator;
import android.os.Bundle;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
private ViewPager mViewPager;
//这里为ViewPager模拟5张图片
private int[] mImags = new int[]{
R.drawable.img1,
R.drawable.img2,
R.drawable.img3,
R.drawable.img4,
R.drawable.img5};
private ArrayList<ImageView> mImageViews = new ArrayList<ImageView>();
//Viewpager小圆点导航
private CircleIndicator mIndicator;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mViewPager = (ViewPager) findViewById(R.id.view_pager);
for (int i = 0; i < mImags.length; i++) {
ImageView imageView = new ImageView(MainActivity.this);
imageView.setImageResource(mImags[i]);
imageView.setScaleType(ImageView.ScaleType.FIT_XY);
mImageViews.add(imageView);
}
mViewPager.setAdapter(pagerAdapter);
mIndicator = (CircleIndicator) findViewById(R.id.pager_indicator);
//将indicator和ViewPager绑定起来,实现联动效果
mIndicator.setViewPager(mViewPager);
}
PagerAdapter pagerAdapter = new PagerAdapter() {
@Override
public int getCount() {
return mImags.length;
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView(mImageViews.get(position));
}
@Override
public CharSequence getPageTitle(int position) {
return "null";
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
container.addView(mImageViews.get(position));
return mImageViews.get(position);
}
};
}
4、创建小原点自定义对象CircleShape
package com.world.hello.circleindicator;
import android.graphics.Paint;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.Shape;
/**
* 小圆点类
* Created by chengguo on 2016/6/1.
*/
public class CircleShape {
//设置默认值
private float x = 0;
private float y = 0;
private ShapeDrawable shape;
private Paint paint;
public CircleShape(ShapeDrawable shape) {
this.shape = shape;
}
public void setPaint(Paint value) {
paint = value;
}
public Paint getPaint() {
return paint;
}
public void setX(float value) {
x = value;
}
public float getX() {
return x;
}
public void setY(float value) {
y = value;
}
public float getY() {
return y;
}
public void setShape(ShapeDrawable value) {
shape = value;
}
public ShapeDrawable getShape() {
return shape;
}
public float getWidth() {
return shape.getShape().getWidth();
}
public void setWidth(float width) {
Shape s = shape.getShape();
s.resize(width, s.getHeight());
}
public float getHeight() {
return shape.getShape().getHeight();
}
public void setHeight(float height) {
Shape s = shape.getShape();
s.resize(s.getWidth(), height);
}
public void resizeShape(final float width, final float height) {
shape.getShape().resize(width, height);
}
}
5、自定义CirCleIndicator 实现类
package com.world.hello.circleindicator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.OvalShape;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.View;
import java.util.ArrayList;
import java.util.List;
/**
* 自定义indicator类
* Created by chengguo on 2016/6/1.
*/
public class CircleIndicator extends View {
//接收从activity传过来的ViewPager,实现联动
private ViewPager mViewPager;
//当前小圆点的对象
private CircleShape mSelectIndicator;
//所有小原点对象集合
private List<CircleShape> mIndicatorLists = new ArrayList<CircleShape>();
//小圆点的圆半径
private float mIndicatorRadius;
//小圆点之间的间隔
private float mIndicatorMargin;
//小圆点的背景
private int mIndicatorBackground;
//选中小圆点的背景
private int mIndicatorSelectedBackground;
//viewpager当前的位置
private int mCurrentPosition = 0;//默认为0
//ViewPager当前位置的偏移量
private float mCurrentPositionOffset = 0;
//下面是一些自定义属性的默认值
private final int DEFAULT_RADIUS = 10; //默认半径
private final int DEFAULT_MARGIN = 50; //默认间距
private final int DEFAULT_BACKGROUND = Color.WHITE; //默认颜色
private final int DEFAULT_SELECTED_BACKGROUND = Color.YELLOW; //默认选中颜色
public CircleIndicator(Context context) {
super(context, null);
}
public CircleIndicator(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public CircleIndicator(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
/**
* 初始化属性
*
* @param context
* @param attrs
*/
private void init(Context context, AttributeSet attrs) {
if (attrs == null) {
return;
}
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.CircleIndicator);
mIndicatorRadius = ta.getDimensionPixelSize(R.styleable.CircleIndicator_indicator_radio, DEFAULT_RADIUS);
mIndicatorMargin = ta.getDimensionPixelSize(R.styleable.CircleIndicator_indicator_margin, DEFAULT_MARGIN);
mIndicatorBackground = ta.getColor(R.styleable.CircleIndicator_indicator_background, DEFAULT_BACKGROUND);
mIndicatorSelectedBackground = ta.getColor(R.styleable.CircleIndicator_indicator_selected_background, DEFAULT_SELECTED_BACKGROUND);
//回收资源
ta.recycle();
}
/**
* 从activity把ViewPager传递进来,实现ViewPager和Indicator联动
*
* @param viewPager
*/
public void setViewPager(final ViewPager viewPager) {
mViewPager = viewPager;
createIndicators();
createSelectIndicator();
setUpdateChangeListener();
}
/**
* 监听ViewPager的改变,实现小圆点与ViewPager联动
*/
private void setUpdateChangeListener() {
mViewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels);
mCurrentPosition = position;
mCurrentPositionOffset = positionOffset;
//强制从新布局
requestLayout();
//重新绘制
invalidate();
}
});
}
/**
* 创建选择的小原点,就是随着viewPager移动而移动的小圆点
*/
private void createSelectIndicator() {
OvalShape circle = new OvalShape();
ShapeDrawable drawable = new ShapeDrawable(circle);
mSelectIndicator = new CircleShape(drawable);
Paint paint = drawable.getPaint();
paint.setColor(mIndicatorSelectedBackground);
paint.setAntiAlias(true);
//在目标图片顶部绘制源图像,从命名上也可以看出来就是把源图像绘制在上方,把选中的小原点绘制在上面
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));
mSelectIndicator.setPaint(paint);
}
/**
* 创建与ViewPager个数相同的导航小圆点
*/
private void createIndicators() {
for (int i = 0; i < mViewPager.getAdapter().getCount(); i++) {
//用圆形Shape创建小圆点对象
OvalShape circle = new OvalShape();
ShapeDrawable drawable = new ShapeDrawable(circle);
CircleShape circleShape = new CircleShape(drawable);
Paint paint = drawable.getPaint();
paint.setColor(mIndicatorBackground);
paint.setAntiAlias(true);
circleShape.setPaint(paint);
mIndicatorLists.add(circleShape);
}
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
layoutIndicatorLists(getWidth(), getHeight());
layoutSelectIndicator(mCurrentPosition, mCurrentPositionOffset);
}
/**
* 放置小圆点
*
* @param containerWidth
* @param containerHeight
*/
private void layoutIndicatorLists(int containerWidth, int containerHeight) {
if (mIndicatorLists == null) {
return;
}
//容器的水平中间线
float yCoordinate = containerHeight * 0.5f;
float startPosition = startDrawPosition(containerWidth);
for (int i = 0; i < mIndicatorLists.size(); i++) {
CircleShape item = mIndicatorLists.get(i);
item.resizeShape(2 * mIndicatorRadius, 2 * mIndicatorRadius);
//每个小原点的左上角Y位置
item.setY(yCoordinate - mIndicatorRadius);
//每个小圆点X开始位置
float x = startPosition + (mIndicatorMargin + mIndicatorRadius * 2) * i;
item.setX(x);
}
}
/**
* 获取总体小原点的开始位置
*
* @param containerWidth
* @return
*/
private float startDrawPosition(int containerWidth) {
float tabItemsLength = mIndicatorLists.size() * (mIndicatorMargin + 2 * mIndicatorRadius) - mIndicatorMargin;
if (containerWidth < tabItemsLength) {
return 0;
}
//水平居中显示
return (containerWidth - tabItemsLength) / 2;
}
/**
* 放置滚动的小原点
*
* @param position
* @param positionOffset
*/
private void layoutSelectIndicator(int position, float positionOffset) {
if (mSelectIndicator == null) {
return;
}
if (mIndicatorLists.size() == 0) {
return;
}
CircleShape item = mIndicatorLists.get(position);
mSelectIndicator.resizeShape(item.getWidth(), item.getHeight());
//设置滚动的小圆点的X位置偏移量
float x = item.getX() + (mIndicatorMargin + mIndicatorRadius * 2) * positionOffset;
mSelectIndicator.setX(x);
mSelectIndicator.setY(item.getY());
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mIndicatorLists.size() == 0 || mSelectIndicator == null) {
return;
}
int sc = canvas.saveLayer(0, 0, getWidth(), getHeight(), null,
Canvas.MATRIX_SAVE_FLAG |
Canvas.CLIP_SAVE_FLAG |
Canvas.HAS_ALPHA_LAYER_SAVE_FLAG |
Canvas.FULL_COLOR_LAYER_SAVE_FLAG |
Canvas.CLIP_TO_LAYER_SAVE_FLAG);
for (CircleShape item : mIndicatorLists) {
drawItem(canvas, item);
}
drawItem(canvas, mSelectIndicator);
canvas.restoreToCount(sc);
}
/**
* 绘制小圆点
*
* @param canvas
* @param indicator
*/
private void drawItem(Canvas canvas, CircleShape indicator) {
canvas.save();
canvas.translate(indicator.getX(), indicator.getY());
indicator.getShape().draw(canvas);
canvas.restore();
}
}
上面代码的注释讲解的非常清楚了,大家可以学习下。下面给出源码demo,大家可以下载用到自己的工程中,非常的实用: