效果图


由于微信篇幅所限,部分代码省略,查看完整代码可点击左下角“阅读原文”。
作为Android开发者需要立即提升的三项技能:
分别是:UI,网络,线程,而UI又分:布局,绘制,以及触摸事件的反馈.其实UI确实只有这么几个最主要的东西,但是很多人却没能搞明白.
UI的这三方面,说容易也容易,说难也难.有同学当时也问到:怎么样才能算是掌握了这三个方面呢?
给出一个不算是为难的界面,能布局出来,绘制好,并掌握相应的触摸反馈,就算是基本上掌握UI了.
实际上UI容易也是在这里,初步上手,对大部分比较认真的同学来说都是可以做到的,但是深入了解的,却比较少.例如,触摸Touch事件是如何分发的,
它的原理是什么,这就需要大家更加深入的学习了.
前面说到的UI的三个方面,其实在股票图里面都有比较好的体现,下面就这三个方法,讲解一下实现股票图的思路
股票图基本知识
了解股票图如何绘制,首先应该了解股票图的业务逻辑是怎样的,这篇文章是仿雪球股票写的,建议大家下载雪球股票软件体验一下.在写这个股票图之前,我对股票是一无所知(原谅我穷买不起股票),
所以花了一点时间了解了一下股票图的基本信息,如果知道股票图是如何解读的,可以跳过这节.
股票图的种类特别多,不同的种类的股票图也不一样,例如股票有港股,美股,上证,深圳,创业板等等.然后上证又有:分时,日K,月K等等.
复杂程度完全可以直接绕晕人,没错,我就是看不懂所以不敢买.
股票图的种类之多,本文也没有一一编写,这里主要是仿照了雪球股票之上证指数的:分时图,以及日K图.
也就是股票的两大图种:分时图,以及蜡烛图.
分时图

分时图有股票当天的涨跌情况,以及一些最高点,最低点,比分比,
长按分时图,可以定位当时手指按下的时间所对应的股票点是多少点,并且可以左右滑动
股票的开盘时间是早上09:30-11:30,下午是13:00-15:00.
蜡烛图

