Android原生股票图-分时图讲解和绘制(一)

Android原生股票图-分时图讲解和绘制(一)
Android原生股票图-分时图讲解和绘制(二)
Android原生股票图-K线图讲解和绘制(一)

一、试图的展示效

效果一

效果二

二、试图简介**

  • 效果图中分为两部分:
    1、主视图,主要展示分时图数据;
    2、子试图,主要展示CJL图和MACD图,点击子试图时两者进行切换。
  • 分时图、CJL图和MACD图简介:
    1、分时图
    分时图只要是 均线和成交价的折线,左轴时成交价中点为昨日成交量,右轴成交价的涨跌幅;
    2、CJL图
    左轴为成交量主要展示柱体的变化,柱体的类型见图:
    CJL 柱体类型

    右轴为持仓量主要展示折线图。
    3、MACD图
    如MACD(12,26,9)主要展示DIFF的值、DEA的值和STICK的值;计算公式如下:
    DIFF白线:
    DIFF=当前周期EMA(12)-当前周期EMA(26)
    EMA(12)=当前周期EMA(12)×11/13+当前周期收盘价×2/13
    EMA(26)=当前周期EMA(26)×25/27+当前周期收盘价×2/27
    DEA黄线
    当前周期DEA=前一周期DEA×8/10+当前周期DIF×2/10。
    STICK(MACD柱)
    STICK=当前周期(DIF-DEA)×2
    Y轴刻度生成规则:
    中轴为0,取图内DIFF、DEA、STICK三项指标最高绝对值时点值前两位向上取5的最小倍数,剩下位数以0填充,后将该值2等分并分别在中轴上下标识刻度。例如当前图内最高时点值为-1623,则前两位16向上取2的最小倍数为20,取值为2000,2等分后上下刻度分别为2000、1000、0、-1000、-2000。
    柱体类型:
    MACD 柱体类型

三、代码**

  • MACD的计算代码 ,主要根据closePrice生成DIF、DEA和STICK
/**
     * 计算macd
     *
     * @param datas
     */
    public static void calculateMACD(List<Minute> datas) {
        float ema12 = 0;
        float ema26 = 0;
        float dif = 0;
        float dea = 0;
        float macd = 0;

        for (int i = 0; i < datas.size(); i++) {
            Minute point = datas.get(i);
            final float closePrice = point.getClose();
            if (i == 0) {
                ema12 = closePrice;
                ema26 = closePrice;
            } else {
//                EMA(12) = 前一日EMA(12) X 11/13 + 今日收盘价 X 2/13
//                EMA(26) = 前一日EMA(26) X 25/27 + 今日收盘价 X 2/27
                ema12 = ema12 * 11f / 13f + closePrice * 2f / 13f;
                ema26 = ema26 * 25f / 27f + closePrice * 2f / 27f;
            }
//            DIF = EMA(12) - EMA(26) 。
//            今日DEA = (前一日DEA X 8/10 + 今日DIF X 2/10)
//            用(DIF-DEA)*2即为MACD柱状图。
            dif = ema12 - ema26;
            dea = dea * 8f / 10f + dif * 2f / 10f;
            macd = (dif - dea) * 2f;
            point.diff = dif;
            point.dea = dea;
            point.macd = macd;
        }

    }
  • 子试图点击区域的处理,根据点击来却换CJL和MACD

    //CJL和MACL之间的切换
    @Override
    protected void jumpToCJLAndMACL(float downX, float downY) {
        //点击子试图判断
        if (downX > 0 && downX < mWidth) {
            if (downY > mMainHeight + mVolumeTextHeight && downY < mMainHeight + mVolumeTextHeight + mVolumeHeight) {
                Log.d("--->", "x = " + downX + ";" + "y = " + downY);
                isCJL = !isCJL;
                invalidate();
            }

        }

        //点击文字判断
        if (downX > 0 && downX < mBaseTextPaddingLeft + mTextPaint.measureText("MACD") + 10) {
            if (downY > mMainHeight && downY < mMainHeight + mVolumeTextHeight) {
                Log.d("--->", "x = " + downX + ";" + "y = " + downY);
                isCJL = !isCJL;
                invalidate();
            }
        }

    }
  • 给子试图和主视图添加水印
 /**
     * 添加主视图水印
     *
     * @param canvas
     */
    public void drawMainViewLogo(Canvas canvas) {
        if (mBitmapLogo != null) {
            int mLeft = getWidth() / 2 - mBitmapLogo.getWidth() / 2;
            int mTop = mMainHeight / 2 - mBitmapLogo.getHeight() / 2 + mTopPadding;
            canvas.drawBitmap(mBitmapLogo, mLeft, mTop, null);
        }
    }

    /**
     * 添加子视图水印
     *
     * @param canvas
     */
    public void drawChildViewLogo(Canvas canvas) {
        if (mBitmapLogo != null) {
            int mLeft = getWidth() / 2 - mBitmapLogo.getWidth() / 2;
            int mTop = mMainHeight + mVolumeTextHeight + (mVolumeHeight / 2) - mBitmapLogo.getHeight() / 2 + mTopPadding;
            canvas.drawBitmap(mBitmapLogo, mLeft, mTop, null);
        }
    }
  • 分时图基类
