android 循环轮播控件com.youth.banner 的自定义轮播指示器,满足开发需求。

1:轮播com.youth.banner

        轮播图控件com.youth.banner,github地址:https://github.com/youth5201314/banner。

        具体使用请看github,简单方便,咱们主要介绍如何自定义指示器。

2:自定义指示器

       1:自定义属性

              在values.xml添加属性

<declare-styleable name="ViewPagerIndicator">
    <attr format="color|reference" name="sbl_selected_color"/>
    <attr format="color|reference" name="sbl_default_color"/>
    <attr format="dimension|reference" name="sbl_radius"/>
    <attr format="dimension|reference" name="sbl_radius_selected"/>
    <attr format="dimension|reference" name="sbl_length"/>
    <attr format="dimension|reference" name="sbl_distance"/>
    <attr format="integer" name="sbl_num"/>
    <attr name="sbl_indicatorType">
        <enum name="LINE" value="0"/>
        <enum name="CIRCLE" value="1"/>
        <enum name="CIRCLE_LINE" value="2"/>
        <enum name="BEZIER" value="3"/>
        <enum name="SPRING" value="4"/>
        <enum name="PROGRESS" value="5"/>
    </attr>
    <attr name="sbl_distanceType">
        <enum name="BY_RADIUS" value="0"/>
        <enum name="BY_DISTANCE" value="1"/>
        <enum name="BY_LAYOUT" value="2"/>
    </attr>
    <attr format="boolean" name="sbl_animation"/>
</declare-styleable>

2:自定义view

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;

import androidx.viewpager.widget.ViewPager;
import androidx.viewpager2.widget.ViewPager2;

import com.sbl.hwscankitdemo.R;

/**
 * 自定义轮播
 */
public class MyViewPagerIndicator extends View {

    private Path mPath;
    private Paint paintFill;
    private Paint paintStroke;
    private int mNum;//个数
    private float mRadius;//半径
    private float mRadiusSelected;//选中半径,默认为mRadius
    private float mLength;//线长
    private float mHeight;//线宽
    private float mOffset;//偏移量
    private int mSelected_color;//选中颜色
    private int mDefault_color;//默认颜色
    private int mIndicatorType;//点类型
    private int mDistanceType;//距离类型
    private float mDistance;//间隔距离
    private int mPosition;//第几张
    /**
     * 一个常量,用来计算绘制圆形贝塞尔曲线控制点的位置
     */
    private static final float M = 0.551915024494f;
    private float mPercent;
    private boolean mIsLeft;
    private boolean mIsInfiniteCircle;//无限循环
    private boolean mAnimation;

    public MyViewPagerIndicator(Context context, AttributeSet attrs) {
        super(context, attrs);
        setStyleable(context, attrs);
        initPaint();
    }

