程序员人生 网站导航

一步步搞定Android行情K线蜡烛图(带十字光标)

栏目:综合技术时间:2017-02-18 08:53:37

行情K线图也就是我们常说的烛炬图,是金融类软件里可以说必不可少的,不管日K, 周K,月K,还是分钟K,准确的来表达个股在1定时间内涨跌走势,K线图有着不可疏忽的作用,其绘制进程也是彰显1个程序员对自定义控件的熟练程度,特别是对Canvas的灵活应用,绘线,绘边框,及位置的选取,比例的分配,今天这个Demo,则1步步为你诠释。


按惯例,先看下今天要实现的效果,全部Demo地址为:http://download.csdn.net/detail/ming_147/9732963,也能够关注公众号后(评论区第1条评论扫描便可)回复“行情k线图”,源码就会发送给您,公众号有很多android及其它技术文章,还请大家承蒙关注。




相对来讲比较简单的1个小Demo,为何来讲简单呢,1数据是固定的,2,时间是固定的,相比较实际项目中来讲,这已相当的简单了,我们可以简单的分1下步骤模块,然后再依照顺次来进行实现,通过上面的图片,我们可以大致分为,边框,横线,纵线,底部时间,左侧刻度,柱状图(烛炬图),10字光标这几个部份,好,分好以后,我们就来1步步实现吧。


由于代码稍多,为显得代码结构清晰,我们可以先写1个父类,用于实现边框,横纵线,及底部时间,左部刻度的绘制,柱状图(烛炬图)及10字光标我们放在子类中实现。


自定义1个父类继承于View,实现其构造方法,在onMeasure方法里设置View的大小:


@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpecheightMeasureSpec);
    
setMeasuredDimension(measureWidth(widthMeasureSpec),
            
measureHeight(heightMeasureSpec));
}


