android实现可以滑动的平滑曲线图

来源:互联网 发布:java 数字选择控件 编辑:程序博客网 时间:2024/06/06 03:20

直接上代码,里面有详细注解

1 attr 属性编写

    <!-- xy坐标轴颜色 -->    <attr name="xy_line_color" format="color" />    <!-- xy坐标轴宽度 -->    <attr name="xy_line_width" format="dimension" />    <!-- xy坐标轴文字颜色 -->    <attr name="xy_text_color" format="color" />    <!-- xy坐标轴文字大小 -->    <attr name="xy_text_size" format="dimension" />    <!-- 折线图中折线的颜色 -->    <attr name="line_color" format="color" />    <!-- x轴各个坐标点水平间距 -->    <attr name="interval" format="dimension" />    <!-- 背景颜色 -->    <attr name="bg_color" format="color" />    <!-- 曲线选中外部颜色 -->    <attr name="select_circle_color" format="color" />    <!-- 曲线选中内部颜色 -->    <attr name="select_reminder_color" format="color" />    <!--是否抬手滚动-->    <attr name="isScroll" format="boolean" />    <declare-styleable name="ChartView">        <attr name="xy_line_color" />        <attr name="xy_line_width" />        <attr name="xy_text_color" />        <attr name="xy_text_size" />        <attr name="line_color" />        <attr name="interval" />        <attr name="bg_color" />        <attr name="select_circle_color" />        <attr name="select_reminder_color" />        <attr name="isScroll" />        <!--提示框跟滑动显示的位置-->        <attr name="show_position">            <enum name="first" value="1" />            <enum name="middle" value="2" />            <enum name="end" value="3" />        </attr>    </declare-styleable>

2 ChartView

