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

简介: 可能绘制的比较简单,但基本上该包含的也都有所涉及,比如触摸十字光标,价格时间的显示都有。

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


先看一下最终实现的效果吧:


image.gif

设置数据源:


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


/*** 模拟时间数据* */privateString[] 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"};
/*** 模拟价格数据* */privateint[] 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,实现其构造及其他方法,并初始化信息,这里我们主要讲的是绘制过程,其它方法,请看源码。


/*** 绘制折线时的路径* */privatePathmPath;
/*** 初始化一些信息* path* 背景等* */privatevoidinitView(Contextcontext) {
setBackgroundColor(Color.parseColor("#222222"));//设置背景为黑色mPath=newPath();
}


1、绘制主要干线

两条实线和三条虚线

/*** 初始化边框画笔信息(实线)* */privatePaintmRectPaint;
privatevoidinitRectPaint(){
mRectPaint=newPaint();
mRectPaint.setColor(Color.parseColor("#ffffff"));
mRectPaint.setAntiAlias(true);//设置抗锯齿mRectPaint.setStrokeWidth(2);//线条粗细mRectPaint.setStyle(Paint.Style.STROKE);//设置空心}
/*** 初始化虚线边框画笔信息* */privatePaintmInnerXPaint;
privatevoidinitInnerXPaint(){
mInnerXPaint=newPaint();
mInnerXPaint.setColor(Color.parseColor("#ffffff"));
mInnerXPaint.setAntiAlias(true);//设置抗锯齿mInnerXPaint.setStrokeWidth(2);//线条粗细mInnerXPaint.setStyle(Paint.Style.STROKE);//设置空心setLayerType(LAYER_TYPE_SOFTWARE, null);//禁用硬件加速PathEffecteffects=newDashPathEffect(newfloat[] {15, 5}, 1);
mInnerXPaint.setPathEffect(effects);
}
/*** 绘制边框 上下两条实线* */privateintrectLeft=50, rectTop=10,rectRight=50,rectBottom=50;
privatevoidonDrawRect() {
canvas.drawLine(rectLeft,rectTop,mWidth-rectRight,rectTop,mRectPaint);
canvas.drawLine(rectLeft,mHeight-rectBottom,mWidth-rectRight,mHeight-rectBottom,mRectPaint);
}
/*** 绘制虚线,三条* 由于只绘制中间,a=1,减少开始和结束时的重复绘制* */privatevoidonDrawInnerLine() {
intheightInner=(mHeight-rectTop-rectBottom)/4;
for (inta=1;a<4;a++){
canvas.drawLine(rectLeft,heightInner*a,mWidth-rectRight,heightInner*a,mInnerXPaint);
    }
}


2、绘制左边价格和底部时间


由于都是白色,这里我直接使用的是边框的画笔


/*** 绘制边框左边的价格*返回的数据源,我们这里分成5个区间,根据返回的价格最大减去最小来确定区间的大小* 一般价格都是从下到上增大** */privatevoidonDrawLeftPrice() {
intheightInner=(mHeight-rectBottom)/4;
for (inta=0;a<priceLeft.length;a++){
floatleftPrice=heightInner*a;
if (a==0){
leftPrice=rectTop+5;
        }
canvas.drawText(priceLeft[a],rectLeft-35,leftPrice,mRectPaint);
    }
}
/*** 绘制底部时间,我这里只显示了三个* */privatevoidonDrawBootmTime() {
floatwidthTime=(mWidth-rectLeft-rectRight)/2;
for (inta=0;a<3;a++){
floattime=widthTime*a;
canvas.drawText(bootomTime[a],time,mHeight-20,mRectPaint);
    }
}


3、绘制价格波动也就是折线图


这里有一个stockList,是传入的数据集合,StockBean是数据对象,也就是把上边的时间和价格追加到集合里,StockBean有四个参数,除了价格和时间,还有就是价格和时间所对应的XY坐标,绘制折线图的时候,添加对象里,在后续的十字光标有用到,具体可看源码:


/*** 初始化折线图画笔* */privatePaintmBrokenPaint;
privatevoidinitBrokenPaint() {
mBrokenPaint=newPaint();
mBrokenPaint.setColor(Color.parseColor("#3785d9"));
mBrokenPaint.setStyle(Paint.Style.STROKE);
mBrokenPaint.setAntiAlias(true);
mBrokenPaint.setStrokeWidth(2);
}
/*** 绘制折线图* */privatevoidonDrawZheLine() {
floattimeX= ( mWidth-rectLeft-rectRight)/stockList.size();//一份时间所占的宽度floatpriceY= ( mHeight-rectTop-rectBottom)/(maxStock-minStock);//一份价格所占的高度for (inta=0;a<stockList.size();a++){
StockBeanb=stockList.get(a);
floatx=timeX*a;
floaty=b.getPrice()-minStock;
if(a==0){
mPath.moveTo(rectLeft,mHeight-rectBottom);
        }
Log.i("onDrawZheLine",priceY+"----"+timeX+"-----"+x+"-------"+y);
floatxLine=x+rectLeft;
floatyLine=mHeight-(rectBottom+y*priceY);
b.setStockX(xLine);
b.setStockY(yLine);
mPath.lineTo(xLine,yLine);
    }
canvas.drawPath(mPath,mBrokenPaint);
}


4、绘制十字光标和右下价格时间展示,这里longTime是1000,我判断只要大于1000,我就认为是长按,抬起时过1000就隐藏十字光标


