自定义View之(股票或基金)行情分时图的绘制

       之前自定义View文章写过好几篇了,像折线图的绘制,及行情蜡烛图的绘制,都有写过,但对于分时图的绘制,还没有总结过,正好,最近公司不是很忙,索性就重新的画一下及总结一下,可能绘制的比较简单,但基本上该包含的也都有所涉及,比如触摸十字光标,价格时间的显示都有。

先看一下最终实现的效果吧(GitHub下载地址:https://github.com/ming723/StockLine):


设置数据源:


        一般开发当中的数据,无论长连接还是短连接,服务端都会给我们返回过来数据,但这里毕竟是一个Demo,所以数据都是一些自己定义的假数据:这里我定义了一个月的数据量,看上图也会发现,只绘制一个月的,其实时间无论多少,只要掌握了绘制的方式,举一反三,就很容易绘制了。

/**
 * 模拟时间数据
 * */
private String[] times={"2018/1/1","2018/1/2","2018/1/3","2018/1/4","2018/1/5","2018/1/6","2018/1/7","2018/1/8","2018/1/9","2018/1/10","2018/1/11","2018/1/12","2018/1/13","2018/1/14","2018/1/15"
        ,"2018/1/16","2018/1/17","2018/1/18","2018/1/19","2018/1/20","2018/1/21","2018/1/22","2018/1/23","2018/1/24","2018/1/25","2018/1/26","2018/1/27","2018/1/28","2018/1/29","2018/1/30"};
/**
 * 模拟价格数据
 * */
private int[] prices={121,125,128,122,150,168,133,166,120,166,188,200,220,215,210,190,180,148,136,158,168,198,120,135,168,133,200,230,200,188};
具体分析:


        俗话说磨刀不误砍柴工,要把大象装进冰箱,主要分几步,这里我们也要去分析一下具体实现步骤,毕竟有了清晰的思路,那么后面绘制也就更加的有条不紊了,看看上图,这里我绘制的流程是,第一、先绘制了主要的干线,上下两条实线,中间三条虚线,第二、绘制左边价格和底部时间,第三、绘制价格波动也就是折线图,第四、绘制十字光标和右下价格时间展示。

开始绘制:


        具体分析之后,那么就开始我们的绘制吧,首先自定义一个类去继承于View,实现其构造及其他方法,并初始化信息,这里我们主要讲的是绘制过程,其它方法,请看源码。

/**
 * 绘制折线时的路径
 * */
private  Path mPath;
/**
 * 初始化一些信息
 * path
 * 背景等
 * */
private void initView(Context context) {
    setBackgroundColor(Color.parseColor("#222222"));//设置背景为黑色
    mPath=new Path();
}
1、绘制主要干线(两条实线和三条虚线):

/**
 * 初始化边框画笔信息(实线)
 * */
private Paint mRectPaint;
private void initRectPaint(){
    mRectPaint=new Paint();
    mRectPaint.setColor(Color.parseColor("#ffffff"));
    mRectPaint.setAntiAlias(true);//设置抗锯齿
    mRectPaint.setStrokeWidth(2);//线条粗细
    mRectPaint.setStyle(Paint.Style.STROKE);//设置空心
}
/**
 * 初始化虚线边框画笔信息
 * */
private Paint mInnerXPaint;
private void initInnerXPaint(){
    mInnerXPaint=new Paint();
    mInnerXPaint.setColor(Color.parseColor("#ffffff"));
    mInnerXPaint.setAntiAlias(true);//设置抗锯齿
    mInnerXPaint.setStrokeWidth(2);//线条粗细
    mInnerXPaint.setStyle(Paint.Style.STROKE);//设置空心
    setLayerType(LAYER_TYPE_SOFTWARE, null);//禁用硬件加速
    PathEffect effects = new DashPathEffect(new float[] {15, 5}, 1);
    mInnerXPaint.setPathEffect(effects);
}
/**
 * 绘制边框 上下两条实线
 * */
private int rectLeft=50, rectTop=10,rectRight=50,rectBottom=50;
private void onDrawRect() {
    canvas.drawLine(rectLeft,rectTop,mWidth-rectRight,rectTop,mRectPaint);
    canvas.drawLine(rectLeft,mHeight-rectBottom,mWidth-rectRight,mHeight-rectBottom,mRectPaint);
}

/**
 * 绘制虚线,三条
 * 由于只绘制中间,a=1,减少开始和结束时的重复绘制
 * */
private void onDrawInnerLine() {
    int heightInner=(mHeight-rectTop-rectBottom)/4;
    for (int a=1;a<4;a++){
        canvas.drawLine(rectLeft,heightInner*a,mWidth-rectRight,heightInner*a,mInnerXPaint);
    }
}
2、绘制左边价格和底部时间(由于都是白色,这里我直接使用的是边框的画笔):

/**
 * 绘制边框左边的价格
 *返回的数据源,我们这里分成5个区间,根据返回的价格最大减去最小来确定区间的大小
 * 一般价格都是从下到上增大
 *
 * */
private void onDrawLeftPrice() {
    int heightInner=(mHeight-rectBottom)/4;
    for (int a=0;a<priceLeft.length;a++){
        float leftPrice=heightInner*a;
        if (a==0){
            leftPrice=rectTop+5;
        }
        canvas.drawText(priceLeft[a],rectLeft-35,leftPrice,mRectPaint);
    }

}
/**
 * 绘制底部时间,我这里只显示了三个
 * */
private void onDrawBootmTime() {
    float widthTime=(mWidth-rectLeft-rectRight)/2;
    for (int a=0;a<3;a++){
        float time=widthTime*a;
        canvas.drawText(bootomTime[a],time,mHeight-20,mRectPaint);
    }
}
3、绘制价格波动也就是折线图(这里有一个stockList,是传入的数据集合,StockBean是数据对象,也就是把上边的时间和价格追加到集合里, StockBean有四个参数,除了价格和时间,还有就是价格和时间所对应的XY坐标,绘制折线图的时候,添加对象里,在后续的十字光标有用到,具体可看源码):

/**
 * 初始化折线图画笔
 * */
private Paint mBrokenPaint;
private void initBrokenPaint() {
    mBrokenPaint = new Paint();
    mBrokenPaint.setColor(Color.parseColor("#3785d9"));
    mBrokenPaint.setStyle(Paint.Style.STROKE);
    mBrokenPaint.setAntiAlias(true);
    mBrokenPaint.setStrokeWidth(2);
}
/**
 * 绘制折线图
 * */
private void onDrawZheLine() {
   float timeX= ( mWidth-rectLeft-rectRight)/stockList.size();//一份时间所占的宽度
    float priceY= ( mHeight-rectTop-rectBottom)/(maxStock-minStock);//一份价格所占的高度
    for (int a=0;a<stockList.size();a++){
        StockBean b = stockList.get(a);
        float x=timeX*a;
        float y=b.getPrice()-minStock;
        if(a==0){
            mPath.moveTo(rectLeft,mHeight-rectBottom);
        }
        Log.i("onDrawZheLine",priceY+"----"+timeX+"-----"+x+"-------"+y);
        float xLine=x+rectLeft;
        float yLine=mHeight-(rectBottom+y*priceY);
        b.setStockX(xLine);
        b.setStockY(yLine);
        mPath.lineTo(xLine,yLine);
    }
    canvas.drawPath(mPath,mBrokenPaint);
}
4、绘制十字光标和右下价格时间展示,这里longTime是1000,我判断只要大于1000,我就认为是长按,抬起时过1000就隐藏十字光标

private long downTime;//按下时间

private boolean isShowShiLine=false;//是否显示十字光标

private float moveX,moveY;
@Override
public boolean onTouchEvent(MotionEvent event) {
    super.onTouchEvent(event);
    switch (event.getAction()){
        case MotionEvent.ACTION_DOWN:
            downTime = event.getDownTime();
            break;
        case MotionEvent.ACTION_UP:
            hiddenLongPressView();
            break;
        case MotionEvent.ACTION_MOVE:
            long eventTime = event.getEventTime();
            if((eventTime-downTime)>longTime){//长按
                moveX=event.getX();
                moveY=event.getY();
                isShowShiLine=true;
                invalidate();
            }
            break;
    }
    return true;
}
/**
 * 抬起后過一秒后隐藏十字光标
 * */
private void hiddenLongPressView() {
    postDelayed(new Runnable() {
        @Override
        public void run() {
            isShowShiLine = false;
            invalidate();
        }
    }, 1000);
}
/**
 * 初始化十字光标
 * */
private Paint mShiPaint;
private void initShiLine() {
    mShiPaint = new Paint();
    mShiPaint.setColor(Color.parseColor("#d43c3c"));
    mShiPaint.setStyle(Paint.Style.STROKE);
    mShiPaint.setAntiAlias(true);
    mShiPaint.setStrokeWidth(2);
}

/**
 * 绘制十字光标
 * */
private void onDrawShiLine() {
    if(isShowShiLine){
        StockBean bean = stockList.get(0);
        float maxNum = Integer.MAX_VALUE;
        for (int a=0;a<stockList.size();a++){
            StockBean b = stockList.get(a);
            float x = Math.abs(moveX - b.getStockX());
            if(x<maxNum){
                bean=b;
                maxNum=x;
            }
        }

        Log.i("onDrawShiLine",moveX+"----"+bean.getStockY()+"----"+bean.getStockX());
        //绘制横线
        canvas.drawLine(rectLeft,bean.getStockY(),mWidth-rectRight,bean.getStockY(),mShiPaint);
        //绘制纵线
        canvas.drawLine(bean.getStockX(),rectTop,bean.getStockX(),mHeight-rectBottom,mShiPaint);

        //绘制右边价格边框
        Rect rectR=new Rect();
        rectR.top=(int)bean.getStockY()-15;
        rectR.bottom=(int)bean.getStockY()+15;
        rectR.left=mWidth-46;
        rectR.right=mWidth-5;
        canvas.drawRect(rectR,mShiPaint);

        //绘制右边价格
        canvas.drawText(bean.getPrice()+"",mWidth-42,bean.getStockY()+5,mRectPaint);

        //绘制底部时间边框
        Rect rectB=new Rect();
        rectB.top=mHeight-46;
        rectB.bottom=mHeight-10;
        rectB.left=(int) bean.getStockX()-35;
        rectB.right=(int) bean.getStockX()+40;
        canvas.drawRect(rectB,mShiPaint);

        //绘制底部时间
        canvas.drawText(bean.getTime()+"",bean.getStockX()-25,mHeight-25,mRectPaint);

    }
}
补充说明,向View传递数据:

/**
 * 获取价格中的最大和最小值
 * */
private float minStock,maxStock;
private String[] priceLeft=new String[5];//右边5个价格展示
private String[] bootomTime=new String[3];//底部3个时间展示
public void setStockData(List<StockBean> list){
    if(!list.isEmpty()){
        isData=true;
        stockList=list;
        mRectPaintLodingText.setColor(0x00000000);//隐藏加载
        mRectPaintLoding.setColor(0x00000000);//隐藏加载
        float minPrice=stockList.get(0).getPrice();
        float maxPrice=stockList.get(0).getPrice();
        for (int a=0;a<stockList.size();a++){
            float p=stockList.get(a).getPrice();
            if(p<minPrice){
                minPrice=p;
            }
            if(p>maxPrice){
                maxPrice=p;
            }
        }

        bootomTime[0]=stockList.get(0).getTime();
        bootomTime[1]=stockList.get(stockList.size()/2-1).getTime();
        bootomTime[2]=stockList.get(stockList.size()-1).getTime();
        minStock=minPrice;
        maxStock=maxPrice;

        float p=(maxStock-minStock)/4;
        priceLeft[4]=minStock+"";
        priceLeft[3]=(minStock+p)+"";
        priceLeft[2]=(minStock+2*p)+"";
        priceLeft[1]=(minStock+3*p)+"";
        priceLeft[0]=maxStock+"";
        invalidate();

    }
}
加载蒙版展示(一开始先绘制加载,待数据传来时,隐藏加载,这里我隐藏的方法是,把画笔颜色设置为透明,如上述传递数据代码):

/**
 * 初始化加载边框画笔信息
 * */
private Paint mRectPaintLoding;
private void initRectPaintLoding(){
    mRectPaintLoding=new Paint();
    mRectPaintLoding.setColor(Color.parseColor("#ffffff"));
    mRectPaintLoding.setAntiAlias(true);//设置抗锯齿
    mRectPaintLoding.setStrokeWidth(2);//线条粗细
    mRectPaintLoding.setStyle(Paint.Style.FILL);//设置实心
}
/**
 * 初始化文字信息
 * */
private Paint mRectPaintLodingText;
private void initRectPaintLodingText(){
    mRectPaintLodingText=new Paint();
    mRectPaintLodingText.setColor(Color.parseColor("#222222"));
    mRectPaintLodingText.setAntiAlias(true);//设置抗锯齿
    mRectPaintLodingText.setStrokeWidth(2);//线条粗细
    mRectPaintLodingText.setStyle(Paint.Style.STROKE);//设置空心
    mRectPaintLodingText.setTextSize(26);
}
/**
 * 绘制加载框
 * */
private String rectLoding="正在加载……";
private void onDrawRectLoding() {
    Rect rect=new Rect(0,0,mWidth,mHeight);
    canvas.drawRect(rect,mRectPaintLoding);
    canvas.drawText(rectLoding,mWidth/2-mRectPaintLodingText.measureText(rectLoding) / 2,mHeight/2,mRectPaintLodingText);
}
至此,就绘制结束了,如有问题,可在下方评论提问,看到会及时回复。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

程序员一鸣

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值