    /**
     * xml 参数设置  选中颜色 默认颜色  点大小 长度 距离 距离类型 类型 真实个数(轮播)
     *
     * @param context
     * @param attrs
     */
    private void setStyleable(Context context, AttributeSet attrs) {
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ViewPagerIndicator);
        mSelected_color = array.getColor(R.styleable.ViewPagerIndicator_sbl_selected_color, 0xffffffff);
        mDefault_color = array.getColor(R.styleable.ViewPagerIndicator_sbl_default_color, 0xffcdcdcd);
        mRadius = array.getDimension(R.styleable.ViewPagerIndicator_sbl_radius, 20);//px
        mRadiusSelected = array.getDimension(R.styleable.ViewPagerIndicator_sbl_radius_selected, mRadius);//px
        mLength = array.getDimension(R.styleable.ViewPagerIndicator_sbl_length, 2 * mRadius);//px
        mDistance = array.getDimension(R.styleable.ViewPagerIndicator_sbl_distance, 3 * mRadius);//px
        mDistanceType = array.getInteger(R.styleable.ViewPagerIndicator_sbl_distanceType, MyViewPagerIndicator.DistanceType.BY_RADIUS);
        mIndicatorType = array.getInteger(R.styleable.ViewPagerIndicator_sbl_indicatorType, MyViewPagerIndicator.IndicatorType.CIRCLE);
        mNum = array.getInteger(R.styleable.ViewPagerIndicator_sbl_num, 0);
        mAnimation = array.getBoolean(R.styleable.ViewPagerIndicator_sbl_animation, true);
        array.recycle();
        switch (mIndicatorType) {
            case MyViewPagerIndicator.IndicatorType.BEZIER:
                mControlPoint = new MyViewPagerIndicator.Point[]{new MyViewPagerIndicator.Point(), new MyViewPagerIndicator.Point(), new MyViewPagerIndicator.Point(), new MyViewPagerIndicator.Point(), new MyViewPagerIndicator.Point(), new MyViewPagerIndicator.Point(),
                        new MyViewPagerIndicator.Point(), new MyViewPagerIndicator.Point(), new MyViewPagerIndicator.Point(), new MyViewPagerIndicator.Point(), new MyViewPagerIndicator.Point(), new MyViewPagerIndicator.Point()};
                break;
            case MyViewPagerIndicator.IndicatorType.SPRING:
                mSpringPoint = new MyViewPagerIndicator.Point[]{new MyViewPagerIndicator.Point(), new MyViewPagerIndicator.Point(), new MyViewPagerIndicator.Point(), new MyViewPagerIndicator.Point(), new MyViewPagerIndicator.Point(), new MyViewPagerIndicator.Point()};
                break;
        }
        invalidate();
    }

    /**
     * 初始化画笔
     */
    private void initPaint() {
        paintStroke = new Paint();
        paintFill = new Paint();
        mPath = new Path();
        //实心
        paintFill.setStyle(Paint.Style.FILL_AND_STROKE);
        paintFill.setColor(mSelected_color);
        paintFill.setAntiAlias(true);
        paintFill.setStrokeWidth(3);
        //空心
        paintStroke.setStyle(Paint.Style.FILL);
        paintStroke.setColor(mDefault_color);
        paintStroke.setAntiAlias(true);
        paintStroke.setStrokeWidth(3);
    }

    /**
     * 绘制   invalidate()后 执行
     *
     * @param canvas
     */
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if ((mNum <= 0)) {
            return;
        }
        int width = canvas.getWidth();
        int height = canvas.getHeight();
        canvas.translate(width / 2, height / 2);
        //初始化画笔
        initPaint();
        //距离
        switch (mDistanceType) {
            case MyViewPagerIndicator.DistanceType.BY_DISTANCE:

                break;
            case MyViewPagerIndicator.DistanceType.BY_RADIUS://圆心到 3倍半径 只有一个半径
                mDistance = 3 * mRadius;
                break;
            case MyViewPagerIndicator.DistanceType.BY_LAYOUT://布局等分
                if (mIndicatorType == MyViewPagerIndicator.IndicatorType.CIRCLE_LINE) {
                    mDistance = width / (mNum + 1);
                } else {
                    mDistance = width / mNum;
                }
                break;
        }
        switch (mIndicatorType) {
            case MyViewPagerIndicator.IndicatorType.CIRCLE://圆
                for (int i = 0; i < mNum; i++) {//默认点 -(mNum - 1) * 0.5f * mDistance 第一个点
                    canvas.drawCircle(-(mNum - 1) * 0.5f * mDistance + i * mDistance, 0, mRadius, paintStroke);
                }
                //选中
                canvas.drawCircle(-(mNum - 1) * 0.5f * mDistance + mOffset, 0, mRadiusSelected, paintFill);
                break;
            case MyViewPagerIndicator.IndicatorType.LINE://线
                paintStroke.setStrokeWidth(mRadius);
                float startX = -(mNum - 1) * 0.5f * mDistance - mLength / 2;
                float stopX = -(mNum - 1) * 0.5f * mDistance + mLength / 2;
                //默认
                for (int i = 0; i < mNum; i++) {
                    canvas.drawLine(startX + i * mDistance, 0, stopX + i * mDistance, 0, paintStroke);
                }
                //选中
                paintFill.setStrokeWidth(mRadius);
                float startF = -(mNum - 1) * 0.5f * mDistance - mLength / 2 + mOffset;
                float stopF = -(mNum - 1) * 0.5f * mDistance + mLength / 2 + mOffset;
                canvas.drawLine(startF, 0, stopF, 0, paintFill);

                break;
            case MyViewPagerIndicator.IndicatorType.CIRCLE_LINE://圆线

                if (mPosition == mNum - 1) {//最后一个 右滑
                    //第一个 线 选中 消失
                    float leftClose = -(mNum) * 0.5f * mDistance - mRadius;
                    float rightClose = leftClose + 2 * mRadius + mOffset;
                    float topClose = -mRadius;
                    float bottomClose = mRadius;
                    RectF rectClose = new RectF(leftClose, topClose, rightClose, bottomClose);// 设置个新的长方形
                    canvas.drawRoundRect(rectClose, mRadius, mRadius, paintStroke);
                    //最后一个 线  显示
                    float rightOpen = -(mNum) * 0.5f * mDistance + mNum * mDistance + mRadius;
                    float leftOpen = rightOpen - 2 * mRadius - mDistance + mOffset;
                    float topOpen = -mRadius;
                    float bottomOpen = mRadius;
                    RectF rectOpen = new RectF(leftOpen, topOpen, rightOpen, bottomOpen);// 设置个新的长方形
                    canvas.drawRoundRect(rectOpen, mRadius, mRadius, paintStroke);
                    //圆
                    for (int i = 1; i < mNum; i++) {
                        canvas.drawCircle(rightClose - mRadius + i * mDistance, 0, mRadius, paintStroke);
                    }

                } else {
                    //第一个 线 选中 消失
                    float leftClose = -(mNum) * 0.5f * mDistance + mPosition * mDistance - mRadius;
                    float rightClose = leftClose + 2 * mRadius + mDistance - mOffset;
                    float topClose = -mRadius;
                    float bottomClose = mRadius;
                    RectF rectClose = new RectF(leftClose, topClose, rightClose, bottomClose);// 设置个新的长方形
                    canvas.drawRoundRect(rectClose, mRadius, mRadius, paintStroke);
                    //第二个 线  显示
                    if (mPosition < mNum - 1) {
                        float rightOpen = -(mNum) * 0.5f * mDistance + (mPosition + 2) * mDistance + mRadius;
                        float leftOpen = rightOpen - 2 * mRadius - mOffset;
                        float topOpen = -mRadius;
                        float bottomOpen = mRadius;
                        RectF rectOpen = new RectF(leftOpen, topOpen, rightOpen, bottomOpen);// 设置个新的长方形
                        canvas.drawRoundRect(rectOpen, mRadius, mRadius, paintStroke);
                    }
                    //圆
                    for (int i = mPosition + 3; i <= mNum; i++) {
                        canvas.drawCircle(-(mNum) * 0.5f * mDistance + i * mDistance, 0, mRadius, paintStroke);
                    }
                    for (int i = mPosition - 1; i >= 0; i--) {
                        canvas.drawCircle(-(mNum) * 0.5f * mDistance + i * mDistance, 0, mRadius, paintStroke);
                    }
                }
                break;
            case MyViewPagerIndicator.IndicatorType.BEZIER://贝塞尔
                for (int i = 0; i < mNum; i++) {//默认点 -(mNum - 1) * 0.5f * mDistance 第一个点
                    canvas.drawCircle(-(mNum - 1) * 0.5f * mDistance + i * mDistance, 0, mRadius, paintStroke);
                }
                //选中
                drawCubicBezier(canvas);
                break;
            case MyViewPagerIndicator.IndicatorType.SPRING://贝塞尔 弹性
                for (int i = 0; i < mNum; i++) {//默认点 -(mNum - 1) * 0.5f * mDistance 第一个点
                    canvas.drawCircle(-(mNum - 1) * 0.5f * mDistance + i * mDistance, 0, mRadius, paintStroke);
                }
                drawSpringBezier(canvas);
                break;
            case MyViewPagerIndicator.IndicatorType.PROGRESS://进度条
                for (int i = 0; i < mNum; i++) {//默认点 -(mNum - 1) * 0.5f * mDistance 第一个点
                    canvas.drawCircle(-(mNum - 1) * 0.5f * mDistance + i * mDistance, 0, mRadius, paintStroke);
                }
                //选中
                float rightOpen = -(mNum - 1) * 0.5f * mDistance + mOffset + mRadius;
                float leftOpen = -(mNum - 1) * 0.5f * mDistance -   mRadius;
                float topOpen = -mRadius;
                float bottomOpen = mRadius;
                RectF rectOpen = new RectF(leftOpen, topOpen, rightOpen, bottomOpen);// 设置个新的长方形
                canvas.drawRoundRect(rectOpen, mRadius, mRadius, paintFill);
                break;
        }
    }

    private MyViewPagerIndicator.Point[] mSpringPoint = new MyViewPagerIndicator.Point[6];

    /**
     * 绘制弹性
     *
     * @param canvas
     */
    private void drawSpringBezier(Canvas canvas) {
        //右圆圆心
        float right_circle_x;
        //右圆半径
        float right_circle_radius;
        //左圆圆心
        float left_circle_x;
        //左圆半径
        float left_circle_radius;
        //最大半径
        float max_radius = mRadius;
        //最小半径
        float min_radius = mRadius / 2;
        //控制点
        if (mPosition == mNum - 1 && !mIsLeft) {//第一个 右滑  0---4
            if (mPercent <= 0.5) {
                right_circle_x = -(mNum - 1) * 0.5f * mDistance + (mNum - 1) * mDistance;
                left_circle_x = -(mNum - 1) * 0.5f * mDistance + (0.5f - mPercent) / 0.5f * (mNum - 1) * mDistance;
                right_circle_radius = min_radius + (max_radius - min_radius) * (0.5f - mPercent) / 0.5f;
            } else {
                right_circle_x = -(mNum - 1) * 0.5f * mDistance + (1f - mPercent) / 0.5f * (mNum - 1) * mDistance;
                left_circle_x = -(mNum - 1) * 0.5f * mDistance;
                right_circle_radius = min_radius;
            }
            left_circle_radius = mRadius * (mPercent);
        } else if (mPosition == mNum - 1 && mIsLeft) {//最后一个 左滑 4--0
            //0-1
            if (mPercent >= 0.5) {//左亭
                left_circle_radius = min_radius + (max_radius - min_radius) * (-0.5f + mPercent) / 0.5f;
                left_circle_x = -(mNum - 1) * 0.5f * mDistance;
                right_circle_x = -(mNum - 1) * 0.5f * mDistance + (1 - mPercent) / 0.5f * (mNum - 1) * mDistance;
            } else {//左动
                left_circle_radius = min_radius;
                left_circle_x = -(mNum - 1) * 0.5f * mDistance + (0.5f - mPercent) / 0.5f * (mNum - 1) * mDistance;
                right_circle_x = -(mNum - 1) * 0.5f * mDistance + (mNum - 1) * mDistance;
            }
            right_circle_radius = mRadius * (1 - mPercent);
        } else if (mIsLeft) {//中间的 左滑
            mOffset = (mPercent + mPosition) * mDistance;
            if (mPercent >= 0.5) {
                left_circle_x = -(mNum - 1) * 0.5f * mDistance + ((mPercent - 0.5f) / 0.5f + mPosition) * mDistance;
                right_circle_x = -(mNum - 1) * 0.5f * mDistance + (1 + mPosition) * mDistance;
                right_circle_radius = min_radius + (max_radius - min_radius) * (mPercent - 0.5f) / 0.5f;
            } else {
                right_circle_x = -(mNum - 1) * 0.5f * mDistance + ((mPercent) / 0.5f + mPosition) * mDistance;
                left_circle_x = -(mNum - 1) * 0.5f * mDistance + mPosition * mDistance;
                right_circle_radius = min_radius;
            }
            left_circle_radius = mRadius * (1 - mPercent);
        } else {//右滑
            mOffset = (mPercent + mPosition) * mDistance;
            if (mPercent <= 0.5) {
                left_circle_x = -(mNum - 1) * 0.5f * mDistance + (mPosition) * mDistance;
                right_circle_x = -(mNum - 1) * 0.5f * mDistance + ((mPercent) / 0.5f + mPosition) * mDistance;
                left_circle_radius = min_radius + (max_radius - min_radius) * (0.5f - mPercent) / 0.5f;
            } else {
                left_circle_x = -(mNum - 1) * 0.5f * mDistance + ((mPercent - 0.5f) / 0.5f + mPosition) * mDistance;
                right_circle_x = -(mNum - 1) * 0.5f * mDistance + (mPosition + 1) * mDistance;
                left_circle_radius = min_radius;
            }
            right_circle_radius = mRadius * (mPercent);
        }
        //右圆
        canvas.drawCircle(right_circle_x, 0, right_circle_radius, paintFill);
        //左圆
        canvas.drawCircle(left_circle_x, 0, left_circle_radius, paintFill);
        //贝塞尔
        //控制点
        mSpringPoint[0].x = left_circle_x;
        mSpringPoint[0].y = -left_circle_radius;
        mSpringPoint[5].x = mSpringPoint[0].x;
        mSpringPoint[5].y = left_circle_radius;
        //
        mSpringPoint[1].x = (left_circle_x + right_circle_x) / 2;
        mSpringPoint[1].y = -left_circle_radius / 2;
        mSpringPoint[4].x = mSpringPoint[1].x;
        mSpringPoint[4].y = left_circle_radius / 2;
        //
        mSpringPoint[2].x = right_circle_x;
        mSpringPoint[2].y = -right_circle_radius;
        mSpringPoint[3].x = mSpringPoint[2].x;
        mSpringPoint[3].y = right_circle_radius;

        mPath.reset();
        mPath.moveTo(mSpringPoint[0].x, mSpringPoint[0].y);
        mPath.quadTo(mSpringPoint[1].x, mSpringPoint[1].y, mSpringPoint[2].x, mSpringPoint[2].y);
        mPath.lineTo(mSpringPoint[3].x, mSpringPoint[3].y);
        mPath.quadTo(mSpringPoint[4].x, mSpringPoint[4].y, mSpringPoint[5].x, mSpringPoint[5].y);
        canvas.drawPath(mPath, paintFill);
    }

    /**
     * 绘制贝塞尔曲线
     *
     * @param canvas
     */
    private void drawCubicBezier(Canvas canvas) {
        //更换控制点
        changePoint();

        /** 清除Path中的内容
         reset不保留内部数据结构,但会保留FillType.
         rewind会保留内部的数据结构,但不保留FillType */
        mPath.reset();

        //0
        mPath.moveTo(mControlPoint[0].x, mControlPoint[0].y);
        //0-3
        mPath.cubicTo(mControlPoint[1].x, mControlPoint[1].y, mControlPoint[2].x, mControlPoint[2].y, mControlPoint[3].x, mControlPoint[3].y);
        //3-6
        mPath.cubicTo(mControlPoint[4].x, mControlPoint[4].y, mControlPoint[5].x, mControlPoint[5].y, mControlPoint[6].x, mControlPoint[6].y);
        //6-9
        mPath.cubicTo(mControlPoint[7].x, mControlPoint[7].y, mControlPoint[8].x, mControlPoint[8].y, mControlPoint[9].x, mControlPoint[9].y);
        //9-0
        mPath.cubicTo(mControlPoint[10].x, mControlPoint[10].y, mControlPoint[11].x, mControlPoint[11].y, mControlPoint[0].x, mControlPoint[0].y);

        canvas.drawPath(mPath, paintFill);
    }

    /**
     * 控制点
     */
    private void changePoint() {
        mCenterPoint.y = 0;
        float mc = M;
        mControlPoint[2].y = mRadius;//底部
        mControlPoint[8].y = -mRadius;//顶部

        //圆心位置
        if (mPosition == mNum - 1 && !mIsLeft) {//第一个 右滑  0-->4

            if (mPercent <= 0.2) { //回弹 圆心到达
                mCenterPoint.x = -(mNum - 1) * 0.5f * mDistance + (mNum - 1) * mDistance;//最后一个
            } else if (mPercent <= 0.8) {//加速 左凸起 扁平化M 最右端固定不变  圆心移动
                mCenterPoint.x = -(mNum - 1) * 0.5f * mDistance + (1 - (mPercent - 0.2f) / 0.6f) * (mNum - 1) * mDistance;
            } else if (mPercent > 0.8 && mPercent < 1) {//
                mCenterPoint.x = -(mNum - 1) * 0.5f * mDistance;//第一个
            } else if (mPercent == 1) {//圆
                mCenterPoint.x = -(mNum - 1) * 0.5f * mDistance;
            }
            //控制点位置
            if (mPercent > 0.8 && mPercent <= 1) {//右凸起 圆心不变
                mControlPoint[5].x = mCenterPoint.x + mRadius * (2 - (mPercent - 0.8f) / 0.2f);//右半圆
                mControlPoint[0].x = mCenterPoint.x - mRadius;//左半圆
            } else if (mPercent > 0.5 && mPercent <= 0.8) {//加速 左凸起 扁平化M 最右端固定不变  圆心移动
                mControlPoint[5].x = mCenterPoint.x + 2 * mRadius;//右半圆
                mControlPoint[0].x = mCenterPoint.x - mRadius * (1 + (0.8f - mPercent) / 0.3f);//左半圆
                mControlPoint[2].y = mRadius * (1 + (mPercent - 0.8f) / 0.3f * 0.1f);//底部
                mControlPoint[8].y = -mRadius * (1 + (mPercent - 0.8f) / 0.3f * 0.1f);//顶部
                mc = mc * (1 + (-mPercent + 0.8f) / 0.3f * 0.3f);
            } else if (mPercent > 0.2 && mPercent <= 0.5) {//左右恢复 变圆M逐渐重置为原来大小  圆心移动
                mControlPoint[5].x = mCenterPoint.x + mRadius * (1 + (mPercent - 0.2f) / 0.3f);//右半圆
                mControlPoint[0].x = mCenterPoint.x - mRadius * (1 + (mPercent - 0.2f) / 0.3f);//左半圆
                mControlPoint[2].y = mRadius * (1 - (mPercent - 0.2f) / 0.3f * 0.1f);//底部
                mControlPoint[8].y = -mRadius * (1 - (mPercent - 0.2f) / 0.3f * 0.1f);//顶部
                mc = mc * (1 + (mPercent - 0.2f) / 0.3f * 0.3f);
            } else if (mPercent > 0.1 && mPercent <= 0.2) {//左凹 圆心到达.0
                mControlPoint[5].x = mCenterPoint.x + mRadius;//右半圆
                mControlPoint[0].x = mCenterPoint.x - mRadius * (1 - (0.2f - mPercent) / 0.1f * 0.5f);//左半圆
            } else if (mPercent >= 0 && mPercent <= 0.1) {//回弹 圆心到达
                mControlPoint[5].x = mCenterPoint.x + mRadius;//右半圆
                mControlPoint[0].x = mCenterPoint.x - mRadius * (1 - (mPercent) / 0.1f * 0.5f);//左半圆
            }

        } else if (mPosition == mNum - 1 && mIsLeft) {//最后一个 左滑  4-->0
            if (mPercent <= 0.2) {//圆
                mCenterPoint.x = -(mNum - 1) * 0.5f * mDistance + (mNum - 1) * mDistance;
            } else if (mPercent <= 0.8) {//加速 左凸起 扁平化M 最右端固定不变  圆心移动
                mCenterPoint.x = -(mNum - 1) * 0.5f * mDistance + (1 - (mPercent - 0.2f) / 0.6f) * (mNum - 1) * mDistance;
            } else if (mPercent > 0.8 && mPercent < 1) {//回弹 圆心到达
                mCenterPoint.x = -(mNum - 1) * 0.5f * mDistance;//第一个
            } else if (mPercent == 1) {//圆
                mCenterPoint.x = -(mNum - 1) * 0.5f * mDistance + mPosition * mDistance;
            }

            if (mPercent <= 0) {//圆

            } else if (mPercent <= 0.2 && mPercent >= 0) {//左凸起 圆心不变
                mControlPoint[5].x = mCenterPoint.x + mRadius;//右半圆
                mControlPoint[0].x = mCenterPoint.x - mRadius * (1 + (mPercent) / 0.2f);//左半圆
            } else if (mPercent > 0.2 && mPercent <= 0.5) {//加速 右凸起 扁平化M 最左端固定不变  圆心移动
                mControlPoint[5].x = mCenterPoint.x + mRadius * (1 + (mPercent - 0.2f) / 0.3f);//右半圆
                mControlPoint[0].x = mCenterPoint.x - 2 * mRadius;//左半圆
                mControlPoint[2].y = mRadius * (1 - (mPercent - 0.2f) / 0.3f * 0.1f);//底部
                mControlPoint[8].y = -mRadius * (1 - (mPercent - 0.2f) / 0.3f * 0.1f);//顶部
                mc = mc * (1 + (mPercent - 0.2f) / 0.3f * 0.3f);
            } else if (mPercent > 0.5 && mPercent <= 0.8) {//左右恢复 变圆M逐渐重置为原来大小  圆心移动
                mControlPoint[5].x = mCenterPoint.x + mRadius * (1 + (0.8f - mPercent) / 0.3f);//右半圆
                mControlPoint[0].x = mCenterPoint.x - mRadius * (1 + (0.8f - mPercent) / 0.3f);//左半圆
                mControlPoint[2].y = mRadius * (1 + (mPercent - 0.8f) / 0.3f * 0.1f);//底部
                mControlPoint[8].y = -mRadius * (1 + (mPercent - 0.8f) / 0.3f * 0.1f);//顶部
                mc = mc * (1 + (0.8f - mPercent) / 0.3f * 0.3f);
            } else if (mPercent > 0.8 && mPercent <= 0.9) {//右凹 圆心到达
                mControlPoint[5].x = mCenterPoint.x + mRadius * (1 - (mPercent - 0.8f) / 0.1f * 0.5f);//右半圆
                mControlPoint[0].x = mCenterPoint.x - mRadius;//左半圆
            } else if (mPercent > 0.9 && mPercent <= 1) {//回弹 圆心到达
                mControlPoint[5].x = mCenterPoint.x + mRadius * (1 - (mPercent - 0.9f) / 0.1f * 0.5f);//右半圆
                mControlPoint[0].x = mCenterPoint.x - mRadius;//左半圆
            }


        } else {
            if (mPercent <= 0.2) {//圆
                mCenterPoint.x = -(mNum - 1) * 0.5f * mDistance + mPosition * mDistance;
            } else if (mPercent <= 0.8) {//加速 左凸起 扁平化M 最右端固定不变  圆心移动
                mCenterPoint.x = -(mNum - 1) * 0.5f * mDistance + (mPosition + mPercent) * mDistance;
                mCenterPoint.x = -(mNum - 1) * 0.5f * mDistance + (mPosition + (mPercent - 0.2f) / 0.6f) * mDistance;
            } else if (mPercent > 0.8 && mPercent < 1) {//回弹 圆心到达
                mCenterPoint.x = -(mNum - 1) * 0.5f * mDistance + (mPosition + 1) * mDistance;
            } else if (mPercent == 1) {//圆
                mCenterPoint.x = -(mNum - 1) * 0.5f * mDistance + mPosition * mDistance;
            }
            //控制点位置
            if (mIsLeft)//左滑
            {
                if (mPercent >= 0 && mPercent <= 0.2) {//右凸起 圆心不变
                    mControlPoint[5].x = mCenterPoint.x + mRadius * (2 - (0.2f - mPercent) / 0.2f);//右半圆
                    mControlPoint[0].x = mCenterPoint.x - mRadius;//左半圆
                } else if (mPercent > 0.2 && mPercent <= 0.5) {//加速 左凸起 扁平化M 最右端固定不变  圆心移动
                    mControlPoint[5].x = mCenterPoint.x + 2 * mRadius;//右半圆
                    mControlPoint[0].x = mCenterPoint.x - mRadius * (1 + (mPercent - 0.2f) / 0.3f);//左半圆
                    mControlPoint[2].y = mRadius * (1 - (mPercent - 0.2f) / 0.3f * 0.1f);//底部
                    mControlPoint[8].y = -mRadius * (1 - (mPercent - 0.2f) / 0.3f * 0.1f);//顶部
                    mc = mc * (1 + (mPercent - 0.2f) / 0.3f * 0.3f);
                } else if (mPercent > 0.5 && mPercent <= 0.8) {//左右恢复 变圆M逐渐重置为原来大小  圆心移动
                    mControlPoint[5].x = mCenterPoint.x + mRadius * (1 + (0.8f - mPercent) / 0.3f);//右半圆
                    mControlPoint[0].x = mCenterPoint.x - mRadius * (1 + (0.8f - mPercent) / 0.3f);//左半圆
                    mControlPoint[2].y = mRadius * (1 + (mPercent - 0.8f) / 0.3f * 0.1f);//底部
                    mControlPoint[8].y = -mRadius * (1 + (mPercent - 0.8f) / 0.3f * 0.1f);//顶部
                    mc = mc * (1 + (-mPercent + 0.8f) / 0.3f * 0.3f);
                } else if (mPercent > 0.8 && mPercent <= 0.9) {//左凹 圆心到达
                    mControlPoint[5].x = mCenterPoint.x + mRadius;//右半圆
                    mControlPoint[0].x = mCenterPoint.x - mRadius * (1 - (mPercent - 0.8f) / 0.1f * 0.5f);//左半圆
                } else if (mPercent > 0.9 && mPercent <= 1) {//回弹 圆心到达
                    mControlPoint[5].x = mCenterPoint.x + mRadius;//右半圆
                    mControlPoint[0].x = mCenterPoint.x - mRadius * (1 - (1.0f - mPercent) / 0.1f * 0.5f);//左半圆
                }
            } else//右滑
            {
                if (mPercent <= 1 && mPercent >= 0.8) {//左凸起 圆心不变
                    mControlPoint[5].x = mCenterPoint.x + mRadius;//右半圆
                    mControlPoint[0].x = mCenterPoint.x - mRadius * (2 - (mPercent - 0.8f) / 0.2f);//左半圆
                } else if (mPercent > 0.5 && mPercent <= 0.8) {//加速 右凸起 扁平化M 最左端固定不变  圆心移动
                    mControlPoint[5].x = mCenterPoint.x + mRadius * (2 - (mPercent - 0.5f) / 0.3f);//右半圆
                    mControlPoint[0].x = mCenterPoint.x - 2 * mRadius;//左半圆
                    mControlPoint[2].y = mRadius * (1 - (0.8f - mPercent) / 0.3f * 0.1f);//底部
                    mControlPoint[8].y = -mRadius * (1 - (0.8f - mPercent) / 0.3f * 0.1f);//顶部
                    mc = mc * (1 + (0.8f - mPercent) / 0.3f * 0.3f);
                } else if (mPercent > 0.2 && mPercent <= 0.5) {//左右恢复 变圆M逐渐重置为原来大小  圆心移动
                    mControlPoint[5].x = mCenterPoint.x + mRadius * (1 + (mPercent - 0.2f) / 0.3f);//右半圆
                    mControlPoint[0].x = mCenterPoint.x - mRadius * (1 + (mPercent - 0.2f) / 0.3f);//左半圆
                    mControlPoint[2].y = mRadius * (1 - (mPercent - 0.2f) / 0.3f * 0.1f);//底部
                    mControlPoint[8].y = -mRadius * (1 - (mPercent - 0.2f) / 0.3f * 0.1f);//顶部
                    mc = mc * (1 + (mPercent - 0.2f) / 0.3f * 0.3f);
                } else if (mPercent > 0.1 && mPercent <= 0.2) {//右凹 圆心到达
                    mControlPoint[5].x = mCenterPoint.x + mRadius * (1 - (0.2f - mPercent) / 0.1f * 0.5f);//右半圆
                    mControlPoint[0].x = mCenterPoint.x - mRadius;//左半圆
                } else if (mPercent >= 0 && mPercent <= 0.1) {//回弹 圆心到达
                    mControlPoint[5].x = mCenterPoint.x + mRadius * (1 - (mPercent) / 0.1f * 0.5f);//右半圆
                    mControlPoint[0].x = mCenterPoint.x - mRadius;//左半圆
                }

            }
        }

        //11 0 1
        mControlPoint[0].y = 0;
        mControlPoint[1].x = mControlPoint[0].x;
        mControlPoint[1].y = mRadius * mc;
        mControlPoint[11].x = mControlPoint[0].x;
        mControlPoint[11].y = -mRadius * mc;
        //2 3 4
        mControlPoint[2].x = mCenterPoint.x - mRadius * mc;
        mControlPoint[3].x = mCenterPoint.x;
        mControlPoint[3].y = mControlPoint[2].y;
        mControlPoint[4].x = mCenterPoint.x + mRadius * mc;
        mControlPoint[4].y = mControlPoint[2].y;
        //5 6 7
        mControlPoint[5].y = mRadius * mc;
        mControlPoint[6].x = mControlPoint[5].x;
        mControlPoint[6].y = 0;
        mControlPoint[7].x = mControlPoint[5].x;
        mControlPoint[7].y = -mRadius * mc;
        //8 9 10
        mControlPoint[8].x = mCenterPoint.x + mRadius * mc;
        mControlPoint[9].x = mCenterPoint.x;
        mControlPoint[9].y = mControlPoint[8].y;
        mControlPoint[10].x = mCenterPoint.x - mRadius * mc;
        mControlPoint[10].y = mControlPoint[8].y;
    }

    private MyViewPagerIndicator.Point[] mControlPoint = new MyViewPagerIndicator.Point[9];
    private MyViewPagerIndicator.CenterPoint mCenterPoint = new MyViewPagerIndicator.CenterPoint();

    class CenterPoint {
        float x;
        float y;
    }

    class Point {
        float x;
        float y;
    }

    /**
     * 移动指示点
     *
     * @param percent  比例
     * @param position 第几个
     * @param isLeft   是否左滑
     */
    public void move(float percent, int position, boolean isLeft) {
        mPosition = position;
        mPercent = percent;
        mIsLeft = isLeft;
        switch (mIndicatorType) {
            case MyViewPagerIndicator.IndicatorType.CIRCLE_LINE://圆线
                if (mPosition == mNum - 1 && !isLeft) {//第一个 右滑
                    mOffset = percent * mDistance;
                }
                if (mPosition == mNum - 1 && isLeft) {//最后一个 左滑
                    mOffset = percent * mDistance;
                } else {//中间
                    mOffset = percent * mDistance;
                }
                break;
            case MyViewPagerIndicator.IndicatorType.CIRCLE://圆
            case MyViewPagerIndicator.IndicatorType.LINE://线
            case MyViewPagerIndicator.IndicatorType.PROGRESS://进度条
                if (mPosition == mNum - 1 && !isLeft) {//第一个 右滑
                    mOffset = (1 - percent) * (mNum - 1) * mDistance;
                } else if (mPosition == mNum - 1 && isLeft) {//最后一个 左滑
                    mOffset = (1 - percent) * (mNum - 1) * mDistance;
                } else {//中间的
                    mOffset = (percent + mPosition) * mDistance;
                }
                break;
            case MyViewPagerIndicator.IndicatorType.BEZIER://贝塞尔

                break;
            case MyViewPagerIndicator.IndicatorType.SPRING://弹性

                break;
        }

        invalidate();
    }

    /**
     * 个数
     *
     * @param num
     */
    public MyViewPagerIndicator setNum(int num) {
        mNum = num;
        invalidate();
        return this;
    }

    /**
     * 类型
     *
     * @param indicatorType
     */
    public MyViewPagerIndicator setType(int indicatorType) {
        mIndicatorType = indicatorType;
        invalidate();
        return this;
    }


    /**
     * 线,圆,圆线,贝塞尔,弹性球,进度条
     */
    public interface IndicatorType {
        int LINE = 0;
        int CIRCLE = 1;
        int CIRCLE_LINE = 2;
        int BEZIER = 3;
        int SPRING = 4;
        int PROGRESS = 5;
    }


    /**
     * 半径
     *
     * @param radius
     */
    public MyViewPagerIndicator setRadius(float radius) {
        this.mRadius = radius;
        invalidate();
        return this;
    }

    /**
     * 距离 在IndicatorDistanceType为BYDISTANCE下作用
     *
     * @param distance
     */
    public MyViewPagerIndicator setDistance(float distance) {
        this.mDistance = distance;
        invalidate();
        return this;
    }

    /**
     * 距离类型
     *
     * @param mDistanceType
     */
    public MyViewPagerIndicator setDistanceType(int mDistanceType) {
        this.mDistanceType = mDistanceType;
        invalidate();
        return this;
    }

    /**
     * 布局,距离,半径
     */
    public interface DistanceType { //
        int BY_RADIUS = 0;
        int BY_DISTANCE = 1;
        int BY_LAYOUT = 2;
    }

    /**
     * 一般 不循环 固定
     * @param viewPager 适配的viewpager
     * @return
     */
    public MyViewPagerIndicator setViewPager(ViewPager2 viewPager) {
        setViewPager(viewPager, viewPager.getAdapter().getItemCount(),false);
        return this;
    }


    /**
     *
     * @param viewpager 适配的viewpager
     * @param CycleNumber 伪无限循环 真实个数
     * @return
     */
    public MyViewPagerIndicator setViewPager(ViewPager2 viewpager, int CycleNumber) {

        setViewPager(viewpager, CycleNumber,false);
        return this;
    }
    /**
     *
     * @param viewPager 适配的viewpager
     * @param isInfiniteCircle 真无限循环 配合BannerView 通常是true;false为一般 不循环 固定等价于{@link #setViewPager(ViewPager viewPager)}
     *
     * @return
     */
    public MyViewPagerIndicator setViewPager(ViewPager2 viewPager, boolean isInfiniteCircle) {

        if(isInfiniteCircle){
            setViewPager(viewPager,viewPager.getAdapter().getItemCount()-2,isInfiniteCircle);
        }else{
            setViewPager(viewPager,viewPager.getAdapter().getItemCount(),isInfiniteCircle);
        }
        return this;
    }

    /**
     *
     * @param viewpager 适配的viewpager
     * @param CycleNumber 真/伪无限循环都必须输入
     * @param isInfiniteCircle 真无限循环 配合Banner
     * @return
     */
    public MyViewPagerIndicator setViewPager(ViewPager2 viewpager, int CycleNumber, boolean isInfiniteCircle) {
        mNum = CycleNumber;
        mIsInfiniteCircle = isInfiniteCircle;
        viewpager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {

            //记录上一次滑动的positionOffsetPixels值
            private int lastValue = -1;

            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
                if(!mAnimation){
                    //不需要动画
                    return;
                }
                boolean isLeft = mIsLeft;
                if (lastValue / 10 > positionOffsetPixels / 10) {
                    //右滑
                    isLeft = false;
                } else if (lastValue / 10 < positionOffsetPixels / 10) {
                    //左滑
                    isLeft = true;
                }
                if (mNum > 0&&!mIsInfiniteCircle) {
                    move(positionOffset, position % mNum, isLeft);
                }else if(mNum>0&&mIsInfiniteCircle){
                    if(position==0){
                        position=mNum-1;
                    }else if(position==mNum+1){
                        position=0;
                    }else{
                        position--;
                    }
                    move(positionOffset, position , isLeft);
                }
                lastValue = positionOffsetPixels;
            }

            @Override
            public void onPageSelected(int position) {
                if(mAnimation){
                    //需要动画
                    return;
                }
                if(mNum>0&&!mIsInfiniteCircle)
                {
                    move(0, position % mNum, false);
                }else if(mNum>0&&mIsInfiniteCircle){
                    if(position==0){
                        position=mNum-1;
                    }else if(position==mNum+1){
                        position=0;
                    }else{
                        position--;
                    }
                    move(0, position , false);
                }
            }

            @Override
            public void onPageScrollStateChanged(int state) {

            }
        });

        return this;
    }


}

