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

自定义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

   

时间: 2024-10-09 18:13:11

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

Android 自定义 ViewPager 打造千变万化的图片切换效果

Android 自定义 ViewPager 打造千变万化的图片切换效果 标签: Android自定义ViewPagerJazzyViewPager 目录(?)[+] 转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38026503 记 得第一次见到ViewPager这个控件,瞬间爱不释手,做东西的主界面通通ViewPager,以及图片切换也抛弃了ImageSwitch之类的,开 始让ViewPager来做.时间长了,ViewPa

三行代码接入,社交软件打字时底下弹出的表情布局,自定义ViewPager+页面点标+各种功能的android小框架。

(转载请声明出处:http://www.cnblogs.com/linguanh/) 前言: 接上次分享的 ListView 动态加载类,入口:http://www.cnblogs.com/linguanh/p/4645115.html  这次分享给大家的是,刚些写好的类似社交软件打字时地下弹出的表情布局. 先看下我的默认效果图. 该效果图里面使用的图片资源,是默认的IC_lanucher,在我的类里面,你可以自定义,包括布局,几行几列,什么的,都可以自定义.底下的是小点标. 下集预告:我将在使

【转】android 自定义ViewPager,修改原动画

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38026503 记 得第一次见到ViewPager这个控件,瞬间爱不释手,做东西的主界面通通ViewPager,以及图片切换也抛弃了ImageSwitch之类的,开 始让ViewPager来做.时间长了,ViewPager的切换效果觉得枯燥,形成了审美疲劳~~我们需要改变,今天教大家如何改变ViewPager 切换时的效果,实现个性化的图片切换~~ 看一下这样效果的图片切换: 是

Android为ViewPager增加切换动画——自定义ViewPager

转载请注明出处:http://blog.csdn.net/allen315410/article/details/44224517 在上篇博客中,我写了一个使用属性动画为ViewPager添加切换动画的方法,并且可以兼容到Android3.0以下版本的设备上,那么关于为ViewPager添加动画的方式还会有另外一种实现方案,就是自定义一个自己带动画效果的ViewPager,关于上篇博客,还没来得及查看的朋友可以点击这里进行查看.下面,我们将新建一个工程,来说说怎样自定义一个自带切换动画效果的Vi

android 自定义状态栏和导航栏分析与实现

效果 android 4.4之后,系统是支持自定义状态栏和导航栏的,举个最典型的例子就是bilibili客户端了(iOS版本和android版本能用两套完全不一样符合各自系统的设计ui,良心啊-),顶部状态栏为粉色,底部导航栏为半透明色: 接着QQ最新的版本6.2也使用了状态栏透明风格,但是出来的效果在不同版本,不同手机上,显示的效果真是差异很大(4.3版本是无法使用状态栏透明风格的,只是放出来做个对比): ------------------------------------ -------

Android实现图片轮显效果——自定义ViewPager控件

一.问题概述 使用ViewPager控件实现可横向翻页.水平切换图片等效果,但ViewPager需要手动滑动才能切换页面,图片轮显效果的效果本质上就是在ViewPager控件的基础上让它能自动的进行切换,所以实现图片轮显步骤如下: 1.  扩展ViewPager自定义一个MyScrollImageView类 2.  为MyScrollImageView定义适配器,装载图片信息 3.  定义图片滑动动画时间控制类 接下来我们就一步步实现下图案例: 二.实现套路 1.为自定义ViewPager控件编

千变万化的ViewPager切换动画(2)--自定义ViewPager的实现方法

(1)创建项目Viewpager_anim_myself 都采用默认的方式,添加三张图片资源文件,src-包目录下创建MyViewPagerTransformerAnim.java类 布局文件activity_main.xml如下: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools

Viewpager + 小点点导航的具体实现

Viewpager + 小点点导航的具体实现 Viewpager来实现广告展示效果肯定是不错的,配上小点点的分页导航肯定是标配,那么现在就来说一说具体是怎么样来实现的呢? 我们先看看效果图: 写完成之后觉得是非常简单的,实现的方式肯定也有很多种,那么我使用的其实是viewpager + viewgroup + view即可.Viewpager负责广告滑动,而viewgroup+ view则是负责下面的小点点导航. 一.首先看他的布局文件 我们先看看布局: <RelativeLayout xmln

ViewPager实现导航页

我们在首次安装app时,往往会发现会有导航页. 导航页一般用于介绍功能和引导使用,那我们其实可以用ViewPager实现. ViewPager用于实现多页面的滑动切换效果,使用时需要引入"android.support.v4"包. 好了,我们现在开始就来做一个简单的导航页引导. 首先我们新建一个Android Application Project,我们把自动生成的MainActivity作为应用程序的主界面: 其布局文件我们简单设置下: activity_main.xml: <