public abstract class BaseMinuteView extends View implements GestureDetector.OnGestureListener,
        ScaleGestureDetector.OnScaleGestureListener {
    protected int ONE_MINUTE = 60000;

    protected int mMainHeight; //主视图
    protected int mWidth; //试图宽度
    //可修改
    protected int mVolumeHeight; //子试图高度
    protected int mVolumeTextHeight = 20; //CJL高度

    protected int mTopPadding = 1; //据顶部
    protected int mBottomPadding = 15;//距底部
    //左右padding,允许修改
    protected int mBasePaddingLeft = 50;
    protected int mBasePaddingRight = 50;
    protected float mBaseTextPaddingLeft = 15; //字体据左侧的距离
    protected float mBaseTextPaddingRight = 15;//字体据右侧的距离
    protected int mBaseTimePadding = 5; //下方时间文字

    protected int GridColumns = 5; //列数
    protected int mGridRows = 6; //主试图的行数
    protected int mGridChildRows = 4; //子试图的行数
    protected float mPointWidth; //柱子单位量

    protected boolean isLongPress = false; //是否长按事件
    protected boolean isClosePress = true; //关闭长按时间

    protected boolean mScaleEnable = false; //是否可以缩放
    protected GestureDetectorCompat mDetector;
    protected ScaleGestureDetector mScaleDetector;

    protected boolean isDrawChildView = true; //是否子试图点击事件


    protected long mClickTime = 0; //点击时间

    protected float mScaleY = 1; //Y轴单位量
    protected float mScaleX = 1; //x轴的单位量
    protected float mOldScale = 1.0f; //用来判断当前是否缩放
    protected OnScaleGestureListener mOnScaleGestureListener;

    protected long mTotalTime; //总时间

    protected Paint mGridPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    protected Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    protected int mBackgroundColor;

    protected long mPointCount = 0; //点的个数
    protected float mValueStart;//成交量起始值/昨收价
    protected final List<IMinuteLine> mPoints = new ArrayList<>();
    protected final List<IMinuteTime> mTimes = new ArrayList<>();
    protected Date mMainStartTime;
    protected Date mMainEndTime;
    protected Date mStartTime;
    protected Date mEndTime;

    protected int mMACDClickPoint; //MACD选择点
    private Bitmap mBitmapLogo = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.ic_app_logo);

    private IValueFormatter mVolumeFormatter;
    protected int mIndex = 0; //天数索引

    public BaseMinuteView(Context context) {
        super(context);
        init();
    }

    public BaseMinuteView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public BaseMinuteView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }


    protected void init() {
        mDetector = new GestureDetectorCompat(getContext(), this);
        mScaleDetector = new ScaleGestureDetector(getContext(), this);

        mBackgroundPaint.setDither(true);
        mBackgroundColor = Color.parseColor("#2A2D4F");
        mBackgroundPaint.setColor(mBackgroundColor);

        mGridPaint.setDither(true);
        mGridPaint.setColor(Color.parseColor("#10FFFFFF")); //网格线颜色
        mGridPaint.setStrokeWidth(dp2px(1));

        mVolumeFormatter = new BigValueFormatter();

    }


    /**
     * @param data          数据源
     * @param startTime     显示的开始时间
     * @param endTime       显示的结束时间
     * @param times         休息开始时间
     * @param yesClosePrice 昨收价
     */
    public void initData(Collection<? extends IMinuteLine> data, Date startTime, Date endTime,
                         Collection<? extends IMinuteTime> times, float yesClosePrice, int index) {
        this.mIndex = index;
        if ((data == null && data.isEmpty()) || (times == null && times.isEmpty())) {
            return;
        }
        if (times != null) {
            mTimes.clear();
            mTimes.addAll(times);
            mTotalTime = 0;
        }

        this.mMainStartTime = startTime;
        this.mMainEndTime = endTime;
        if (mMainStartTime.getTime() >= mMainEndTime.getTime())
            throw new IllegalStateException("开始时间不能大于结束时间");

        if (mTimes.size() != 0) {
            for (int i = 0; i < mTimes.size(); i++) {
                mStartTime = mTimes.get(i).getStartDate();
                mEndTime = mTimes.get(i).getEndDate();
                mTotalTime += mEndTime.getTime() - mStartTime.getTime();

            }

        } else {
            mTotalTime = mMainEndTime.getTime() - mMainStartTime.getTime();
        }
        if (mMainStartTime != mTimes.get(0).getStartDate()){
            mMainStartTime = mTimes.get(0).getStartDate();
        }

        setValueStart(yesClosePrice); //设置开始的值(昨日成交量)

        if (data != null) {
            mPoints.clear();
            this.mPoints.addAll(data);
            mPointCount = mPoints.size();
            mMACDClickPoint = mPoints.size() - 1;
        }
        notifyChanged();
    }


    /**
     * 点击, 处理长安时间
     *
     * @param event
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
                mClickTime = System.currentTimeMillis();
                break;

            case MotionEvent.ACTION_MOVE:
                //一个点的时候滑动
                if (event.getPointerCount() == 1) {
                    //长按之后移动
                    if (isLongPress || !isClosePress) {
                        calculateSelectedX(event.getX());
                        invalidate();
                    }
                }
                break;
            case MotionEvent.ACTION_UP:
                if (!isClosePress) {
                    isLongPress = false;
                }
                mMACDClickPoint = mPoints.size() - 1;

//                if (isDrawChildView && isClickPress) {
//                    //当点击时间小于2000ms时,为交互时间
//                    if (System.currentTimeMillis() - mClickTime < 500) {
//                        float downX = event.getX();
//                        float downY = event.getY();
//                        jumpToCJLAndMACL(downX, downY);
//                    }
//                }

                invalidate();
                break;
            case MotionEvent.ACTION_CANCEL:
                if (!isClosePress) {
                    isLongPress = false;
                }
                mMACDClickPoint = mPoints.size() - 1;
                invalidate();
                break;
        }
        this.mDetector.onTouchEvent(event);
        this.mScaleDetector.onTouchEvent(event);
        return true;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
        setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
        int height = heightSpecSize - mTopPadding - mBottomPadding - mVolumeTextHeight;
        if (isDrawChildView) {
            this.mMainHeight = (int) (height * 0.75f);
            this.mVolumeHeight = (int) (height * 0.25f);
        } else {
            this.mMainHeight = (int) (height * 1f);
            this.mVolumeHeight = (int) (height * 0f);
        }
        this.mWidth = getMeasuredWidth();
        notifyChanged();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        int height = h - mTopPadding - mBottomPadding - mVolumeTextHeight;
        if (isDrawChildView) {
            this.mMainHeight = (int) (height * 0.75f);
            this.mVolumeHeight = (int) (height * 0.25f);
        } else {
            this.mMainHeight = (int) (height * 1f);
            this.mVolumeHeight = (int) (height * 0f);
        }
        this.mWidth = w;
        notifyChanged();
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {//横竖屏切换
        super.onConfigurationChanged(newConfig);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        //绘制背景颜色
        canvas.drawColor(mBackgroundColor);
        if (mWidth == 0 || mMainHeight == 0) {
            return;
        }
        drawMainViewLogo(canvas);
        if (isDrawChildView) {
            drawChildViewLogo(canvas);
        }
        drawGird(canvas); //绘制网格
        if (mPoints == null || mPoints.size() == 0) {
            return;
        }
        super.onDraw(canvas);
    }

    /**
     * 添加主视图水印
     *
     * @param canvas
     */
    public void drawMainViewLogo(Canvas canvas) {
        if (mBitmapLogo != null) {
            int mLeft = getWidth() / 2 - mBitmapLogo.getWidth() / 2;
            int mTop = mMainHeight / 2 - mBitmapLogo.getHeight() / 2 + mTopPadding;
            canvas.drawBitmap(mBitmapLogo, mLeft, mTop, null);
        }
    }

    /**
     * 添加子视图水印
     *
     * @param canvas
     */
    public void drawChildViewLogo(Canvas canvas) {
        if (mBitmapLogo != null) {
            int mLeft = getWidth() / 2 - mBitmapLogo.getWidth() / 2;
            int mTop = mMainHeight + mVolumeTextHeight + (mVolumeHeight / 2) - mBitmapLogo.getHeight() / 2 + mTopPadding;
            canvas.drawBitmap(mBitmapLogo, mLeft, mTop, null);
        }
    }


    //绘制网格线
    private void drawGird(Canvas canvas) {
        //先画出坐标轴
        canvas.translate(0, mTopPadding);
        canvas.scale(1, 1);
        //主视图横向的grid
        float rowSpace = mMainHeight / mGridRows;
        for (int i = 1; i <= mGridRows; i++) {
            canvas.drawLine(0, rowSpace * i, mWidth, rowSpace * i, mGridPaint);
        }

        //主试图纵向的grid
        float columnSpace = (mWidth - mBasePaddingLeft - mBasePaddingRight) / GridColumns;
        for (int i = 0; i <= GridColumns; i++) {
            canvas.drawLine(columnSpace * i + mBasePaddingLeft, 0,
                    columnSpace * i + mBasePaddingLeft, mMainHeight, mGridPaint);
        }

        //中间轴线
//        canvas.drawLine(0, rowSpace * mGridRows / 2, mWidth, rowSpace * mGridRows / 2, mGridPaint);

        if (isDrawChildView) {
            //子视图横向的grid
            float rowChildSpace = mVolumeHeight / mGridChildRows;
            for (int i = 0; i <= mGridChildRows; i++) {
                canvas.drawLine(0, mMainHeight + rowChildSpace * i + mVolumeTextHeight, mWidth,
                        mMainHeight + rowChildSpace * i + mVolumeTextHeight, mGridPaint);
            }

            //子试图纵向的grid
            float columnChildSpace = (mWidth - mBasePaddingLeft - mBasePaddingRight) / GridColumns;
            for (int i = 0; i <= GridColumns; i++) {
                canvas.drawLine(columnChildSpace * i + mBasePaddingLeft, mMainHeight + mVolumeTextHeight, columnChildSpace * i + mBasePaddingLeft,
                        mMainHeight + mVolumeHeight + mVolumeTextHeight, mGridPaint);
            }
        }

    }


    /*********************************************抽象方法用于实现继承类**************************************/

    protected abstract void notifyChanged();

    protected abstract void calculateSelectedX(float x);

    protected abstract void jumpToCJLAndMACL(float downX, float downY);


    protected int dp2px(float dp) {
        final float scale = getContext().getResources().getDisplayMetrics().density;
        return (int) (dp * scale + 0.5f);
    }

    protected int sp2px(float spValue) {
        final float fontScale = getContext().getResources().getDisplayMetrics().scaledDensity;
        return (int) (spValue * fontScale + 0.5f);
    }

    /**
     * 设置开始的值 对称轴线
     */
    protected void setValueStart(float valueStart) {
        this.mValueStart = valueStart;
    }

    /*********************************************可设置参数**************************************/

    /**
     * 设置表格行数
     */
    public void setGridRows(int gridRows) {
        if (gridRows < 1) {
            gridRows = 1;
        }
        mGridRows = gridRows;
    }

    /**
     * 设置子试图表格行数
     */
    public void setGridChildRows(int gridRows) {
        if (gridRows < 1) {
            gridRows = 1;
        }
        mGridChildRows = gridRows;
    }

    /**
     * 设置表格列数
     */
    public void setGridColumns(int gridColumns) {
        if (gridColumns < 1) {
            gridColumns = 1;
        }
        GridColumns = gridColumns;
    }


    //是否绘制子试图
    public void setDrawChildView(boolean b) {
        isDrawChildView = b;
        if (!isDrawChildView) {
            mVolumeHeight = 0; //子试图高度
            mVolumeTextHeight = 0; //CJL高度
        }
    }

    //设置子试图高度
    public void setVolumeHeight(int height) {
        mVolumeHeight = dp2px(height);
    }

    //设置CJL高度
    public void setVolumeTextHeight(int height) {
        mVolumeTextHeight = dp2px(height);
    }

    //设置主试图高度
    public void setMainHeight(int height) {
        mMainHeight = dp2px(height);
    }

    /**
     * 获取最大能有多少个点
     */
    public long getMaxPointCount(int count) {
        return mTotalTime / ONE_MINUTE;
    }

    /**
     * 添加一个点
     */
    public void addPoint(IMinuteLine point) {
        mPoints.add(point);
        notifyChanged();
    }

    /**
     * 修改某个点的值
     *
     * @param position 索引值
     */
    public void changePoint(int position, IMinuteLine point) {
        mPoints.set(position, point);
        notifyChanged();
    }


    /**
     * 根据索引获取点
     */
    public IMinuteLine getItem(int position) {
        return mPoints.get(position);
    }

    /**
     * 获取点的个数
     */
    private int getItemSize() {
        return mPoints.size();
    }

    /**
     * 刷新最后一个点
     */
    public void refreshLastPoint(IMinuteLine point) {
        changePoint(getItemSize() - 1, point);
    }

    //是否可以缩放
    public void setScaleEnable(boolean scaleEnable) {
        this.mScaleEnable = scaleEnable;
    }


    /**
     * 设置成交量格式化器
     *
     * @param volumeFormatter {@link IValueFormatter} 成交量格式化器
     */
    public void setVolumeFormatter(IValueFormatter volumeFormatter) {
        mVolumeFormatter = volumeFormatter;
    }


    //设置时间单位; 1代表一分钟, 2代表两分钟
    public void setOpenMinute(int number) {
        ONE_MINUTE = number * 60000;
    }

    public void setLongPress(boolean longPress) {
        isLongPress = longPress;
    }

    public void setClosePress(boolean closePress) {
        isClosePress = closePress;
    }

    /******************************长按,点击手势*****************************************/

    // 单击, 触摸屏按下时立刻触发
    @Override
    public boolean onDown(MotionEvent e) {
        Log.i("--->", "onDown");
        return false;
    }

    //短按, 触摸屏按下后片刻后抬起,会触发这个手势,如果迅速抬起则不会
    @Override
    public void onShowPress(MotionEvent e) {
    }

    //抬起, 手指离开触摸屏时触发(长按、滚动、滑动时,不会触发这个手势)
    @Override
    public boolean onSingleTapUp(MotionEvent e) {
        switch (e.getAction()) {
            case MotionEvent.ACTION_DOWN:
                Log.i("--->", "ACTION_DOWN");
                break;
            case MotionEvent.ACTION_UP: //双指点击时不会触发
                Log.i("--->", "ACTION_UP");
                if (isClosePress) {
                    if (isDrawChildView) {
                        //当点击时间小于2000ms时,为交互时间
                        if (System.currentTimeMillis() - mClickTime < 500) {
                            float downX = e.getX();
                            float downY = e.getY();
                            jumpToCJLAndMACL(downX, downY);
                        }
                    }
                } else {
                    isClosePress = true;
                }

                break;
            case MotionEvent.ACTION_MOVE:
                Log.i("--->", "ACTION_MOVE");
                break;
        }
        return false;
    }

    //滚动, 触摸屏按下后移动
    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        return false;
    }

    //长按, 触摸屏按下后既不抬起也不移动,过一段时间后触发
    @Override
    public void onLongPress(MotionEvent e) {
        isLongPress = true;
        isClosePress = false;
        calculateSelectedX(e.getX());
        invalidate();
    }

    //滑动, 触摸屏按下后快速移动并抬起,会先触发滚动手势,跟着触发一个滑动手势
    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        return false;
    }


    /******************************缩放手势*****************************************/
    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        if (!isScaleEnable()) {
            return false;
        }
        mOldScale = 1.0f;
        float f = detector.getScaleFactor();


        mOldScale *= f;