package com.laisontech.commonuilibrary.customviews;import android.animation.Animator;import android.animation.ValueAnimator;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Path;import android.graphics.PorterDuff;import android.graphics.PorterDuffXfermode;import android.graphics.Rect;import android.graphics.RectF;import android.util.AttributeSet;import android.util.Log;import android.util.TypedValue;import android.view.MotionEvent;import android.view.VelocityTracker;import android.view.View;import android.view.animation.DecelerateInterpolator;import com.laisontech.commonuilibrary.R;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;/** * 自定义折线图 */public class ChartView extends View {    private static final int FIRST = 1;    private static final int MIDDLE = 2;    private static final int END = 3;    //xy坐标轴颜色    private int xyLineColor = 0xffCFE2CF;    //折线选中的圆形颜色    private int selectCircleColor = 0xff00A8FF;    //选中数据提示框颜色    private int selectReminderColor = 0xff00A8FF;    //折线中圆形内部部颜色    private int xyTextColor = 0xff0014FF;    //折线图中折线的颜色    private int lineColor = 0xffFD00FF;    //xy坐标轴宽度    private int xyLineWidth = dpToPx(1);    //xy坐标轴文字大小    private int xyTextSize = spToPx(12);    //x轴各个坐标点水平间距    private int interval = dpToPx(40);    //背景颜色    private int bgColor = 0xffffffff;    //是否有起手时的滑动感    private boolean isScroll = false;    //提示框显示位置    private int mShowPositionType = 3;    //绘制XY轴坐标对应的画笔    private Paint mXYPaint;    //绘制XY轴的文本对应的画笔    private Paint mXYTextPaint;    //画折线对应的画笔    private Paint mSpinnerLinePaint;    private int width;    private int height;    //x轴的原点坐标    private int mXOri;    //y轴的原点坐标    private int mYOri;    //第一个点X的坐标    private float mXInit;    //第一个点对应的最大X坐标    private float maxXInit;    //第一个点对应的最小X坐标    private float minXInit;    //x轴坐标对应的数据    private List<String> mXData = new ArrayList<>();    //y轴坐标对应的数据    private List<Integer> mYData = new ArrayList<>();    //折线对应的数据    private Map<String, Integer> mSpinnerValue = new HashMap<>();    //点击的点对应的X轴的第几个点,默认1    private int selectIndex = 1;    //X轴刻度文本对应的最大矩形,为了选中时,在x轴文本画的框框大小一致,获取从数据中得到的x轴数据,获得最长数据    private Rect xValueRect;    //速度检测器    private VelocityTracker mTracker;    //是否为短距离滑动    private boolean isShortSlide = false;    //获取尺寸的的中间    private int mSelectMiddle = 0;    //曲线切率    private float mLineSmoothness = 0.18f;    public ChartView(Context context) {        this(context, null);    }    public ChartView(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public ChartView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init(context, attrs, defStyleAttr);        initPaint();    }    //设置切率    public void setLineSmoothness(float lineSmoothness) {        if (lineSmoothness != this.mLineSmoothness) {            this.mLineSmoothness = lineSmoothness;        }    }    /**     * 初始化     */    private void initPaint() {        mXYPaint = new Paint();        mXYPaint.setAntiAlias(true);        mXYPaint.setStrokeWidth(xyLineWidth);        mXYPaint.setStrokeJoin(Paint.Join.ROUND);        mXYPaint.setColor(xyLineColor);        mXYTextPaint = new Paint();        mXYTextPaint.setAntiAlias(true);        mXYTextPaint.setTextSize(xyTextSize);        mXYTextPaint.setStrokeJoin(Paint.Join.ROUND);        mXYTextPaint.setColor(xyTextColor);        mXYTextPaint.setStyle(Paint.Style.STROKE);        mSpinnerLinePaint = new Paint();        mSpinnerLinePaint.setAntiAlias(true);        mSpinnerLinePaint.setStrokeWidth(xyLineWidth);        mSpinnerLinePaint.setColor(lineColor);        mSpinnerLinePaint.setStyle(Paint.Style.STROKE);        mSpinnerLinePaint.setStrokeJoin(Paint.Join.ROUND);    }    /**     * 初始化     *     * @param context     * @param attrs     * @param defStyleAttr     */    private void init(Context context, AttributeSet attrs, int defStyleAttr) {        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ChartView, defStyleAttr, 0);        int count = array.getIndexCount();        for (int i = 0; i < count; i++) {            int attr = array.getIndex(i);            if (attr == R.styleable.ChartView_xy_line_color) {                xyLineColor = array.getColor(attr, xyLineColor);            } else if (attr == R.styleable.ChartView_xy_line_width) {                xyLineWidth = (int) array.getDimension(attr, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, xyLineWidth, getResources().getDisplayMetrics()));            } else if (attr == R.styleable.ChartView_xy_text_color) {                xyTextColor = array.getColor(attr, xyTextColor);            } else if (attr == R.styleable.ChartView_xy_text_size) {                xyTextSize = (int) array.getDimension(attr, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, xyTextSize, getResources().getDisplayMetrics()));            } else if (attr == R.styleable.ChartView_line_color) {                lineColor = array.getColor(attr, lineColor);            } else if (attr == R.styleable.ChartView_interval) {                interval = (int) array.getDimension(attr, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, interval, getResources().getDisplayMetrics()));            } else if (attr == R.styleable.ChartView_bg_color) {                bgColor = array.getColor(attr, bgColor);            } else if (attr == R.styleable.ChartView_select_circle_color) {                selectCircleColor = array.getColor(attr, selectCircleColor);            } else if (attr == R.styleable.ChartView_select_reminder_color) {                selectReminderColor = array.getColor(attr, selectReminderColor);            } else if (attr == R.styleable.ChartView_isScroll) {                isScroll = array.getBoolean(attr, isScroll);            } else if (attr == R.styleable.ChartView_show_position) {                mShowPositionType = array.getInt(attr, mShowPositionType);            }        }        array.recycle();    }    @Override    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {        if (changed) {            width = getWidth();            height = getHeight();            //Y轴文本的最大宽度            float textYWdith = getTextBounds(mYData.get(getListItemMaxIndex(mYData)) + "", mXYTextPaint).width();            for (int i = 0; i < mYData.size(); i++) {//求取y轴文本最大的宽度                float temp = getTextBounds(mYData.get(i) + "", mXYTextPaint).width();                if (temp > textYWdith)                    textYWdith = temp;            }            int dp2 = dpToPx(2);            int dp3 = dpToPx(3);            mXOri = (int) (dp2 + textYWdith + dp2 + xyLineWidth);            //获取x轴的最长文本的宽度所占的矩形            xValueRect = getTextBounds(mXData.get(getListItemMaxIndex(mXData)), mXYTextPaint);            //X轴文本高度            float textXHeight = xValueRect.height();            for (int i = 0; i < mXData.size(); i++) {                Rect rect = getTextBounds(mXData.get(i) + "", mXYTextPaint);                if (rect.height() > textXHeight)                    textXHeight = rect.height();                if (rect.width() > xValueRect.width())                    xValueRect = rect;            }            mYOri = (int) (height - dp2 - textXHeight - dp3 - xyLineWidth);            mXInit = mXOri + xValueRect.width() / 2 + dpToPx(5);            minXInit = width - (width - mXOri) * 0.1f - interval * (mXData.size() - 1);            maxXInit = mXInit;        }        selectIndex = getSelectIndexFromShowType(mShowPositionType);        super.onLayout(changed, left, top, right, bottom);    }    @Override    protected void onDraw(Canvas canvas) {        canvas.drawColor(bgColor);        drawXY(canvas);        drawBrokenLineAndPoint(canvas);    }    /**     * 绘制交点处对应的点     */    private void drawBrokenLineAndPoint(Canvas canvas) {        if (mXData.size() <= 0)            return;        int layerId = canvas.saveLayer(0, 0, width, height, null, Canvas.ALL_SAVE_FLAG);        drawBrokenLine(canvas);        drawBrokenPoint(canvas);        // 将超出x轴坐标的部分截掉        mSpinnerLinePaint.setStyle(Paint.Style.FILL);        mSpinnerLinePaint.setColor(bgColor);        mSpinnerLinePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));        RectF rectF = new RectF(0, 0, mXOri, height);        canvas.drawRect(rectF, mSpinnerLinePaint);        mSpinnerLinePaint.setXfermode(null);        canvas.restoreToCount(layerId);    }    /**     * 绘制曲线对应的点     */    private void drawBrokenPoint(Canvas canvas) {        float dp2 = dpToPx(2);        float dp4 = dpToPx(4);        float dp7 = dpToPx(7);        Log.e("selectIndex", "index:" + selectIndex);        //绘制节点        for (int i = 0; i < mXData.size(); i++) {            float x = mXInit + interval * i;            float y = mYOri - mYOri * (1 - 0.1f) * mSpinnerValue.get(mXData.get(i)) / mYData.get(mYData.size() - 1);            //绘制选中点            if (i == selectIndex - 1) {                mSpinnerLinePaint.setStyle(Paint.Style.FILL);                //设置选中颜色                mSpinnerLinePaint.setColor(selectCircleColor);                canvas.drawCircle(x, y, dp7, mSpinnerLinePaint);                mSpinnerLinePaint.setColor(selectReminderColor);                canvas.drawCircle(x, y, dp4, mSpinnerLinePaint);                drawFloatTextBox(canvas, x, y - dp7, mSpinnerValue.get(mXData.get(i)));            }            //绘制普通节点            mSpinnerLinePaint.setStyle(Paint.Style.FILL);            mSpinnerLinePaint.setColor(Color.WHITE);            canvas.drawCircle(x, y, dp2, mSpinnerLinePaint);            mSpinnerLinePaint.setStyle(Paint.Style.STROKE);            mSpinnerLinePaint.setColor(lineColor);            canvas.drawCircle(x, y, dp2, mSpinnerLinePaint);        }    }    /**     * 绘制浮动框     * */    private void drawFloatTextBox(Canvas canvas, float x, float y, int text) {        int dp6 = dpToPx(6);        int dp18 = dpToPx(18);        //p1        Path path = new Path();        path.moveTo(x, y);        //p2        path.lineTo(x - dp6, y - dp6);        //p3        path.lineTo(x - dp18, y - dp6);        //p4        path.lineTo(x - dp18, y - dp6 - dp18);        //p5        path.lineTo(x + dp18, y - dp6 - dp18);        //p6        path.lineTo(x + dp18, y - dp6);        //p7        path.lineTo(x + dp6, y - dp6);        //p1        path.lineTo(x, y);        canvas.drawPath(path, mSpinnerLinePaint);        mSpinnerLinePaint.setColor(Color.WHITE);        mSpinnerLinePaint.setTextSize(spToPx(14));        Rect rect = getTextBounds(text + "", mSpinnerLinePaint);        canvas.drawText(text + "", x - rect.width() / 2, y - dp6 - (dp18 - rect.height()) / 2, mSpinnerLinePaint);    }    /**     * 绘制平滑曲线     */    private void drawBrokenLine(Canvas canvas) {        mSpinnerLinePaint.setStyle(Paint.Style.STROKE);        mSpinnerLinePaint.setColor(lineColor);        //绘制折线        Path path = new Path();        float prePreviousPointX = Float.NaN;        float prePreviousPointY = Float.NaN;        float previousPointX = Float.NaN;        float previousPointY = Float.NaN;        float currentPointX = Float.NaN;        float currentPointY = Float.NaN;        float nextPointX;        float nextPointY;        int lineSize = mXData.size();        for (int i = 0; i < lineSize; i++) {            float x;            float y;            if (Float.isNaN(currentPointX)) {                currentPointX = getSpinnerPoint(i).x;                currentPointY = getSpinnerPoint(i).y;            }            if (Float.isNaN(previousPointX)) {                //是第一个点?                if (i > 0) {                    previousPointX = getSpinnerPoint(i - 1).x;                    previousPointY = getSpinnerPoint(i - 1).y;                } else {                    //用当前点表示上一个点                    previousPointX = currentPointX;                    previousPointY = currentPointY;                }            }            if (Float.isNaN(prePreviousPointX)) {                //是前两个点?                if (i > 1) {                    prePreviousPointX = getSpinnerPoint(i - 2).x;                    prePreviousPointY = getSpinnerPoint(i - 2).y;                } else {                    //当前点表示上上个点                    prePreviousPointX = previousPointX;                    prePreviousPointY = previousPointY;                }            }            // 判断是不是最后一个点了            if (i < lineSize - 1) {                nextPointX = getSpinnerPoint(i + 1).x;                nextPointY = getSpinnerPoint(i + 1).y;            } else {                //用当前点表示下一个点                nextPointX = currentPointX;                nextPointY = currentPointY;            }            if (i == 0) {                // 将Path移动到开始点                path.moveTo(currentPointX, currentPointY);            } else {                // 求出控制点坐标                final float firstDiffX = (currentPointX - prePreviousPointX);                final float firstDiffY = (currentPointY - prePreviousPointY);                final float secondDiffX = (nextPointX - previousPointX);                final float secondDiffY = (nextPointY - previousPointY);                final float firstControlPointX = previousPointX + (mLineSmoothness * firstDiffX);                final float firstControlPointY = previousPointY + (mLineSmoothness * firstDiffY);                final float secondControlPointX = currentPointX - (mLineSmoothness * secondDiffX);                final float secondControlPointY = currentPointY - (mLineSmoothness * secondDiffY);                //画出曲线                path.cubicTo(firstControlPointX, firstControlPointY, secondControlPointX, secondControlPointY,                        currentPointX, currentPointY);            }            // 更新            prePreviousPointX = previousPointX;            prePreviousPointY = previousPointY;            previousPointX = currentPointX;            previousPointY = currentPointY;            currentPointX = nextPointX;            currentPointY = nextPointY;        }        canvas.drawPath(path, mSpinnerLinePaint);    }    /**     * 绘制XY坐标     */    private void drawXY(Canvas canvas) {        int length = dpToPx(5);//刻度的长度        //绘制Y坐标        canvas.drawLine(mXOri - xyLineWidth / 2, 0, mXOri - xyLineWidth / 2, mYOri, mXYPaint);        //绘制箭头        mXYPaint.setStyle(Paint.Style.STROKE);        Path path = new Path();        path.moveTo(mXOri - xyLineWidth / 2 - dpToPx(5), dpToPx(12));        path.lineTo(mXOri - xyLineWidth / 2, xyLineWidth / 2);        path.lineTo(mXOri - xyLineWidth / 2 + dpToPx(5), dpToPx(12));        canvas.drawPath(path, mXYPaint);        //绘制刻度        int yLength = (int) (mYOri * (1 - 0.1f) / (mYData.size() - 1));//y轴上面空出10%,计算出y轴刻度间距        for (int i = 0; i < mYData.size(); i++) {            //绘制刻度            canvas.drawLine(mXOri, mYOri - yLength * i + xyLineWidth / 2, mXOri + length, mYOri - yLength * i + xyLineWidth / 2, mXYPaint);            mXYTextPaint.setColor(xyTextColor);            //绘制文本            String text = mYData.get(i) + "";            Rect rect = getTextBounds(text, mXYTextPaint);            canvas.drawText(text, 0, text.length(), mXOri - xyLineWidth - dpToPx(2) - rect.width(), mYOri - yLength * i + rect.height() / 2, mXYTextPaint);        }        //绘制坐标        canvas.drawLine(mXOri, mYOri + xyLineWidth / 2, width, mYOri + xyLineWidth / 2, mXYPaint);        //绘制箭头        mXYPaint.setStyle(Paint.Style.STROKE);        path = new Path();        //整个长度        float xLength = mXInit + interval * (mXData.size() - 1) + (width - mXOri) * 0.1f;        if (xLength < width)            xLength = width;        path.moveTo(xLength - dpToPx(12), mYOri + xyLineWidth / 2 - dpToPx(5));        path.lineTo(xLength - xyLineWidth / 2, mYOri + xyLineWidth / 2);        path.lineTo(xLength - dpToPx(12), mYOri + xyLineWidth / 2 + dpToPx(5));        canvas.drawPath(path, mXYPaint);        //绘制x轴刻度        for (int i = 0; i < mXData.size(); i++) {            float x = mXInit + interval * i;            if (x >= mXOri) {//只绘制从原点开始的区域                mXYTextPaint.setColor(xyTextColor);                canvas.drawLine(x, mYOri, x, mYOri - length, mXYPaint);                //绘制X轴文本                String text = mXData.get(i);                Rect rect = getTextBounds(text, mXYTextPaint);                if (i == selectIndex - 1) {                    mXYTextPaint.setColor(lineColor);                    canvas.drawText(text, 0, text.length(), x - rect.width() / 2, mYOri + xyLineWidth + dpToPx(2) + rect.height(), mXYTextPaint);                    canvas.drawRoundRect(x - xValueRect.width() / 2 - dpToPx(3), mYOri + xyLineWidth + dpToPx(1), x + xValueRect.width() / 2 + dpToPx(3), mYOri + xyLineWidth + dpToPx(2) + xValueRect.height() + dpToPx(2), dpToPx(2), dpToPx(2), mXYTextPaint);                } else {                    canvas.drawText(text, 0, text.length(), x - rect.width() / 2, mYOri + xyLineWidth + dpToPx(2) + rect.height(), mXYTextPaint);                }            }        }    }    private float startX;    private float startx;    @Override    public boolean onTouchEvent(MotionEvent event) {        if (isScrolling)            return super.onTouchEvent(event);            //当该view获得点击事件,就请求父控件不拦截事件        this.getParent().requestDisallowInterceptTouchEvent(true);        obtainVelocityTracker(event);        switch (event.getAction()) {            case MotionEvent.ACTION_DOWN:                startX = event.getX();                startx = event.getX();                Log.e("XXXX", "down:" + startX + "");                break;            case MotionEvent.ACTION_MOVE:                //滑动距离小于等于8的时候任务为短距离滑动                //当前x轴的尺寸与设置的x轴间隔的距离之乘积大于 屏幕中的显示布局宽度与x轴七点之差时,开始移动                if (interval * mXData.size() > width - mXOri) {                    //获取滑动的距离                    float dis = event.getX() - startX;                    //重新赋值给startX                    startX = event.getX();                    //当前x原点距离与左右滑动的距离之和没有最小值大,则将当前x距离赋值为最小,以下相似                    if (mXInit + dis < minXInit) {                        mXInit = minXInit;                    } else if (mXInit + dis > maxXInit) {                        mXInit = maxXInit;                    } else {                        mXInit = mXInit + dis;                    }                    invalidate();                }                break;            case MotionEvent.ACTION_UP:                isShortSlide = Math.abs(event.getX() - startx) <= dpToPx(8);                clickAction(event);                scrollAfterActionUp();                this.getParent().requestDisallowInterceptTouchEvent(false);                recycleVelocityTracker();                break;            case MotionEvent.ACTION_CANCEL:                //增加这行代码防止与父类的滑动事件冲突                this.getParent().requestDisallowInterceptTouchEvent(false);                recycleVelocityTracker();                break;        }        return true;    }    //是否正在滑动    private boolean isScrolling = false;    /**     * 手指抬起后的滑动处理     */    private void scrollAfterActionUp() {        if (!isScroll)            return;        final float velocity = getVelocity();        float scrollLength = maxXInit - minXInit;        if (Math.abs(velocity) < 10000)            scrollLength = (maxXInit - minXInit) * Math.abs(velocity) / 10000;        ValueAnimator animator = ValueAnimator.ofFloat(0, scrollLength);        animator.setDuration((long) (scrollLength / (maxXInit - minXInit) * 1000));//时间最大为1000毫秒,此处使用比例进行换算        animator.setInterpolator(new DecelerateInterpolator());        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator valueAnimator) {                float value = (float) valueAnimator.getAnimatedValue();                if (velocity < 0 && mXInit > minXInit) {//向左滑动                    if (mXInit - value <= minXInit)                        mXInit = minXInit;                    else                        mXInit = mXInit - value;                } else if (velocity > 0 && mXInit < maxXInit) {//向右滑动                    if (mXInit + value >= maxXInit)                        mXInit = maxXInit;                    else                        mXInit = mXInit + value;                }                invalidate();            }        });        animator.addListener(new Animator.AnimatorListener() {            @Override            public void onAnimationStart(Animator animator) {                isScrolling = true;            }            @Override            public void onAnimationEnd(Animator animator) {                isScrolling = false;            }            @Override            public void onAnimationCancel(Animator animator) {                isScrolling = false;            }            @Override            public void onAnimationRepeat(Animator animator) {            }        });        animator.start();    }    /**     * 获取速度     *     * @return     */    private float getVelocity() {        if (mTracker != null) {            mTracker.computeCurrentVelocity(1000);            return mTracker.getXVelocity();        }        return 0;    }    /**     * 点击X轴坐标或者折线节点     *  */    // 44  142  139    private void clickAction(MotionEvent event) {        int dp8 = dpToPx(8);        float eventX = event.getX();        float eventY = event.getY();        if (!isShortSlide) {            for (int i = 0; i < mXData.size(); i++) {                float x = mXInit + interval * i;                float start = mXOri;                if (x >= start + (mSelectMiddle - 1) * interval && x < start + mSelectMiddle * interval) {                    selectIndex = i + 1;                    invalidate();                }            }            return;        }        for (int i = 0; i < mXData.size(); i++) {            //节点            float x = mXInit + interval * i;            float y = mYOri - mYOri * (1 - 0.1f) * mSpinnerValue.get(mXData.get(i)) / mYData.get(mYData.size() - 1);            if (eventX >= x - dp8 && eventX <= x + dp8 &&                    eventY >= y - dp8 && eventY <= y + dp8 && selectIndex != i + 1) {//每个节点周围范围内都是可点击区域                selectIndex = i + 1;                invalidate();                return;            }            //X轴刻度            String text = mXData.get(i);            Rect rect = getTextBounds(text, mXYTextPaint);            x = mXInit + interval * i;            y = mYOri + xyLineWidth + dpToPx(2);            if (eventX >= x - rect.width() / 2 - dp8 && eventX <= x + rect.width() + dp8 / 2 &&                    eventY >= y - dp8 && eventY <= y + rect.height() + dp8 && selectIndex != i + 1) {                selectIndex = i + 1;                invalidate();                return;            }        }    }    /**     * 获取速度跟踪器     *     * @param event     */    private void obtainVelocityTracker(MotionEvent event) {        if (!isScroll)            return;        if (mTracker == null) {            mTracker = VelocityTracker.obtain();        }        mTracker.addMovement(event);    }    /**     * 回收速度跟踪器     */    private void recycleVelocityTracker() {        if (mTracker != null) {            mTracker.recycle();            mTracker = null;        }    }    /**     * 根据用户输入显示类型,在滑动时在不同的位置显示提示框     */    private int getSelectIndexFromShowType(int showPositionType) {        int visibleScale = (width - mXOri) / interval;        switch (showPositionType) {            case FIRST:                mSelectMiddle = 1;                return mSelectMiddle;            case MIDDLE:                if (mXData.size() <= visibleScale) {                    mSelectMiddle = middleIndex(mXData.size());                } else {                    mSelectMiddle = middleIndex(visibleScale);                }                return mSelectMiddle;  //屏幕可显示的刻度            case END:                if (mXData.size() <= visibleScale) {                    mSelectMiddle = mXData.size();                } else {                    mSelectMiddle = visibleScale;                }                return visibleScale;            default:                mSelectMiddle = 0;                return mSelectMiddle;        }    }    public void setValue(Map<String, Integer> value) {        this.mSpinnerValue = value;        invalidate();    }    public void setValue(Map<String, Integer> value, List<String> xValue, List<Integer> yValue) {        this.mSpinnerValue = value;        this.mXData = xValue;        this.mYData = yValue;        invalidate();    }    public Map<String, Integer> getValue() {        return mSpinnerValue;    }    /**     * 获取丈量文本的矩形     *     * @param text     * @param paint     * @return     */    private Rect getTextBounds(String text, Paint paint) {        Rect rect = new Rect();        paint.getTextBounds(text, 0, text.length(), rect);        return rect;    }    /**     * dp转化成为px     *     * @param dp     * @return     */    private int dpToPx(int dp) {        float density = getContext().getResources().getDisplayMetrics().density;        return (int) (dp * density + 0.5f * (dp >= 0 ? 1 : -1));    }    /**     * sp转化为px     *     * @param sp     * @return     */    private int spToPx(int sp) {        float scaledDensity = getContext().getResources().getDisplayMetrics().scaledDensity;        return (int) (scaledDensity * sp + 0.5f * (sp >= 0 ? 1 : -1));    }    /**     * 获取集合中最长的index     */    private static final int NULL_INDEX = -1;    public int getListItemMaxIndex(List<?> data) {        if (data == null || data.size() < 1) {            return NULL_INDEX;        }        int max = (data.get(0) + "").length();        for (int i = 0; i < data.size(); i++) {            String s = data.get(i) + "";            if (s.length() > max) {                return i;            }        }        return NULL_INDEX;    }    //获得在滑动结束的时候在屏幕内的点    private int middleIndex(int size) {        if (size % 2 == 0) {            return size / 2;        } else {            return size / 2 + 1;        }    }    /**     * 根据两点坐标获取中间某个点     *     * @param from 坐标1     * @param to   坐标2     */    //获取已知点的斜率 y = kx+b    private float getSlope(Point from, Point to) {        float k = (to.y - from.y) / (to.x - from.x);        Log.e("Point", "参数b:" + k);        return k;    }    //获取参数 b    private float getParams(Point from, Point to) {        float b = from.y - (getSlope(from, to) * from.x);        Log.e("Point", "参数b:" + b);        return b;    }    //根据两点间的坐标获取x轴的任意一个坐标x值,    private float getArbitrarilyX(Point from, Point to, int grade, int needGrade) {        //获得输入的新坐标        float x = ((to.x - from.x) * needGrade) / grade + from.x;        Log.e("Point", "x坐标值:" + x);        return x;    }    //获取坐标值    private Point getPoint(Point from, Point to, int grade, int needGrade) {        Point point = new Point();        point.setX(getArbitrarilyX(from, to, grade, needGrade));        float slope = getSlope(from, to);        point.setY(slope * point.x + getParams(from, to));        return point;    }    //获取绘制折线的点    private Point getSpinnerPoint(int valueIndex) {        float x = mXInit + interval * (valueIndex);        float y = mYOri - mYOri * (1 - 0.1f) * mSpinnerValue.get(mXData.get(valueIndex)) / mYData.get(mYData.size() - 1);        return new Point(x, y);    }    private class Point {        float x;        float y;        public Point() {        }        public float getX() {            return x;        }        public void setX(float x) {            this.x = x;        }        public float getY() {            return y;        }        public void setY(float y) {            this.y = y;        }        public Point(float x, float y) {            this.x = x;            this.y = y;        }        @Override        public String toString() {            return "Point{" +                    "x=" + x +                    ", y=" + y +                    '}';        }    }}