private int measureWidth(int measureSpec) {
    int result = 0;
    int 
specMode = MeasureSpec.getMode(measureSpec);//得到模式
    
int specSize = MeasureSpec.getSize(measureSpec);//得到尺寸

    
if (specMode == MeasureSpec.EXACTLY) {
        result = specSize;
    
else if (specMode == MeasureSpec.AT_MOST) {
        result = Math.min(resultspecSize);
    
}
    return result;
}

private int measureHeight(int measureSpec) {
    int result = 0;
    int 
specMode = MeasureSpec.getMode(measureSpec);
    int 
specSize = MeasureSpec.getSize(measureSpec);

    if 
(specMode == MeasureSpec.EXACTLY) {
        result = specSize;
    
else if (specMode == MeasureSpec.AT_MOST) {
        result = Math.min(resultspecSize);
    
}
    return result;
}




这里简单对两个类型做下解释:


MeasureSpec.EXACTLY是精确尺寸,当我们将控件的layout_widthlayout_height指定为具体数值时如:andorid:layout_width="50dip",或为FILL_PARENT是,都是控件大小已肯定的情况,都是精确尺寸。


MeasureSpec.AT_MOST
是最大尺寸,当控件的layout_widthlayout_height指定为WRAP_CONTENT时,控件大小1般随着控件的子空间或内容进行变化,此时控件尺寸只要不超过父控件允许的最大尺寸便可。因此,此时的modeAT_MOSTsize给出了父控件允许的最大尺寸。


设置完大小以后,我们先在构造方法里初始化1些信息,比如背风景,画笔:


/**
 * 
设置背风景及初始化画笔
 */
private void init() {
    setBackgroundColor(Color.parseColor("#222222"));
    
mPaint new Paint();
    
mPaint.setStrokeWidth(1);
    
mPaint.setStyle(Paint.Style.STROKE);
}


重写onDraw方法,并在方法内绘制相干信息。绘制边框,距离左上各位10,距离右侧为View宽度-10,距离底部为View高度-50:


private void drawBorder(Canvas canvas) {
    mPaint.setColor(Color.WHITE);
    
Rect r = new Rect();
    
r.left 10;
    
r.top 10;
    
r.right this.getRight() - 10;
    
r.bottom this.getHeight() - 50;
    
canvas.drawRect(rmPaint);
}


绘制横线,由于要留出1段区域做刻度绘制,所以,距离左侧要有1段距离,这里我设置的100,所以每条横线的起始位置1定,都是100,由于边框的最右侧为View宽度-10,所以横线的终止位置也是1致,起始y的位置和终止y的位置应当1致,依照1定的距离等分开来,这里的lineSize是要分成几份,我定义的是4份,则每份的长度就为:(当前View的高度-距离底部的距离-距离上部的距离)/lineXSize:


private int lineXSize 4;

private void drawXLine(Canvas canvas) {
    mPaint.setColor(Color.WHITE);
    float 
height = (this.getHeight() - 10f 50f) / lineXSize;//平均分为几分
    
for (int a = 0a < lineXSizea++) {
        float h = height * a + 10f;
        
Log.i("BaseLine"h + "===");
        
canvas.drawLine(100fh, this.getRight() - 10fhmPaint);
    
}
}


绘制纵线,其原理和绘制横线差不多,起始x的位置为距离左侧的距离既100,则每份的宽度就是,(当前View的宽度-距离左侧的距离-距离右侧的距离)/要分为几份,这里我定义的是lineYSize=3:


private int lineYSize 3;

private void drawYLine(Canvas canvas) {
    mPaint.setColor(Color.WHITE);
    float 
width = (this.getRight() - 10f 100f) / lineYSize;
    for 
(int a = 0a < lineYSizea++) {
        float w = width * a + 100f;
        
canvas.drawLine(w10fw, this.getHeight() - 50fmPaint);
    
}
}


绘制底部时间,times是自己定义的1个时间数组,其坐标位置和纵线类似,y值是固定不变的,x轴增加的距离和纵线1致:


private int[] times = {5678};

private void drawTimes(Canvas canvas) {
    mPaint.setColor(Color.parseColor("#FF00FF"));
    
mPaint.setTextSize(24);
    float 
width = (this.getRight() - 10f 100f) / lineYSize;
    for 
(int a = 0a < lineYSize 1a++) {
        float w = width * a + 100f;
        if 
(a == lineYSize) {
            canvas.drawText(times[a] + ""w - 30f, this.getHeight() - 25fmPaint);
        
else {
            canvas.drawText(times[a] + ""w - 15f, this.getHeight() - 25fmPaint);
        
}
    }
}


绘制Y轴价格刻度,价格刻度的绘制,就和绘制横线有点类似了,price是自己定义的1个刻度数组:


private float[] price = {260f240f220f};

private void drawYPrice(Canvas canvas) {
    mPaint.setColor(Color.WHITE);
    float 
height = (this.getHeight() - 10f 50f) / lineXSize;//平均分为几分
    
for (int a = 1a < lineXSizea++) {
        float h = height * a + 10f;
        
canvas.drawText(price[a - 1] + ""40fhmPaint);
    
}
}


经过以上父 类中的绘制,基本的边框,横线,纵线,底部时间,左部价格刻度,就完成了,接下来就是柱状图和10字光标:

自定义1个view集成于父类,实现其构造方法,初始化1些信息,设置画笔为实心的:


private void init() {
    mPaint new Paint();
    
mPaint.setStrokeWidth(1);
    
mPaint.setStyle(Paint.Style.FILL);
}


绘制烛炬图之前,我们需要初始化1些我们需要的数据,这里我定义了1个javaBean,里面我定义了1些数据,开盘,收盘,最高,最低,日期,实现其构造方法和get,set方法。


/**
 * 
开盘价
 */
private float open;

/**
 * 
最高价
 */
private float high;

/**
 * 
最低价
 */
private float low;

/**
 * 
收盘价
 */
private float close;

/**
 * 
日期
 */
private int date;


javaBean实现以后,我们就能够添加摹拟数据了,毕竟不是真实的项目中,所以数据,只能自己去创造了,listData是自己定义存储数据的:


protected List<StockLineBean> listData new ArrayList<>();

/**
 * 
添加数据
 */
private void setLineData() {
    List<StockLineBean> list = new ArrayList<StockLineBean>();
    
list.add(new StockLineBean(25025124825020170731));
    
list.add(new StockLineBean(24925224825220170730));
    
list.add(new StockLineBean(25025124825020170729));
    
list.add(new StockLineBean(24925224825220170728));
    
list.add(new StockLineBean(24825024725020170727));
    
list.add(new StockLineBean(25625624824820170726));
    
list.add(new StockLineBean(25725825625720170725));
    
list.add(new StockLineBean(25926025625620170724));
    
list.add(new StockLineBean(26126125725920170723));
    
list.add(new StockLineBean(25926025625620170722));
    
list.add(new StockLineBean(26126125725920170721));
    
list.add(new StockLineBean(26026025925920170720));
    
list.add(new StockLineBean(26226226026120170719));
    
list.add(new StockLineBean(26026225926220170718));
    
list.add(new StockLineBean(

------分隔线----------------------------
------分隔线----------------------------

最新技术推荐