//        if (mScaleX < mScaleXMin) {
//            mScaleX = mScaleXMin;
//        } else if (mScaleX > mScaleXMax) {
//            mScaleX = mScaleXMax;
//        } else {
//            onScaleChanged(mScaleX, oldScale);
//        }
        return true;
    }

    @Override
    public boolean onScaleBegin(ScaleGestureDetector detector) {
        return true;
    }

    @Override
    public void onScaleEnd(ScaleGestureDetector detector) {
        if (isClosePress) {
            if (mOldScale > 1.0f) { //放大
                mOnScaleGestureListener.setLoseNumber();

            } else { //收缩
                mOnScaleGestureListener.setAddNumber();

            }
        }

    }

    protected void onScaleChanged(float scale, float oldScale) {
        invalidate();
    }


    protected boolean isScaleEnable() {
        return mScaleEnable;
    }


    public void setViewScaleGestureListener(OnScaleGestureListener listener) {
        if (listener != null) {
            this.mOnScaleGestureListener = listener;
        }
    }

    public interface OnScaleGestureListener {
        void setAddNumber();

        void setLoseNumber();

    }
}

GestureDetectorCompat 触摸手势:主要是用来判断单指点击屏幕和双指点击屏幕从而来区分点击切换子试图还是进行缩放操作。
**ScaleGestureDetector **主要根据onScale()、onScaleBegin()和onScaleEnd()这三个方法来判断试图是放大还是缩小。

    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        if (!isScaleEnable()) {
            return false;
        }
        mOldScale = 1.0f;
        float f = detector.getScaleFactor();
        mOldScale *= f;
        return true;
    }

    @Override
    public boolean onScaleBegin(ScaleGestureDetector detector) {
        return true;
    }

    @Override
    public void onScaleEnd(ScaleGestureDetector detector) {
        if (isClosePress) {
            if (mOldScale > 1.0f) { //放大
                mOnScaleGestureListener.setLoseNumber();

            } else { //收缩
                mOnScaleGestureListener.setAddNumber();

            }
        }

    }
  • 分时图重点实现试图展示效果:
