自定义ViewPager的导航indecator(非常实用和主流)

原创 2016年06月02日 10:42:46

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

点击打开链接下载Demo

   









版权声明:本文为博主原创文章,未经博主允许不得转载。

Android-ViewPagerIndicator简单集成

现在很多的应用页面都是由一个个的TAB组成的,我们可以用布局加事件监听实现tab ,只是这样的控制非常麻烦,而且有很多的开源项目可以实现这样的功能,我们今天就介绍一下ViewPagerIndicato...
  • dalancon
  • dalancon
  • 2014年12月03日 14:08
  • 32296

python学习导航

File:文件API XML文件操作 文件操作集合String字符串操作 字符串操作集合 对两文件内字符串进行求或操作 Network网络操作 服务器...
  • sauphy
  • sauphy
  • 2016年03月10日 21:57
  • 344

ViewPager_自定义view_底部导航栏

  • 2016年08月16日 19:59
  • 7.43MB
  • 下载

自定义viewPager下面的导航按钮

要实现viewPager下面的导航按钮,为了方便,我们建立一个自定义view来复用,用起来也方便 首先要新建一个类NavImgLayout集成LinearLayout. 在构造方法里解析自定义属性...

android底部导航栏+viewPager+自定义view的简单实现

底部导航栏

Android自定义控件---导航栏SlideTab(Fragment+ViewPager)

一、前言好久没有更新过博客了,趁今天有空分享一个导航栏的自定义控件。有关此控件的demo相信在网上已经烂大街了,一搜一大把。 我现在只着重分享一些我认为比较难理解的知识点。整个控件的难点大概有三个 ...

自定义指示器 快速实现 ViewPager 滑动页卡切换(可用作整个 app上导航)

我记得在前面,我写了一篇Android 微信6.1 tab栏图标和字体颜色渐变的实现,如果大家仅仅认为这篇文章的功能只是模仿微信颜色渐变效果,那就大错特错了!认真阅读了这篇文章的朋友,应该知道,这里面...
  • pjbwan
  • pjbwan
  • 2015年04月27日 23:15
  • 301

Android多屏滑动:ViewPager自定义小圆圈标签 仿app初次运行时的导航

上文说了多屏滑动基础用法,遗留标签页的问题。纵观App的滑屏体验,标签页无非有两种:一种是每个View对应一个小圆圈,当前View对应的小圆圈颜色和其他不一样。但是所有View的标签在同一时刻都是可见...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:自定义ViewPager的导航indecator(非常实用和主流)
举报原因:
原因补充:

(最多只允许输入30个字)