自定义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,大家可以下载用到自己的工程中,非常的实用: