之前自定义 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, 实现其构造及其他方法,并初始化信息,这里我们主要讲的是绘制过程,其它方法,请看源码.
1,绘制主要干线(两条实线和三条虚线):
/**
* 绘制折线时的路径
* */
private Path mPath;
/**
* 初始化一些信息
* path
* 背景等
* */
private void initView(Context context) {
setBackgroundColor(Color.parseColor("#222222")); //设置背景为黑色
mPath = new Path();
}
2,绘制左边价格和底部时间(由于都是白色,这里我直接使用的是边框的画笔):
/**
* 初始化边框画笔信息(实线)
* */
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);
}
}
3,绘制价格波动也就是折线图(这里有一个 stockList,是传入的数据集合,StockBean 是数据对象,也就是把上边的时间和价格追加到集合里,StockBean 有四个参数,除了价格和时间,还有就是价格和时间所对应的 XY 坐标,绘制折线图的时候,添加对象里,在后续的十字光标有用到,具体可看源码):
/**
* 绘制边框左边的价格
*返回的数据源,我们这里分成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);
}
}
4,绘制十字光标和右下价格时间展示,这里 longTime 是 1000,我判断只要大于 1000,我就认为是长按,抬起时过 1000 就隐藏十字光标
/**
* 初始化折线图画笔
* */
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);
}
补充说明,向 View 传递数据:
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);
}
}
加载蒙版展示(一开始先绘制加载,待数据传来时,隐藏加载,这里我隐藏的方法是,把画笔颜色设置为透明,如上述传递数据代码):
/**
* 获取价格中的最大和最小值
* */
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);
}
来源: http://blog.csdn.net/ming_147/article/details/79010120