蜡烛图和分时图类似,先除去那三条折线.分时图是把涨跌情况用折线表示,而蜡烛图是用一个矩形加一条竖线表示,和一根蜡烛一样,所以形象的称它为蜡烛图,
其中竖线的最高点代表当日最高涨到了多少点,最低表示最低跌到了多少点.
矩形的顶端,表示当日开盘是多少点,底端,表示收盘是多少点.
颜色红,代表收盘后,相对于昨天,涨了,颜色绿,则表示跌了.
三条折线分别代表了MA线,MA是“移动平均线”的简称,后面的数字:5、10、20…..是时间周期。MA5即5天收盘股票的平均点,其他的类推.博主这个例子没有实现MA线,作为大家的补充练手
日K图每个月一个间隔.
下面就分时图,蜡烛图,分别讲解其布局,绘制,触摸反馈
分时图
布局
布局无论是xml引用layout编写,亦或是java直接new出来,或者是使用canvas直接绘制,最重要的不是应该使用
RelativeLayout还是LinearLayout,而是应该剖析它的层次与结构.
层次
根据上面的基本介绍,分时图的可以分为以下几个层次:
第1层:横线,竖线,以及底部时间(底部时间没有其他的元素,可以处于任意一层)
第2层:折线,以及阴影部分
第3层:文字,包括最高点,最低点,百分比
结构
分时图的结构相对简单,在基本介绍上已经说明其基本信息.
股票的开盘时间是早上09:30-11:30,下午是13:00-15:00,所以其分上午,下午两部分.
中间的虚线是昨天收盘的股票点,以此为基准线,计算折线图的位置.
绘制
布局分析好之后,就开始绘制这些基本信息.普通View的绘制,是写好xml或者java代码,然后交给每个view自己绘制,这里我们自己控制其绘制.
绘制的步骤,其实就是布局中所说的层次,绘制的规则,则是布局中的结构.换句话说,这个结构,规则,就是数学中的公式,步骤就是我们解题的思路.
详细绘制步骤
开始.
自定义一个View,覆写其四个构造方法(注意最好四个构造方法都覆写,这样就可以通过多种途径新建这个View),覆写onDraw()方法,画图的时候就是在这个方法进行绘制的.
public class KLineView extends View {
public KLineView(Context context) {
super(context);
init();
}
public KLineView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public KLineView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public KLineView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
}
一般还需要初始化一些信息.为了让自己能看到每一步的绘制效果,编写一个添加测试数据方法,初始化的时候执行该方法即可.
/**
* canvas paint
*/
private Paint mPaint;
private void init() {
mPaint = new Paint();
createTestData();
}
/**
* create the test data
*/
private void createTestData() {
baseData = 3120.50f;
try {
times = new ArrayList<>();
prices = new ArrayList<>();
@SuppressLint("SimpleDateFormat") SimpleDateFormat dateFormat = new SimpleDateFormat
("yyyy-MM-dd HH:mm:ss");
Date date = dateFormat.parse("2017-01-01 09:30:00");
for (int i = 0; i 240; i++) {
if (i == 120) {
date = dateFormat.parse("2017-01-01 13:00:00");
}
date.setTime(date.getTime() + 60 * 1000);
times.add(formatTime(dateFormat.format(date)));
float tmp;
if (i == 0) tmp = (float) (baseData + 5 - Math.random() * 10);
else tmp = (float) (prices.get(i - 1) + 5 - Math.random() * 10);
tmp = formatPrice(tmp);
if (tmp > maxPrice) {
maxPrice = tmp;
}
if (tmp minPrice = tmp;
}
prices.add(tmp);
}
// for (String str : times) {
// Log.e("time", str);
// }
// for (Float item : prices) {
// Log.e("time", item + "");
// }
} catch (ParseException e) {
e.printStackTrace();
}
}
绘制线.
使用MarkMan量取,分时图在720*1280分辨率下,高度是是410,则我们可以把其高度分成410份.
它一共有5条横线,从上到下,每条线距离顶部的距离依次为:10,30,190,360,380.其中第3条为虚线.还有一条竖线,水平居中.
依次画出每一条线.
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int viewHeight = getHeight();
int viewWidth = getWidth();
float item = viewHeight / 410f;
/**
* draw lines
*/
drawLines(canvas, viewWidth, item);
}
/**
* draw lines
*
from top to bottom, it have 5 horizontal lines,
*
1 vertical line in the horizontal center.
*
*
* @param canvas canvas
* @param viewWidth the view's width
* @param item the view's height divided into 410
*/
private void drawLines(Canvas canvas, int viewWidth, float item) {
mPaint.setColor(Color.parseColor("#AAAAAA"));
mPaint.setStrokeWidth(0f);
canvas.drawLine(0, item * 10, viewWidth, item * 10, mPaint);
canvas.drawLine(0, item * 30, viewWidth, item * 30, mPaint);
drawDashEffect(canvas, 0, item * 190, viewWidth, item * 190);
canvas.drawLine(0, item * 360, viewWidth, item * 360, mPaint);
canvas.drawLine(0, item * 380, viewWidth, item * 380, mPaint);
canvas.drawLine(viewWidth / 2.0f, item * 10, viewWidth / 2.0f, item * 380, mPaint);
}
/**
* draw a doted line
*
* @param canvas canvas
* @param x startX
* @param y startY
* @param endX endX
* @param endY endY
*/
private void drawDashEffect(Canvas canvas, float x, float y, float endX, float endY) {
PathEffect effects = new DashPathEffect(new float[]{8, 8, 8, 8}, 1);
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
p.setColor(Color.parseColor("#AAAAAA"));
p.setPathEffect(effects);
p.setStyle(Paint.Style.STROKE);
Path path = new Path();
path.moveTo(x, y);
path.lineTo(endX, endY);
canvas.drawPath(path, p);
}
绘制时间.
时间的最简单,三个时间是固定的,位置也是固定的.
需要注意的是,绘制文字的x,y坐标,x=文字的左边,y=文字的baseline,文字的baseline默认等于-mPaint.getFontMetrics().top
想了解更多关于文字绘制的细节,请移步到这篇文章StyleTextView
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int viewHeight = getHeight();
int viewWidth = getWidth();
float item = viewHeight / 410f;
/**
* draw time
*/
drawTimes(canvas, viewWidth, item);
}
/**
* draw times
*
* draw text method:
*
params: 1:content, 2:x, 3: the baseline
* Note:the baseline == -mPaint.getFontMetrics().top in default
* More information, please
* click this
*
* @param canvas canvas
* @param viewWidth view's width
* @param item the view's height divided into 410
*/
private void drawTimes(Canvas canvas, int viewWidth, float item) {
mPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8f,
getResources().getDisplayMetrics()));
mPaint.setColor(Color.parseColor("#999999"));
float textWidth = mPaint.measureText("09:30");
canvas.drawText("09:30", item * 10, -mPaint.getFontMetrics().top + item * 380, mPaint);
canvas.drawText("11:30", viewWidth / 2.0f - textWidth / 2.0f, -mPaint.getFontMetrics()
.top + item * 380, mPaint);
canvas.drawText("15:00", viewWidth - textWidth - item * 10, -mPaint.getFontMetrics().top
+ item * 380, mPaint);
}
绘制折线,以及折线的阴影面积.
转到canvas上来说,其实就是绘制路径,在前面绘制横线的时候,绘制虚线其实就是绘制路径.
注意绘制阴影的时候,要把画笔设置为实心的,这样才会有阴影的效果,同时路径path要多连接几个点,包括右下角,左下角,表明折线下方,第五条横线上方,就是阴影部分.
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int viewHeight = getHeight();
int viewWidth = getWidth();
float item = viewHeight / 410f;
/**
* draw broken line and shadow graph
*/
drawBrokenLine(canvas, viewWidth, item, "#504F76DB", Paint.Style.FILL);
drawBrokenLine(canvas, viewWidth, item, "#4F76DB", Paint.Style.STROKE);
}
/**
* draw broken line
*
* @param canvas canvas
* @param viewWidth view's width
* @param item the view's height divided into 410
* @param color paint color
* @param style paint style,FILL: draw shadow, STROKE:draw line
*/
private void drawBrokenLine(Canvas canvas, int viewWidth, float item, String color, Paint
.Style style) {
Path path = new Path();
Paint paint = new Paint();
float xItem = viewWidth / 2.0f / 120f;
// get biggest difference value, it will be calculated proportion
float yCount = maxPrice - baseData > baseData - minPrice ? maxPrice - baseData : baseData
- minPrice;
//get one item height
float yItem = 330 * item / yCount / 2.0f;
//set path start point,item * 195 is baseData's y point.
path.moveTo(0, item * 195);
//set other points
for (int i = 0; i path.lineTo(xItem * (i + 1), item * 195 + yItem * (baseData - prices.get(i)));
}
//if draw shadow, we should add 3 points to draw a complete graphics.
//if draw lines, we should let lines bold.
if (Paint.Style.FILL == style) {
path.lineTo(viewWidth, item * 380);
path.lineTo(0, item * 380);
path.lineTo(0, item * 195);
path.close();
} else {
paint.setStrokeWidth(2f);
}
paint.setColor(Color.parseColor(color));
paint.setAntiAlias(true);
paint.setStyle(style);
canvas.drawPath(path, paint);
}
绘制最高点,最低点,以及百分比.
有了绘制时间的经验,我们知道x,y分别代表的是文字的左下角,baseline,直接绘制即可.
绘制最低点的时候需要注意,最低点距离第四条横线的距离,应该与第二条线距离最高点的距离一致.放大雪球股票的图,发现其K线图,以及后面要绘制的蜡烛图,这
两个距离都不相等,虽然无伤大雅.但是如果我们能做到,那就更好不过.
设计或者产品出来一个交互,一个需求,你做不到,没什么关系,因为别人也做不到.但是假设别人做不到,但是你做到了,那么很明显,你就强于别人
在前面绘制文字的时候提到过这篇文章StyleTextView,发布到郭霖的公众号后,有部分同学说,为什么这么麻烦搞这么多,感觉不需要这么复杂.
实际上如果只是单纯做一个需求,确实不需要多复杂的代码,直接绘制是最简单的,但是绘制也涉及到留白的问题,在一个要求不是特别精确的View,一两个像素的差距,确实可有可无,甚至有同学直接根据
实际运行出来的效果图,调整空白大小.
但是你为什么调整空白大小,为什么要这么调,调了以后其他的机型适配吗?如果在一个很大的View上,字体大小很大,此时能保证也能满足正常视觉吗?
故有时候追求一些细节,对自己的代码,以及技术,都是一种负责任的态度.
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int viewHeight = getHeight();
int viewWidth = getWidth();
float item = viewHeight / 410f;
/**
* draw max, min price and percent
*/
drawPriceAndPercent(canvas, viewWidth, item);
}
/**
* draw price and percent
*
* draw text method:
*
params: 1:content, 2:x, 3: the baseline
* Note:the baseline == -mPaint.getFontMetrics().top in default
* More information, please
* click this
*
* @param canvas canvas
* @param viewWidth view's width
* @param item the view's height divided into 410
*/
private void drawPriceAndPercent(Canvas canvas, int viewWidth, float item) {
// get biggest difference value, it will be calculated proportion
float yCount = maxPrice - baseData > baseData - minPrice ? maxPrice - baseData : baseData
- minPrice;
mPaint.setStrokeWidth(2f);
mPaint.setColor(Color.RED);
//draw max price
canvas.drawText(yCount + baseData + "", item * 10, -mPaint.getFontMetrics().top + item *
30, mPaint);
String percentStr = formatPrice(yCount * 100 / baseData) + "%";
float textWidth = mPaint.measureText(percentStr);
//draw max percent
canvas.drawText(percentStr, viewWidth - textWidth - item * 10, -mPaint.getFontMetrics()
.top + item * 30, mPaint);
mPaint.setColor(Color.parseColor("#008000"));
//draw min price
canvas.drawText(baseData - yCount + "", item * 10, item * 360 - (mPaint.getFontMetrics()
.descent - mPaint.getFontMetrics().ascent - mPaint.getTextSize() + mPaint
.getFontMetrics().ascent - mPaint.getFontMetrics().top), mPaint);
percentStr = "-" + percentStr;
textWidth = mPaint.measureText(percentStr);
//draw min percent
canvas.drawText(percentStr, viewWidth - textWidth - item * 10, item * 360 - (mPaint
.getFontMetrics().descent - mPaint.getFontMetrics().ascent -
mPaint.getTextSize() + mPaint.getFontMetrics().ascent - mPaint.getFontMetrics()
.top), mPaint);
}
至此,绘制基本已经结束了,直接运行,就能看到一个基本K线图,但是还差K线图的交互,也就是长按K线图的交互,这其实就是一个触摸反馈的过程