public class MinuteTimeView extends BaseMinuteView {
    private Paint mAvgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    private Paint mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); //文字
    private Paint mTextMACDPaint = new Paint(Paint.ANTI_ALIAS_FLAG); //文字
    private Paint mTextLeftPaint = new Paint(Paint.ANTI_ALIAS_FLAG); //左边文字
    private Paint mTextReightPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//右边文字
    private Paint mLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG); //轴线
    private Paint mTextBottomPaint = new Paint(Paint.ANTI_ALIAS_FLAG); //下边文字
    private Paint mPricePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private Paint mPriceAveragePaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    private Paint mVolumePaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    private Paint mSelectorBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private Paint mSelectorTitlePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private Paint mSelectorTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    private float mValueMin; //最大值
    private float mValueMax; //最小值

    private float mVolume = 0; //成交量
    private float mInterest = 0; //持仓量
    private float mVolumeMax = 0; //最大成交量
    private float mVolumeMin = 0; //最小成交量
    private float mInterestMax = 0; //最大持仓量
    private float mInterestMin = 0; //最小持仓量

    private float mMACDValueY = 0;//MACD Y轴

    private long mCount = 0;

    private float mVolumeTimeScaleY; //各时间点成交量缩放比
    private float mInterestTimeScaleY; //各时间点持仓量缩放比
    private float mMACDLineScaleY; //MACD指标线的缩放量
    private float mTextSize = 10;

    private int selectedIndex;
    private boolean isCJL = true; //是否为CJL图,默认true


    public MinuteTimeView(Context context) {
        super(context);
        initData();
    }

    public MinuteTimeView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initData();
    }

    public MinuteTimeView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initData();
    }


    private void initData() {
        setDrawChildView(true);
        mTopPadding = dp2px(mTopPadding);
        mBottomPadding = dp2px(mBottomPadding);

        mTextSize = sp2px(mTextSize);
        mVolumeHeight = dp2px(mVolumeHeight);
        mVolumeTextHeight = dp2px(mVolumeTextHeight);

        mTextPaint.setDither(true);
        mTextPaint.setColor(Color.parseColor("#6774FF"));
        mTextPaint.setTextSize(sp2px(11));
        mTextPaint.setStrokeWidth(dp2px(0.5f));

        mTextMACDPaint.setDither(true);
        mTextMACDPaint.setColor(Color.parseColor("#6774FF"));
        mTextMACDPaint.setTextSize(sp2px(10));
        mTextMACDPaint.setStrokeWidth(dp2px(0.5f));

        mTextLeftPaint.setDither(true);
        mTextLeftPaint.setColor(Color.parseColor("#B1B2B6"));
        mTextLeftPaint.setTextSize(mTextSize);
        mTextLeftPaint.setStrokeWidth(dp2px(0.5f));

        mTextReightPaint.setDither(true);
        mTextReightPaint.setColor(Color.parseColor("#B1B2B6"));
        mTextReightPaint.setTextSize(mTextSize);
        mTextReightPaint.setStrokeWidth(dp2px(0.5f));

        mTextBottomPaint.setDither(true);
        mTextBottomPaint.setColor(Color.parseColor("#6A798E"));
        mTextBottomPaint.setTextSize(mTextSize);
        mTextBottomPaint.setStrokeWidth(dp2px(0.5f));

        mLinePaint.setDither(true);
        mLinePaint.setColor(Color.parseColor("#6774FF")); //轴线
        mLinePaint.setTextSize(mTextSize);
        mLinePaint.setStrokeWidth(dp2px(0.7f));

        mAvgPaint.setDither(true);
        mAvgPaint.setColor(Color.parseColor("#90A901"));
        mAvgPaint.setStrokeWidth(dp2px(0.5f));
        mAvgPaint.setTextSize(mTextSize);

        mPricePaint.setDither(true);
        mPricePaint.setColor(Color.parseColor("#FF6600"));
        mPricePaint.setStrokeWidth(dp2px(0.5f));
        mPricePaint.setTextSize(mTextSize);

        mPriceAveragePaint.setDither(true);
        mPriceAveragePaint.setColor(Color.parseColor("#EFD521"));
        mPriceAveragePaint.setStrokeWidth(dp2px(0.5f));

        mSelectorBackgroundPaint.setDither(true);
        mSelectorBackgroundPaint.setColor(Color.parseColor("#4F5490"));

        mSelectorTitlePaint.setDither(true);
        mSelectorTitlePaint.setColor(Color.parseColor("#9EB2CD"));
        mSelectorTitlePaint.setTextSize(sp2px(10));

        mSelectorTextPaint.setDither(true);
        mSelectorTextPaint.setColor(Color.parseColor("#E7EDF5"));
        mSelectorTextPaint.setTextSize(sp2px(13));

        mVolumePaint.setDither(true);
        mVolumePaint.setColor(ContextCompat.getColor(getContext(), R.color.chart_red));

    }


    @Override
    protected void calculateSelectedX(float x) {
//        selectedIndex = (int) (x * 1f / getX(mPoints.size() - 1) * (mPoints.size() - 1) + mPointWidth * 0.2f);
        selectedIndex = (int) (x * 1f / getX(mPoints.size() - 1) * (mPoints.size() - 1));
        if (selectedIndex < 0) {
            selectedIndex = 0;
        }
        if (selectedIndex > mPoints.size() - 1) {
            selectedIndex = mPoints.size() - 1;
        }
    }


    /**
     * 根据索引获取x的值
     */
    private float getX(int position) {
        mCount = 0;
        if (mPoints.size() == 0 || mPoints == null || position == -1) {
            return 0;
        }
        Long dateTime = mPoints.get(position).getDate().getTime();
        for (int i = 0; i < mTimes.size(); i++) {
            Long startTime = mTimes.get(i).getStartDate().getTime();
            Long endTime = mTimes.get(i).getEndDate().getTime();
            if (dateTime >= startTime && dateTime <= endTime) {
                mCount += (dateTime - startTime) / ONE_MINUTE;
                break;
            } else {
                mCount += (endTime - startTime) / ONE_MINUTE;
            }
        }
        float c = mCount * mScaleX;
        return mCount * mScaleX;
    }


    /**
     * 当数据发生变化时调用
     */
    @Override
    protected void notifyChanged() {
        mMACDValueY = 0;
        mValueMax = mValueMin = 0;
        mVolumeMax = mVolume = mVolumeMin = 0;
        mInterestMax = mInterest = mInterestMin = 0;

        if (mPoints.size() <= 0) {
            return;
        }
        if (mPoints.size() > 0) {

            mValueMax = mPoints.get(0).getLast();
            mValueMin = mPoints.get(0).getLast();

            mVolumeMax = mPoints.get(0).getVolume();//成交量
            mVolume = mPoints.get(0).getVolume();
            mVolumeMin = mPoints.get(0).getVolume();

            mInterestMax = mPoints.get(0).getInterest(); //持仓量
            mInterest = mPoints.get(0).getInterest();
            mInterestMin = mPoints.get(0).getInterest();
        }

        for (int i = 0; i < mPoints.size(); i++) {
            IMinuteLine point = mPoints.get(i);

            mValueMax = Math.max(mValueMax, point.getLast());
            mValueMin = Math.min(mValueMin, point.getLast());

            mVolumeMax = Math.max(mVolumeMax, point.getVolume());
            mVolumeMin = Math.max(mVolumeMin, point.getVolume());
            mInterestMax = Math.max(mInterestMax, point.getInterest());
            mInterestMin = Math.min(mInterestMin, point.getInterest());

            //MACD Y轴
            mMACDValueY = Math.max(mMACDValueY, Math.max(Math.abs(point.getDea()),
                    Math.max(Math.abs(point.getDiff()), Math.abs(point.getMacd()))));
            Log.i("mMACDValueY :", mMACDValueY + "--; --" + i);

        }
        mMACDValueY = StrUtil.getFaveMultipleMinimum(Long.parseLong(StrUtil.getPositiveNumber(mMACDValueY)));
        Log.i("mMACDValue :", mMACDValueY + "");

        if (mValueMin > mValueStart) {
            mValueStart = (mValueMax + mValueMin) / 2;
        }

        //最大值和开始值的单位差值
        float offsetValueMax = (Math.abs(mValueMax - mValueStart)) / (mGridRows / 2);
        float offsetValueMin = (Math.abs(mValueStart - mValueMin)) / (mGridRows / 2);

        //以开始的点为中点值   上下间隙多出20%
        float offset = (offsetValueMax > offsetValueMin ? offsetValueMax : offsetValueMin) * 1.2f;
        //坐标轴高度以开始的点对称
        mValueMax = mValueStart + offset * (mGridRows / 2);
        mValueMin = mValueStart - offset * (mGridRows / 2);
        //y轴的缩放值
        mScaleY = mMainHeight / (mValueMax - mValueMin);

        //判断最大值和最小值是否一致
        if (mValueMax == mValueMin) {
            //当最大值和最小值都相等的时候 分别增大最大值和 减小最小值
            mValueMax += Math.abs(mValueMax * 0.05f);
            mValueMin -= Math.abs(mValueMax * 0.05f);
            if (mValueMax == 0) {
                mValueMax = 1;
            }
        }

        if (mVolumeMax == 0) {
            mVolumeMax = 1;
        }


        //CJL左轴的缩放值
        mVolumeMin = 0;
        if (mVolumeMin < 0) {
            mVolumeMin = 0;
        }
        mVolumeMax = StrUtil.getFaveMultipleMinimum((long) mVolumeMax);
        mVolumeTimeScaleY = mVolumeHeight / Math.abs(mVolumeMax - mVolumeMin);


        //CJL右轴的缩放值
        float interest = Math.max(Math.abs(mInterestMax - mInterest), Math.abs(mInterest - mInterestMin));
        mInterestMax = mInterest + interest;
        mInterestMin = mInterest - interest;
        if (mInterestMin < 0) {
            mInterestMin = 0;
        }
        mInterestMax = StrUtil.getZeroMultipleMinimum((long) mInterestMax, 1);
        mInterestMin = StrUtil.getZeroMultipleMinimum((long) mInterestMin, 2);
        mInterestTimeScaleY = mVolumeHeight / Math.abs(mInterestMax - mInterestMin);
        Log.i("---> InterestScaleY ", mInterestTimeScaleY + "");

        //MACD右轴的缩放值
        mMACDLineScaleY = mVolumeHeight / (2 * mMACDValueY);

        //x轴的缩放值
        mScaleX = (float) (mWidth - mBaseTimePadding * 2) / getMaxPointCount(1);

        //设置主状图的宽度
        mPointWidth = (float) (mWidth - mBaseTimePadding * 2) / getMaxPointCount(1);
        mVolumePaint.setStrokeWidth(dp2px((float) 0.5));

        invalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mWidth == 0 || mMainHeight == 0 || mPoints == null || mPoints.size() == 0) {
            return;
        }

        //绘制平均线和成交价
        if (mPoints.size() > 0) {
            mPricePaint.setColor(getResources().getColor(R.color.chart_FFFFFF));
            IMinuteLine lastPoint = mPoints.get(0);
            float lastX = getX(0);
            for (int i = 0; i < mPoints.size(); i++) {
                IMinuteLine curPoint = mPoints.get(i);
                float curX = getX(i);

                canvas.drawLine(lastX + mBaseTimePadding - mScaleX / 2,
                        getY(lastPoint.getLast()),
                        curX + mBaseTimePadding - mScaleX / 2,
                        getY(curPoint.getLast()),
                        mPricePaint); //成交价

//                canvas.drawLine(lastX + mBaseTimePadding - mScaleX / 2,
//                        getY(lastPoint.getAverage()),
//                        curX + mBaseTimePadding - mScaleX / 2,
//                        getY(curPoint.getAverage()),
//                        mPriceAveragePaint); //均价线

                if (isDrawChildView) {
                    if (isCJL) {
                        //CJL成交量(柱状图)
                        if (curPoint.getOpen() < curPoint.getClose()) {
                            mVolumePaint.setColor(getResources().getColor(R.color.color_positive_value));

                        } else if (curPoint.getOpen() > curPoint.getClose()) {
                            mVolumePaint.setColor(getResources().getColor(R.color.color_negative_value));

                        } else if (curPoint.getOpen() == curPoint.getClose()) {
                            mVolumePaint.setColor(getResources().getColor(R.color.chart_text));
                        }

                        canvas.drawLine(curX + mBaseTimePadding - mPointWidth * 0.5f,
                                mMainHeight + mVolumeTextHeight + mVolumeHeight,
                                curX + mBaseTimePadding - mPointWidth * 0.5f,
                                getVolumeCJLY(curPoint.getVolume()),
                                mVolumePaint);

                    } else {
                        //MACD成交量(柱状图)
                        if (curPoint.getMacd() > 0) {
                            mVolumePaint.setColor(getResources().getColor(R.color.color_positive_value));
                            canvas.drawLine(curX + mBaseTimePadding - mPointWidth * 0.5f,
                                    getMACDLineY(0),
                                    curX + mBaseTimePadding - mPointWidth * 0.5f,
                                    getMACDLineY(curPoint.getMacd()),
                                    mVolumePaint);

                        } else if (curPoint.getMacd() < 0) {
                            mVolumePaint.setColor(getResources().getColor(R.color.color_negative_value));
                            canvas.drawLine(curX + mBaseTimePadding - mPointWidth * 0.5f,
                                    getMACDLineY(0),
                                    curX + mBaseTimePadding - mPointWidth * 0.5f,
                                    getMACDLineY(curPoint.getMacd()),
                                    mVolumePaint);

                        }

                    }
                }

                //给上一个只赋值
                lastPoint = curPoint;
                lastX = curX;
            }
        }


        drawText(canvas); //绘制文字

        //画指示线
        if (isLongPress || !isClosePress) {
            if (selectedIndex >= mPoints.size() || selectedIndex < 0 || mPoints.size() == 0) {
                return;
            }
            IMinuteLine point = mPoints.get(selectedIndex);
            float x = getX(selectedIndex) + mBaseTimePadding - mPointWidth * 0.5f;
            //轴线
            canvas.drawLine(x, 0, x, mMainHeight + mVolumeHeight + mVolumeTextHeight, mLinePaint);//Y
//            canvas.drawLine(0, getY(point.getLast()), mWidth, getY(point.getLast()), mLinePaint);//X


            drawMainSelector(selectedIndex, point, canvas);

        }
    }


    /**
     * draw选择器
     *
     * @param canvas
     */

    private void drawMainSelector(int selectedIndex, IMinuteLine point, Canvas canvas) {
        Paint.FontMetrics metrics = mTextLeftPaint.getFontMetrics();
        float textHeight = metrics.descent - metrics.ascent;

        float padding = DensityUtil.dp2px(5);
        float margin = DensityUtil.dp2px(5);
        float width = 0;
        float left = 5;
        float top = 10;
        float bottom = 10;

        List<String> strings = new ArrayList<>();
        strings.add(DateUtil.getStringDateByLong(point.getDate().getTime(), 8));
        strings.add("价格");
        strings.add(point.getLast() + "");
        strings.add("均价");
        strings.add(point.getAverage() + "");

        strings.add("涨跌");
        strings.add(point.getUpdown());
        strings.add(point.getPercent());

        strings.add("持仓量");
        strings.add(point.getInterest() + "");
        strings.add(point.getChgInterest());

        strings.add("成交量");
        strings.add(point.getVolume() + "");
        strings.add(point.getChgVolume());//成交量变化

//        strings.add("成交价");
//        strings.add(point.getLast() + "");

        for (int i = 0; i < strings.size(); i++) {
            width = Math.max(width, mSelectorTextPaint.measureText(strings.get(i)));
        }
        width = width + dp2px(6) * 2;

        float x = getX(selectedIndex);
        if (x > mWidth / 2) {
            left = margin;
        } else {
            left = mWidth - width - margin;
        }
        float height = top + padding * 2 + (textHeight - metrics.bottom - metrics.top) / 2 +
                (textHeight + padding) * (strings.size() - 1);
        RectF r = new RectF(left, top, left + width, top + height + bottom);
        canvas.drawRoundRect(r, padding, padding, mSelectorBackgroundPaint);

        float y = top + padding * 2 + (textHeight - metrics.bottom - metrics.top) / 2;

//        if (x > mWidth / 2) {
//            left = margin + padding;
//            mSelectorTextPaint.setTextAlign(Paint.Align.LEFT);
//            mSelectorTitlePaint.setTextAlign(Paint.Align.LEFT);
//        } else {
//            left = mWidth - margin - padding;
//            mSelectorTextPaint.setTextAlign(Paint.Align.RIGHT);
//            mSelectorTitlePaint.setTextAlign(Paint.Align.RIGHT);
//        }
        for (String s : strings) {
            if (StrUtil.isTimeText(s)) {
                mSelectorTextPaint.setColor(getResources().getColor(R.color.color_text_positive_paint));
                canvas.drawText(s, left + padding, y, mSelectorTextPaint);

            } else if (StrUtil.isChinaText(s)) {
                canvas.drawText(s, left + padding, y, mSelectorTitlePaint);

            } else {
                if (StrUtil.isPositiveOrNagativeNumberText(s)) {
                    mSelectorTextPaint.setColor(getResources().getColor(R.color.color_negative_value));
                    canvas.drawText(s, left + padding, y, mSelectorTextPaint);

                } else if (s.equals("- -")) {
                    mSelectorTextPaint.setColor(getResources().getColor(R.color.color_text_positive_paint));
                    canvas.drawText(s, left + padding, y, mSelectorTextPaint);

                } else {
                    mSelectorTextPaint.setColor(getResources().getColor(R.color.color_text_positive_paint));
                    canvas.drawText(s, left + padding, y, mSelectorTextPaint);
                }
            }

            y += textHeight + padding;
        }

        //绘制 MACD, DIFF, DEA, STICK 的指标
        if (isDrawChildView) {
            //GJL
            if (!isCJL) {
                drawMACDText(selectedIndex, point, canvas);
                invalidate();
            }
        }


    }

    private void drawText(Canvas canvas) {
        Paint.FontMetrics fm = mTextLeftPaint.getFontMetrics();
        float textHeight = fm.descent - fm.ascent;
        float baseLine = (textHeight - fm.bottom - fm.top) / 2;

        float rowValue = (mValueMax - mValueMin) / mGridRows;
        float rowSpace = mMainHeight / mGridRows;
        //画左边的值
        mTextLeftPaint.setColor(getResources().getColor(R.color.color_positive_value));
        canvas.drawText(StrUtil.getOneDecimals(mValueStart + rowValue * 3), mBaseTextPaddingLeft, baseLine, mTextLeftPaint); //绘制最大值

        mTextLeftPaint.setColor(getResources().getColor(R.color.color_negative_value));
        canvas.drawText(StrUtil.getOneDecimals(mValueStart - rowValue * 3), mBaseTextPaddingLeft, mMainHeight - textHeight + baseLine, mTextLeftPaint); //绘制最小值

        for (int i = 0; i < 3; i++) {

            if (i == 0) {
                String text = StrUtil.getOneDecimals(mValueStart + rowValue * 1.5);
                mTextLeftPaint.setColor(getResources().getColor(R.color.color_positive_value));
                canvas.drawText(text, mBaseTextPaddingLeft, (float) (rowSpace * 1.5 + baseLine / 2), mTextLeftPaint);

            } else if (i == 1) {
                String text = StrUtil.getOneDecimals(mValueStart);
                mTextLeftPaint.setColor(getResources().getColor(R.color.color_central_paint));
                canvas.drawText(text, mBaseTextPaddingLeft, fixTextY(rowSpace * 3), mTextLeftPaint);

            } else if (i == 2) {
                String text = StrUtil.getOneDecimals(mValueStart - rowValue * 1.5);
                mTextLeftPaint.setColor(getResources().getColor(R.color.color_negative_value));
                canvas.drawText(text, mBaseTextPaddingLeft,
                        (float) (mMainHeight - textHeight / 2 - rowSpace * 1.5 + baseLine / 2), mTextLeftPaint);

            }
        }

        //画右边的值
        mTextReightPaint.setTextAlign(Paint.Align.RIGHT);
        mTextReightPaint.setColor(getResources().getColor(R.color.color_positive_value));
        String text = StrUtil.subAddAndSubMark(StrUtil.floatToString((mValueMax - mValueStart) * 100f / mValueStart, 2) + "%");
        canvas.drawText("+" + text, mWidth - mBaseTextPaddingRight, baseLine, mTextReightPaint);

        mTextReightPaint.setColor(getResources().getColor(R.color.color_negative_value));
        text = StrUtil.subAddAndSubMark(StrUtil.floatToString(Math.abs(mValueMin - mValueStart) * 100f / mValueStart, 2) + "%");
        canvas.drawText("-" + text, mWidth - mBaseTextPaddingRight,
                mMainHeight - textHeight + baseLine, mTextReightPaint);

        for (int i = 0; i < 3; i++) {
            if (i == 0) {
                text = StrUtil.subAddAndSubMark(StrUtil.floatToString((rowValue * 1.5f) * 100f / mValueStart, 2) + "%");
                mTextReightPaint.setColor(getResources().getColor(R.color.color_positive_value));
                canvas.drawText("+" + text, mWidth - mBaseTextPaddingRight,
                        (float) (rowSpace * 1.5 + baseLine / 2), mTextReightPaint);

            } else if (i == 1) {
                text = "0";
                mTextReightPaint.setColor(getResources().getColor(R.color.color_central_paint));
                canvas.drawText(text, mWidth - mBaseTextPaddingRight,
                        fixTextY(rowSpace * 3), mTextReightPaint);

            } else if (i == 2) {
                text = StrUtil.subAddAndSubMark(StrUtil.floatToString((rowValue * 1.5f) * 100f / mValueStart, 2) + "%");
                mTextReightPaint.setColor(getResources().getColor(R.color.color_negative_value));
                canvas.drawText("-" + text, mWidth - mBaseTextPaddingRight,
                        (float) (mMainHeight - textHeight / 2 - rowSpace * 1.5 + baseLine / 2), mTextReightPaint);
            }
        }


        //画时间
        float y = mMainHeight + mVolumeHeight + mVolumeTextHeight + baseLine;
        mTextBottomPaint.setTextAlign(Paint.Align.LEFT);
        mCount = 0;

        if (mIndex == 1) {
            canvas.drawText(DateUtil.getStringDateByLong(mMainStartTime.getTime(), 8),
                    mBaseTimePadding, y, mTextBottomPaint); //起始时间

            for (int i = 0; i < mTimes.size() - 1; i++) {
                mCount += (mTimes.get(i).getEndDate().getTime() - mTimes.get(i).getStartDate().getTime()) / ONE_MINUTE;
                float x = mScaleX * mCount;
                String dataStr = DateUtil.getStringDateByLong(mTimes.get(i + 1).getStartDate().getTime(), 8);
                if (dataStr.equals("04:30")) continue;
                canvas.drawText(dataStr,
                        mBaseTimePadding + x - mTextBottomPaint.measureText(DateUtil.getStringDateByLong(mMainStartTime.getTime(), 8)),
                        y, mTextBottomPaint); //中间时间

            }
            canvas.drawText(DateUtil.getStringDateByLong(mMainEndTime.getTime(), 8),
                    mWidth - mBaseTimePadding - mTextBottomPaint.measureText(DateUtil.getStringDateByLong(mMainEndTime.getTime(), 8)),
                    y, mTextBottomPaint);//结束时间

        } else { //大于1天时
            if (mTimes.size() / 4 == mIndex && mTimes.size() != mIndex) {
                canvas.drawText(DateUtil.getStringDateByLong(mMainStartTime.getTime(), 9), mBaseTimePadding, y, mTextBottomPaint); //起始时间
                for (int i = 0; i < mTimes.size() - 1; i++) {
                    mCount += (mTimes.get(i).getEndDate().getTime() - mTimes.get(i).getStartDate().getTime()) / ONE_MINUTE;
                    float x = mScaleX * mCount;
                    String dataStr = DateUtil.getStringDateByLong(mTimes.get(i).getStartDate().getTime(), 8);
                    if (dataStr.equals("09:00")) { //上期所
                        canvas.drawText(DateUtil.getStringDateByLong(mTimes.get(i).getStartDate().getTime(), 9),
                                mBaseTimePadding + x - mTextBottomPaint.measureText(DateUtil.getStringDateByLong(mMainStartTime.getTime(), 9)) / 3,
                                y, mTextBottomPaint);

                    }
                }

            } else if (mTimes.size() / 2 == mIndex && mTimes.size() != mIndex) {
                canvas.drawText(DateUtil.getStringDateByLong(mMainStartTime.getTime(), 9), mBaseTimePadding, y, mTextBottomPaint); //起始时间
                for (int i = 0; i < mTimes.size() - 1; i++) {
                    if (i + 1 == mTimes.size()) return;
                    mCount += (mTimes.get(i).getEndDate().getTime() - mTimes.get(i).getStartDate().getTime()) / ONE_MINUTE;
                    float x = mScaleX * mCount;
                    String dataStr = DateUtil.getStringDateByLong(mTimes.get(i + 1).getStartDate().getTime(), 8);
                    if (dataStr.equals("09:30")) { // //上证指数 (INDEX)
                        canvas.drawText(DateUtil.getStringDateByLong(mTimes.get(i + 1).getStartDate().getTime(), 9),
                                mBaseTimePadding + x - mTextBottomPaint.measureText(DateUtil.getStringDateByLong(mMainStartTime.getTime(), 9)) / 3,
                                y, mTextBottomPaint);

                    } else if (dataStr.equals("06:00")) {  //上证指数 (INDEX)
                        canvas.drawText(DateUtil.getStringDateByLong(mTimes.get(i + 1).getStartDate().getTime(), 9),
                                mBaseTimePadding + x - mTextBottomPaint.measureText(DateUtil.getStringDateByLong(mMainStartTime.getTime(), 9)) / 3,
                                y, mTextBottomPaint);

                    }
                }

            } else if (mTimes.size() == mIndex) {
                canvas.drawText(DateUtil.getStringDateByLong(mMainStartTime.getTime(), 9), mBaseTimePadding, y, mTextBottomPaint); //起始时间
                for (int i = 0; i < mTimes.size() - 1; i++) {
                    if (i + 1 == mTimes.size()) return;

                    mCount += (mTimes.get(i).getEndDate().getTime() - mTimes.get(i).getStartDate().getTime()) / ONE_MINUTE;
                    float x = mScaleX * mCount;
                    String dataStr = DateUtil.getStringDateByLong(mTimes.get(i + 1).getStartDate().getTime(), 8);
                    if (dataStr.equals("08:00")) {  //LME
                        canvas.drawText(DateUtil.getStringDateByLong(mTimes.get(i + 1).getStartDate().getTime(), 9),
                                mBaseTimePadding + x - mTextBottomPaint.measureText(DateUtil.getStringDateByLong(mMainStartTime.getTime(), 9)) / 3,
                                y, mTextBottomPaint);

                    } else if (dataStr.equals("21:30")) {  //上证指数 (INDEX)
                        canvas.drawText(DateUtil.getStringDateByLong(mTimes.get(i + 1).getStartDate().getTime(), 9),
                                mBaseTimePadding + x - mTextBottomPaint.measureText(DateUtil.getStringDateByLong(mMainStartTime.getTime(), 9)) / 3,
                                y, mTextBottomPaint);

                    } else if (dataStr.equals("09:30")) {  //外汇 (INDEX)
                        canvas.drawText(DateUtil.getStringDateByLong(mTimes.get(i + 1).getStartDate().getTime(), 9),
                                mBaseTimePadding + x - mTextBottomPaint.measureText(DateUtil.getStringDateByLong(mMainStartTime.getTime(), 9)) / 3,
                                y, mTextBottomPaint);

                    }
                }

            }


        }

        if (isDrawChildView) {
            //GJL
            if (isCJL) {
                drawCJL(canvas);
            } else {
                drawMACD(canvas);
            }
        }

    }


    //MACD , 存在问题
    private void drawMACD(Canvas canvas) {
        //上面文字
        mTextPaint.setColor(getResources().getColor(R.color.color_child_text));
        Paint.FontMetrics fm = mTextPaint.getFontMetrics();
        float textHeight = fm.descent - fm.ascent;
        float baseLine = (textHeight - fm.bottom - fm.top) / 2;
        mTextPaint.setTextAlign(Paint.Align.LEFT);
        canvas.drawText("MACD", mBaseTextPaddingLeft, mMainHeight + mVolumeTextHeight / 2 + textHeight - baseLine, mTextPaint); //存在问题??

        //绘制 MACD, DIFF, DEA, STICK 的指标
        drawMACDText(0, mPoints.get(mMACDClickPoint), canvas);

        //左边文字
        float rowChildSpace = mVolumeHeight / mGridChildRows;

        mTextPaint.setColor(getResources().getColor(R.color.color_candle_paint));
        canvas.drawText(StrUtil.getPositiveNumber(mMACDValueY), mBaseTextPaddingLeft,
                mMainHeight + baseLine + mVolumeTextHeight, mTextPaint); //绘制最大值
        canvas.drawText("0", mBaseTextPaddingLeft,
                mMainHeight + mVolumeTextHeight + fixTextY(rowChildSpace * 2), mTextPaint); //中间值
        canvas.drawText(StrUtil.getPositiveNumber(-mMACDValueY), mBaseTextPaddingLeft,
                mMainHeight - textHeight + baseLine + mVolumeTextHeight + mVolumeHeight, mTextPaint); //绘制最小值


        //DIFF线
        if (mPoints.size() > 0) {
            mPricePaint.setColor(getResources().getColor(R.color.chart_FFFFFF));
            mPricePaint.setStyle(Paint.Style.STROKE);
            IMinuteLine lastPoint = mPoints.get(0);
            float lastX = getX(0);


            for (int i = 0; i < mPoints.size(); i++) {
                IMinuteLine curPoint = mPoints.get(i);
                float curX = getX(i);

//                Log.i("diff --> :" , curPoint.getDiff() + "");
                canvas.drawLine(lastX + mBaseTimePadding - mScaleX / 2,
                        getMACDLineY(lastPoint.getDiff()),
                        curX + mBaseTimePadding - mScaleX / 2,
                        getMACDLineY(curPoint.getDiff()),
                        mPricePaint); //成交价

                //给上一个只赋值
                lastPoint = curPoint;
                lastX = curX;
            }
        }

        //DEA线
        if (mPoints.size() > 0) {
            mPricePaint.setColor(getResources().getColor(R.color.color_cjl_line_paint));
            IMinuteLine lastPoint = mPoints.get(0);
            float lastX = getX(0);
            for (int i = 0; i < mPoints.size(); i++) {
                IMinuteLine curPoint = mPoints.get(i);
                float curX = getX(i);

//                Log.i("dea --> :" , curPoint.getDea() + "");
                canvas.drawLine(lastX + mBaseTimePadding - mScaleX / 2,
                        getMACDLineY(lastPoint.getDea()),
                        curX + mBaseTimePadding - mScaleX / 2,
                        getMACDLineY(curPoint.getDea()),
                        mPricePaint); //成交价

                //给上一个只赋值
                lastPoint = curPoint;
                lastX = curX;
            }
        }

    }

    //绘制 MACD, DIFF, DEA, STICK 的指标
    private void drawMACDText(int selectedIndex, IMinuteLine point, Canvas canvas) {
        mMACDClickPoint = selectedIndex;
        Paint.FontMetrics fm = mTextMACDPaint.getFontMetrics();
        float textHeight = fm.descent - fm.ascent;
        float baseLine = (textHeight - fm.bottom - fm.top) / 2;

        float x = dp2px(6) + mTextPaint.measureText("MACD   ") + mBaseTextPaddingLeft;
        float countX = (mWidth - x) / 4;
        mTextMACDPaint.setTextAlign(Paint.Align.LEFT);
        mTextMACDPaint.setColor(getResources().getColor(R.color.color_macd_text));
        canvas.drawText("MACD(12,26,9)", x,
                mMainHeight + mVolumeTextHeight / 2 + textHeight - baseLine, mTextMACDPaint); //MACD

        mTextMACDPaint.setColor(getResources().getColor(R.color.color_diff_text));
        canvas.drawText("DIFF:" + StrUtil.floatToString(point.getDiff(), 2), x + countX,
                mMainHeight + mVolumeTextHeight / 2 + textHeight - baseLine, mTextMACDPaint); //DIFF

        mTextMACDPaint.setColor(getResources().getColor(R.color.color_dea_text));
        canvas.drawText("DEA:" + StrUtil.floatToString(point.getDea(), 2), x + countX * 2,
                mMainHeight + mVolumeTextHeight / 2 + textHeight - baseLine, mTextMACDPaint); //DEA

        mTextMACDPaint.setColor(getResources().getColor(R.color.c6774FF));
        canvas.drawText("STICK:" + StrUtil.floatToString(point.getMacd(), 2), x + countX * 3,
                mMainHeight + mVolumeTextHeight / 2 + textHeight - baseLine, mTextMACDPaint); //STICK
    }

    //GJL
    private void drawCJL(Canvas canvas) {
        //上面文字
        mTextPaint.setColor(getResources().getColor(R.color.color_child_text));
        Paint.FontMetrics fm = mTextPaint.getFontMetrics();
        float textHeight = fm.descent - fm.ascent;
        float baseLine = (textHeight - fm.bottom - fm.top) / 2;
        mTextPaint.setTextAlign(Paint.Align.LEFT);
        canvas.drawText("CJL", mBaseTextPaddingLeft, mMainHeight + mVolumeTextHeight / 2 + textHeight - baseLine, mTextPaint);//存在问题 ???

        float rowChildSpace = mVolumeHeight / mGridChildRows;
        if (mVolumeMax != mVolumeMin) {
            //左边文字
            mTextPaint.setColor(getResources().getColor(R.color.color_candle_paint));
            canvas.drawText(StrUtil.getPositiveNumber(mVolumeMax) + "",
                    mBaseTextPaddingLeft, mMainHeight + baseLine + mVolumeTextHeight, mTextPaint); //绘制最大值

            String text = StrUtil.getPositiveNumber((mVolumeMax - mVolumeMin) / 2);
            canvas.drawText(text, mBaseTextPaddingLeft,
                    mMainHeight + mVolumeTextHeight + fixTextY(rowChildSpace * 2), mTextPaint); //中间值

            canvas.drawText(StrUtil.getFaveMultipleMinimum((long) mVolumeMin) + "", mBaseTextPaddingLeft,
                    mMainHeight - textHeight + baseLine + mVolumeTextHeight + mVolumeHeight, mTextPaint); //绘制最小值
        }

        //GJL线
        if (mPoints.size() > 0) {
            mPricePaint.setColor(getResources().getColor(R.color.color_cjl_line_paint));
            IMinuteLine lastPoint = mPoints.get(0);
            float lastX = getX(0);
            for (int i = 0; i < mPoints.size(); i++) {
                IMinuteLine curPoint = mPoints.get(i);
                float curX = getX(i);

                canvas.drawLine(lastX + mBaseTimePadding - mScaleX / 2,
                        getInterestCJLY(lastPoint.getInterest()),
                        curX + mBaseTimePadding - mScaleX / 2,
                        getInterestCJLY(curPoint.getInterest()),
                        mPricePaint); //成交价

                //给上一个只赋值
                lastPoint = curPoint;
                lastX = curX;
            }
        }


        if (mInterestMax != mInterestMin) {
            //右边文字
            mTextPaint.setTextAlign(Paint.Align.RIGHT);
            mTextPaint.setColor(getResources().getColor(R.color.color_candle_paint));
            canvas.drawText(StrUtil.getPositiveNumber((long) mInterestMax) + "", mWidth - mBaseTextPaddingRight,
                    mMainHeight + baseLine + mVolumeTextHeight, mTextPaint); //绘制最大值

            String text1 = StrUtil.getPositiveNumber(mInterest);
            canvas.drawText(text1, mWidth - mBaseTextPaddingRight,
                    mMainHeight + mVolumeTextHeight + fixTextY(rowChildSpace * 2), mTextPaint); //中间值

            canvas.drawText(StrUtil.getPositiveNumber((long) mInterestMin) + "",
                    mWidth - mBaseTextPaddingRight,
                    mMainHeight - textHeight + baseLine + mVolumeTextHeight + mVolumeHeight, mTextPaint); //绘制最小值
        }

    }


    //CJL和MACL之间的切换
    @Override
    protected void jumpToCJLAndMACL(float downX, float downY) {
        //点击子试图判断
        if (downX > 0 && downX < mWidth) {
            if (downY > mMainHeight + mVolumeTextHeight && downY < mMainHeight + mVolumeTextHeight + mVolumeHeight) {
                Log.d("--->", "x = " + downX + ";" + "y = " + downY);
                isCJL = !isCJL;
                invalidate();
            }

        }

        //点击文字判断
        if (downX > 0 && downX < mBaseTextPaddingLeft + mTextPaint.measureText("MACD") + 10) {
            if (downY > mMainHeight && downY < mMainHeight + mVolumeTextHeight) {
                Log.d("--->", "x = " + downX + ";" + "y = " + downY);
                isCJL = !isCJL;
                invalidate();
            }
        }

    }

    /**
     * 修正y值
     */
    private float getY(float value) {
        return (mValueMax - value) * mScaleY;
    }

    //CJL柱形
    private float getVolumeCJLY(float value) {
        return mMainHeight + mVolumeTextHeight + mVolumeHeight - (value - mVolumeMin) * mVolumeTimeScaleY;
    }

    //CJL线
    private float getInterestCJLY(float value) {
        return mMainHeight + mVolumeTextHeight + mVolumeHeight - (value - mInterestMin) * mInterestTimeScaleY;
    }


    //MACD指标线
    private float getMACDLineY(float value) {
        if (value == 0) {
            return mMainHeight + mVolumeTextHeight + mVolumeHeight / 2;

        } else if (value > 0) {
            return mMainHeight + mVolumeTextHeight + mVolumeHeight / 2 - value * mMACDLineScaleY;

        } else if (value < 0) {
            return mMainHeight + mVolumeTextHeight + mVolumeHeight / 2 - value * mMACDLineScaleY;
        } else {
            return 0;
        }
    }


    /**
     * 解决text居中的问题
     */
    public float fixTextY(float y) {
        Paint.FontMetrics fontMetrics = mTextLeftPaint.getFontMetrics();
        return (y + (fontMetrics.descent - fontMetrics.ascent) / 2 - fontMetrics.descent);
    }
}

具体详解我放在Android原生股票图-分时图讲解和绘制(二)中。
源码

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容