3:属性说明

属性说明
sbl_selected_color
选中的指示器颜色
sbl_default_color
未选中的指示器颜色
sbl_distanceType
指示器的距离类型
sbl_indicatorType
指示器的类型

 

 

 

 

 

 

 

 

 

 

4:使用效果加说明

(1):圆线

<com.sbl.hwscankitdemo.view.MyViewPagerIndicator
    android:layout_centerHorizontal="true"
    android:layout_alignParentBottom="true"
    android:id="@+id/indicator_line"
    android:layout_width="wrap_content"
    android:layout_marginBottom="10dp"
    android:layout_height="16dp"
    app:sbl_default_color="#f00"
    app:sbl_radius="3dp"
    app:sbl_distanceType="BY_DISTANCE"
    app:sbl_indicatorType="CIRCLE_LINE"
    app:sbl_distance="10dp"


    />
属性 (sbl_indicatorType)说明
CIRCLE_LINE
圆线
sbl_default_color
默认的颜色(sbl_selected_color)无作用,
sbl_radius
原点的半径
sbl_distanceType="BY_DISTANCE"指示器的item间距类型,
sbl_distanceitem间距

 

 

 

 

 

 

 

 

 

 

(2):线

<com.sbl.hwscankitdemo.view.MyViewPagerIndicator
    android:layout_centerHorizontal="true"
    android:layout_alignParentBottom="true"
    android:id="@+id/indicator_line"
    android:layout_width="wrap_content"
    android:layout_marginBottom="10dp"
    android:layout_height="16dp"
    app:sbl_default_color="#f00"
    app:sbl_selected_color="#ff0"
    app:sbl_radius="3dp"
    app:sbl_distanceType="BY_DISTANCE"
    app:sbl_indicatorType="LINE"
    app:sbl_distance="10dp"


    />