触摸
网上有很多的触摸文章教程,这里就不展开篇幅讲解了,这里直接使用手势识别类:GestureDetector
但是实际使用发现,假设手指长按了,就不能再接收到
手指的移动事件,看GestureDetector发现,如果它判断是长按就直接break了,同时发现它也没有发送手指离开屏幕的事件,这都不是我想要的,所以我就把它源码直接复制出来了,删掉了一些用不到的事件,并
添加了手指离开事件.第二个手指按下,离开事件.
boolean onDown2(MotionEvent e);
boolean onUp2(MotionEvent e);
boolean onUp(MotionEvent e);
添加手势触摸监听,首先在init初始化GestureDetector,并在onTouch中拦截触摸事件
篇幅所限,代码略。
分时图总结
至此,分时图的布局,绘制,触摸反馈都已经完整,如果再加上设置数据的方法,就可以作为一个基本的分时图使用了.
蜡烛图
布局
层次
第1层:横竖刻度线
第2层:股票点,时间
第3层:蜡烛,以及MA线(MA其实就是绘制折线,这个Demo中没有绘制)
结构
蜡烛图的结构相对复杂,首先是,数据是从右往左的呈现的,最右边是最新的数据,越往左时间越久.
其次,蜡烛图没有昨天收盘的时候的股票点,也就是它没有基准线,它的涨跌情况都是与前一天对比.所有它的刻度范围是不固定的,需要根据
当前呈现的数据,动态计算它的最高点和最低点
绘制
开始
创建类,并初始化,并构建测试数据方便调试
篇幅所限,代码略。
绘制横竖线,并绘制刻度,时间
蜡烛图的高度与分时图一致,总体高度410,第1条线距离顶部是10,View可绘制高度是370.
在绘制刻度之前,要先计算出当前展示的数据的最小值,最大值,以及刻度的比例,才能根据比例来绘制刻度.
/**
* calculate min and max y,the scale y.
*/
private void getYData() {
//计算最大值与最小值
maxPrice = 0;
minPrice = Float.MAX_VALUE;
for (int i = startIndex; i if (candles.get(i).start > maxPrice) maxPrice = candles.get(i).start;
if (candles.get(i).start if (candles.get(i).end > maxPrice) maxPrice = candles.get(i).end;
if (candles.get(i).end if (candles.get(i).max > maxPrice) maxPrice = candles.get(i).max;
if (candles.get(i).max if (candles.get(i).min > maxPrice) maxPrice = candles.get(i).min;
if (candles.get(i).min }
//根据最大值最小值的,来计算刻度的最高点,最低点
yScale = 1;
int diff = (int) (maxPrice - minPrice);
if (diff / 100000 >= 1) {
yScale = 100000;
minY = (int) minPrice / 100000 * 100000;
maxY = ((int) maxPrice / 100000 + 1) * 100000;
} else if (diff / 10000 >= 1) {
yScale = 10000;
minY = (int) minPrice / 10000 * 10000;
maxY = ((int) maxPrice / 10000 + 1) * 10000;
} else if (diff / 1000 >= 1) {
yScale = 1000;
minY = (int) minPrice / 1000 * 1000;
maxY = ((int) maxPrice / 1000 + 1) * 1000;
} else if (diff / 100 >= 1) {
yScale = 100;
minY = (int) minPrice / 100 * 100;
maxY = ((int) maxPrice / 100 + 1) * 100;
} else if (diff / 10 >= 1) {
yScale = 10;
minY = (int) minPrice / 10 * 10;
maxY = ((int) maxPrice / 10 + 1) * 10;
}
Log.e("siyehua", maxPrice + " " + minPrice + " " + maxY + " " + minY + " " +
yScale + " " + " ");
}
计算好Y轴刻度后,则开始绘制
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int viewHeight = getHeight();
int viewWidth = getWidth();
float itemW = (float) viewWidth / count;
float itemH = viewHeight / 410f;
drawLinesAndText(canvas, viewWidth, viewHeight, itemW, itemH);
}
/**
* draw lines and text
*
* @param canvas canvas
* @param viewWidth the view's width
* @param viewHeight the view's height
* @param itemW the view's wight divided into count
* @param itemH the view's height divided into 410
*/
private void drawLinesAndText(Canvas canvas, int viewWidth, int viewHeight, float itemW,float itemH) {
mPaint.setColor(Color.parseColor("#AAAAAA"));
mPaint.setStrokeWidth(0f);
mPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8f,
getResources().getDisplayMetrics()));
/**
* draw x lines and price text
*/
getYData();
int lineCount = (maxY - minY) / yScale;
if (lineCount > 5) {//假设线条超过5条,则把刻度大小翻倍
yScale *= 2;
lineCount = (maxY - minY) / yScale;
}
//draw first line
canvas.drawLine(0, itemH * 10, viewWidth, itemH * 10, mPaint);
float percent = 370 / (float) lineCount;
for (int i = 1; i //draw prices
String content = minY + (lineCount - i) * yScale + "";
canvas.drawText(content, itemH * 10, itemH * (10 + percent * i) - mPaint
.getFontMetrics().bottom, mPaint);
//draw middle lines
canvas.drawLine(0, itemH * (10 + percent * i), viewWidth, itemH * (10 + percent * i),
mPaint);
}
//draw last line
canvas.drawLine(0, itemH * 380, viewWidth, itemH * 380, mPaint);
/**
* draw y lines and time
*/
String tmpMonth = candles.get(startIndex).time.substring(5, 7);
for (int i = startIndex + 1; i //假设明天的时间与今天不一样,说明一个月的间隔到了,则绘制一条竖线
if (!tmpMonth.equals(candles.get(i + 1).time.substring(5, 7))) {
tmpMonth = candles.get(i + 1).time.substring(5, 7);
String timeStr = candles.get(i).time.substring(0, 7);
//注意数据是从右到左呈现的,所有要从右边开始计算坐标
float tmp = itemW * (count + startIndex - i) - itemW / 2;
//draw times
float timeWidth = mPaint.measureText(timeStr);
canvas.drawText(timeStr, tmp - timeWidth / 2, itemH * 380 + -mPaint
.getFontMetrics().top, mPaint);
//draw liens
canvas.drawLine(tmp, itemH * 10, tmp, itemH * 380, mPaint);
}
}
}
绘制蜡烛
绘制好线与文字,就可以绘制蜡烛了.蜡烛图在View的视觉上占了很大的份量,但是绘制的时候,实际上很简单,当刻度计算好后,只需要绘制一条线,以及一个实心的矩形即可.
篇幅所限,代码略。
绘制其他元素
雪球股票的蜡烛图,还有三条折线,以及左上角的文字提示.这些在分时图已经讲解了如何绘制了,只需要依样画葫芦即可.
因为蜡烛图的复杂不在于它的绘制,而在于它的触摸反馈,所以这里把更多的精力花在触摸反馈的处理上.
触摸
初始化触摸类
篇幅所限,代码略。
单根手指触摸
/**
* show touch line
*/
private void showTouchLine(float touchX) {
float itemX = (float) getWidth() / count;
if (longPressFlag) {//长按,与分时图一个效果,先获取触摸的index,再刷新界面
for (int i = 1; i <= count; i++) {
if (itemX * i >= touchX) {
touchIndex = i + 1;
break;
}
}
if (touchMoveListener != null && touchIndex >= 0) {
touchMoveListener.change(candles.get(count + startIndex - touchIndex).time
.substring(0, 10), candles.get(count + startIndex - touchIndex).end + "",
formatPrice((candles.get(count + startIndex - touchIndex).end - candles
.get(count + startIndex - touchIndex + 1).end) / candles.get
(count + startIndex - touchIndex + 1).end * 100) + "%", "4613" +
".93万");
}
} else {//左右滑动,左右滑动,View不需要滑动,只需要修改startIndex的值,即达到滑动的效果
int number = (int) ((touchX - downX) / itemX);
// Log.e("number", number + "");
startIndex = downIndex + number;
if (startIndex 0) startIndex = 0;
if (startIndex > candles.size() - count - 1) startIndex = candles.size() - count - 1;
}
postInvalidate();
}
/**
* draw lines and text
*
* @param canvas canvas
* @param viewWidth the view's width
* @param viewHeight the view's height
* @param itemW the view's wight divided into count
* @param itemH the view's height divided into 410
*/
private void drawTouchLines(Canvas canvas, int viewWidth, int viewHeight, float itemW, float
itemH) {
if (longPressFlag) {//长按
float x = itemW * touchIndex - itemW / 2;
float y;
float a = ((maxY - candles.get(count + startIndex - touchIndex).start) / (maxY -
minY) * 370 + 10) * itemH;
float b = ((maxY - candles.get(count + startIndex - touchIndex).end) / (maxY - minY)
* 370 + 10) * itemH;
if (candles.get(count + startIndex - touchIndex).end - touchIndex + 1).end) {
y = a > b ? a : b;
} else y = a
//draw the lines
mPaint.setColor(Color.parseColor("#999999"));
canvas.drawLine(0, y, viewWidth, y, mPaint);
canvas.drawLine(x, itemH * 10, x, itemH * 380, mPaint);
//draw the point
// mPaint.setColor(Color.parseColor("#FFC125"));
// mPaint.setStrokeWidth(10f);
// canvas.drawPoint(x, y, mPaint);
}
}
多根手指缩放
屏幕默认分为60根蜡烛,缩放后,只需要修改默认的蜡烛数目即可.
private void scaleCandle(float moveDistance) {
if (moveDistance > getWidth() / 30) {
if (count == 20) count = 10;
else if (count == 10) return;
else count -= 20;
} else if (moveDistance 30) {
if (count == 240) return;
else count += 20;
}
postInvalidate();
}
蜡烛图总结
蜡烛图的需求,功能基本上已经实现,除了三条折线没有绘制.只需要再添加设置数据方法,该类便可直接使用.
总结
本文从布局,绘制,触摸,三个方面讲解了分时图,蜡烛图的一步步实现过程.其实股票图的业务上还有很多是没有讲解到的,例如大家应该都有注意到截图中
下面有柱状图,其实这个应该也是图的一部分,还有一些高亮显示等问题.
但其实基本的原理都是不变的,一个View的三个方面都有详细的讲解到,涉及到更多业务逻辑,无非也就是在这个基础上,绘制自己业务想要的效果,万变不离其宗,只要掌握好方法,再复杂的图也信手捏来.
之所以要画这两个图,是因为之前有人问博主有没有别人写好的能直接用的股票图,搜了好久没发现有比较成熟的Android股票图绘制,有的都必须在项目的基础上改造,而且BUG也比较多
就目前来讲,Android暂时还没有一个较为成熟的股票图项目可以依赖,而使用半成熟的项目,本身就会遇到许多bug,与其花精力去修改别人的bug,不如自己编写一个.
博主本人公司并没有涉及到股票图的绘制,个人对股票也不是很熟悉,以上所有的讲解都是个人理解,难免会有错误,欢迎大家留言交流指正.
项目地址: https://github.com/siyehua/KLineGraph
大家都在看
Android自定义柱状图表效果
「WTF系列」深入Java中的位操作
【元旦送书】Android最新面试实战总结
Android 通用的下拉刷新,重温事件传递
欢迎前往安卓巴士博客区投稿,技术成长于分享
期待巴友留言,共同探讨学习