privatelongdownTime;//按下时间privatebooleanisShowShiLine=false;//是否显示十字光标privatefloatmoveX,moveY;
@OverridepublicbooleanonTouchEvent(MotionEventevent) {
super.onTouchEvent(event);
switch (event.getAction()){
caseMotionEvent.ACTION_DOWN:
downTime=event.getDownTime();
break;
caseMotionEvent.ACTION_UP:
hiddenLongPressView();
break;
caseMotionEvent.ACTION_MOVE:
longeventTime=event.getEventTime();
if((eventTime-downTime)>longTime){//长按moveX=event.getX();
moveY=event.getY();
isShowShiLine=true;
invalidate();
            }
break;
    }
returntrue;
}
/*** 抬起后過一秒后隐藏十字光标* */privatevoidhiddenLongPressView() {
postDelayed(newRunnable() {
@Overridepublicvoidrun() {
isShowShiLine=false;
invalidate();
        }
    }, 1000);
}
/*** 初始化十字光标* */privatePaintmShiPaint;
privatevoidinitShiLine() {
mShiPaint=newPaint();
mShiPaint.setColor(Color.parseColor("#d43c3c"));
mShiPaint.setStyle(Paint.Style.STROKE);
mShiPaint.setAntiAlias(true);
mShiPaint.setStrokeWidth(2);
}
/*** 绘制十字光标* */privatevoidonDrawShiLine() {
if(isShowShiLine){
StockBeanbean=stockList.get(0);
floatmaxNum=Integer.MAX_VALUE;
for (inta=0;a<stockList.size();a++){
StockBeanb=stockList.get(a);
floatx=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);
//绘制右边价格边框RectrectR=newRect();
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);
//绘制底部时间边框RectrectB=newRect();
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传递数据:/*** 获取价格中的最大和最小值* */privatefloatminStock,maxStock;
privateString[] priceLeft=newString[5];//右边5个价格展示privateString[] bootomTime=newString[3];//底部3个时间展示publicvoidsetStockData(List<StockBean>list){
if(!list.isEmpty()){
isData=true;
stockList=list;
mRectPaintLodingText.setColor(0x00000000);//隐藏加载mRectPaintLoding.setColor(0x00000000);//隐藏加载floatminPrice=stockList.get(0).getPrice();
floatmaxPrice=stockList.get(0).getPrice();
for (inta=0;a<stockList.size();a++){
floatp=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;
floatp=(maxStock-minStock)/4;
priceLeft[4]=minStock+"";
priceLeft[3]=(minStock+p)+"";
priceLeft[2]=(minStock+2*p)+"";
priceLeft[1]=(minStock+3*p)+"";
priceLeft[0]=maxStock+"";
invalidate();
    }
}
加载蒙版展示(一开始先绘制加载,待数据传来时,隐藏加载,这里我隐藏的方法是,把画笔颜色设置为透明,如上述传递数据代码):/*** 初始化加载边框画笔信息* */privatePaintmRectPaintLoding;
privatevoidinitRectPaintLoding(){
mRectPaintLoding=newPaint();
mRectPaintLoding.setColor(Color.parseColor("#ffffff"));
mRectPaintLoding.setAntiAlias(true);//设置抗锯齿mRectPaintLoding.setStrokeWidth(2);//线条粗细mRectPaintLoding.setStyle(Paint.Style.FILL);//设置实心}
/*** 初始化文字信息* */privatePaintmRectPaintLodingText;
privatevoidinitRectPaintLodingText(){
mRectPaintLodingText=newPaint();
mRectPaintLodingText.setColor(Color.parseColor("#222222"));
mRectPaintLodingText.setAntiAlias(true);//设置抗锯齿mRectPaintLodingText.setStrokeWidth(2);//线条粗细mRectPaintLodingText.setStyle(Paint.Style.STROKE);//设置空心mRectPaintLodingText.setTextSize(26);
}
/*** 绘制加载框* */privateStringrectLoding="正在加载……";
privatevoidonDrawRectLoding() {
Rectrect=newRect(0,0,mWidth,mHeight);
canvas.drawRect(rect,mRectPaintLoding);
canvas.drawText(rectLoding,mWidth/2-mRectPaintLodingText.measureText(rectLoding) /2,mHeight/2,mRectPaintLodingText);
}


至此,就绘制结束了,如有问题,可在下方评论提问,看到会及时回复。


相关文章
|
Web App开发 数据采集 iOS开发
|
29天前
Plotly:绘制2005-2021年各省份人均可支配收入
Plotly:绘制2005-2021年各省份人均可支配收入
24 1
|
5月前
|
弹性计算 运维 Shell
动态股票市场分析
【4月更文挑战第30天】
51 0
|
5月前
TMA三均线股票期货高频交易策略的R语言实现
TMA三均线股票期货高频交易策略的R语言实现
股票基金行情大跌,受伤的不仅是你们,还有小R?
股票基金行情大跌,受伤的不仅是你们,还有小R?
65 0
|
数据可视化 数据处理 区块链
可视化 | Pyecharts动态绘制比特币近几年股价变迁
可视化 | Pyecharts动态绘制比特币近几年股价变迁
算法修炼Day51|● 309.最佳买卖股票时机含冷冻期 ● 714.买卖股票的最佳时机含手续费
算法修炼Day51|● 309.最佳买卖股票时机含冷冻期 ● 714.买卖股票的最佳时机含手续费
|
数据可视化 Python
可视化 | 中国历届夏季奥运会奖牌数据(树图)
可视化 | 中国历届夏季奥运会奖牌数据(树图)
|
数据可视化 数据挖掘 BI
销售额和毛利额月度趋势分析
销售额和毛利额月度趋势分析
683 0
销售额和毛利额月度趋势分析
|
存储 算法 C++