属性 (sbl_indicatorType)说明
LINE
线
sbl_default_color
未选中的颜色
sbl_selected_color
选中的颜色
sbl_radius
原点的半径
sbl_distanceType="BY_DISTANCE"指示器的item间距类型,
sbl_distanceitem间距

 

 

 

 

 

 

 

 

 

 

 

(3):点

<com.sbl.hwscankitdemo.view.MyViewPagerIndicator
    android:layout_centerHorizontal="true"
    android:layout_alignParentBottom="true"
    android:id="@+id/indicator_line"
    android:layout_width="wrap_content"
    android:layout_marginBottom="10dp"
    android:layout_height="16dp"
    app:sbl_default_color="#f00"
    app:sbl_selected_color="#ff0"
    app:sbl_radius="3dp"
    app:sbl_distanceType="BY_DISTANCE"
    app:sbl_indicatorType="CIRCLE"
    app:sbl_distance="10dp"


    />
属性 (sbl_indicatorType)说明
CIRCLE
sbl_default_color
未选中的颜色
sbl_selected_color
选中的颜色
sbl_radius
原点的半径
sbl_distanceType="BY_DISTANCE"指示器的item间距类型,
sbl_distanceitem间距

 

 

 

 

 

 

 

 

 

 

 

(4):贝塞尔曲线、弹性球和进度条样式这里就不举例了,gif制作有点虚,有兴趣的可以自己修改代码看下效果。

3:和viewpager2绑定

public class BannerActivity extends AppCompatActivity {
    Banner banner;
    public List<Integer> list = new ArrayList<>();
    MyViewPagerIndicator indicator;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_banner);
        banner = findViewById(R.id.banner);
        indicator = findViewById(R.id.indicator_line);
        banner.setAdapter(new ImageAdapter(DataBean.getTestData()));
       //在这里绑定viewpager2
       indicator.setViewPager( banner.getViewPager2(),DataBean.getTestData().size());
       




    }



}
activity_banner.xml布局
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="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=".BannerActivity">
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="150dp">
        <com.youth.banner.Banner
            xmlns:banner="http://schemas.android.com/apk/res-auto"
            android:id="@+id/banner"
            android:layout_width="match_parent"
            android:layout_height="150dp"   />
        <com.sbl.hwscankitdemo.view.MyViewPagerIndicator
            android:layout_centerHorizontal="true"
            android:layout_alignParentBottom="true"
            android:id="@+id/indicator_line"
            android:layout_width="wrap_content"
            android:layout_marginBottom="10dp"
            android:layout_height="16dp"
            app:sbl_default_color="#f00"
            app:sbl_selected_color="#ff0"
            app:sbl_radius="3dp"
            app:sbl_distanceType="BY_DISTANCE"
            app:sbl_indicatorType="PROGRESS"
            app:sbl_distance="10dp"


            />
    </RelativeLayout>

</RelativeLayout>

 

4:源码地址:https://gitee.com/ishopSbl/hua-wei-qr-demo.git

 
  • 5
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

weixin_今晚不加班

谢谢

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值