Android版的股票行情K線圖開發(fā)
現(xiàn)在在手上的是一個(gè)證券資訊類型的app,其中有涉及到股票行情界面,行情中有K線圖等,看到網(wǎng)上很多人在求這方面的資料,所以我特地寫了一個(gè)demo在此處給大家分享一下。
下面是做出來的效果圖:
背景圖是利用canvas先畫出一個(gè)矩形,然后再畫幾根虛線,均線圖是通過path來繪制的,總之圖的繪制是很簡單的,我就不在這里作介紹了,大家可以去github下載源碼看看。涉及到均線、最高價(jià)、最低價(jià)、收盤價(jià)、開盤價(jià)的概念大家可以百度一下。
我再這里要介紹的是計(jì)算問題:
大家可以看到分時(shí)圖、日K、月K的左邊的成交價(jià)格都是不一樣的,而我們的k線都是通過這個(gè)價(jià)格來繪制的,也就是說價(jià)格是時(shí)刻變動(dòng),那么我們的k線繪制也是變動(dòng)的。假設(shè)我們要計(jì)算分時(shí)圖中價(jià)格為25.69的那一分鐘應(yīng)該如何畫,畫在屏幕中的哪一個(gè)位置,那么這個(gè)應(yīng)該怎么畫呢,價(jià)格是變動(dòng)的,畫的位置也是變動(dòng)的,但是有一點(diǎn)我們屏幕的大小是不變的。所以我們可以通過背景圖的高度來計(jì)算某個(gè)價(jià)格的線圖應(yīng)該從哪個(gè)地方開始畫。我們可以計(jì)算出一個(gè)像素點(diǎn)對(duì)應(yīng)多少個(gè)價(jià)格,分析圖如下:
價(jià)格和像素形成個(gè)一個(gè)比例計(jì)算是:double heightScale = (endY - startY)/(highPrice - lowPrice);
所以價(jià)格25.69應(yīng)該是畫在mStartY = (float) (startY+ (highPrice - 25.69) * heightScale);
這個(gè)明白了之后其他的原理都是一樣的,我就不介紹了,下面是部分代碼:
@Override protected void drawKChatBackGround() { Rect dirty = new Rect(left, kChartTop, right, KChartbottom); // 畫背景圖的矩形 mCanvas.drawRect(dirty, LineGrayPaint); PathEffect effects = new DashPathEffect(new float[] { 5, 5, 5, 5 }, 1); LineGrayPaint.setPathEffect(effects); Path path = new Path(); int y = kChartTop + 15; // 畫上面的虛線 path.moveTo(left, y ); path.lineTo(right, y ); String text = getPriceText(highPrice); int textHeight = (int) (textGrayPaint.descent() - textGrayPaint.ascent()); mCanvas.drawText(text,left - textGrayPaint.measureText(text) - 5,y + textHeight/2 ,textGrayPaint); double max = highPrice - lowPrice; if (max > 10){ // 分成四等分 // 畫中間的三根虛線 int n = 4; double sper = (highPrice - lowPrice) / 4;// 每一等分代表的價(jià)格 for(int i=1;i<n;i++){ y = i*((KChartbottom - kChartTop)/n) + kChartTop; path.moveTo(left, y); path.lineTo(right,y); text = getPriceText(highPrice - i*sper); mCanvas.drawText(text,left - textGrayPaint.measureText(text) - 5,y + textHeight/2,textGrayPaint); } }else{ // 分成兩等分 // 畫中間的虛線 y = (KChartbottom - kChartTop)/2 + kChartTop; path.moveTo(left, y); path.lineTo(right, y); text = getPriceText(highPrice - (highPrice - lowPrice) / 2); mCanvas.drawText(text,left - textGrayPaint.measureText(text) - 5,y + textHeight/2,textGrayPaint); } // 畫下面的虛線 y = KChartbottom - 15; path.moveTo(left, y); path.lineTo(right, y); text = getPriceText(lowPrice); mCanvas.drawText(text,left - textGrayPaint.measureText(text) - 5,y + textHeight/2,textGrayPaint); // // 畫等分的虛線和下面的日期 for (int i = num - 1; i > 0; i--) { int x = left + perWidth * i; path.moveTo(x, kChartTop); path.lineTo(x, KChartbottom); perXPoint[i - 1] = x; } mCanvas.drawPath(path, LineGrayPaint); }
@Override protected void drawMAChart() { // 畫均線 Path path5 = new Path(); Path path10 = new Path(); Path path20 = new Path(); double heightScale = (KChartbottom - kChartTop)/(highPrice - lowPrice); int maStart = left; float maStartY; path5.moveTo(maStart, (float) (kChartTop + (highPrice - infos.get(0).getMaValue5()) * heightScale)); path10.moveTo(maStart, (float) (kChartTop + (highPrice - infos.get(0).getMaValue10()) * heightScale)); path20.moveTo(maStart, (float) (kChartTop + (highPrice - infos.get(0).getMaValue20()) * heightScale)); for(SingleStockInfo info:infos){ maStart += per * perHalf;// 每一天實(shí)際所占的數(shù)據(jù)是4/6,左右邊距各1/6 maStartY = (float) (kChartTop + (highPrice - info.getMaValue5()) * heightScale); path5.lineTo(maStart, maStartY); maStartY = (float) (kChartTop + (highPrice - info.getMaValue10()) * heightScale); path10.lineTo(maStart, maStartY); maStartY = (float) (kChartTop + (highPrice - info.getMaValue20()) * heightScale); path20.lineTo(maStart, maStartY); maStart += per * perHalf; } Paint paint = new Paint(); paint.setColor(Color.BLUE); paint.setAntiAlias(true); paint.setStrokeWidth(2); paint.setStyle(Style.STROKE); mCanvas.drawPath(path5, paint); paint.setColor(Color.MAGENTA); mCanvas.drawPath(path10, paint); paint.setColor(Color.GREEN); mCanvas.drawPath(path20, paint); }
/** * 下面的柱形圖 */ @Override protected void drawPillarsChart(int flag) { LineGrayPaint.setPathEffect(null); Rect dirty = new Rect(left, pillarsChartTop, right, pillarsChartbottom); // 畫背景圖的矩形 mCanvas.drawRect(dirty, LineGrayPaint); int y = pillarsChartTop + (pillarsChartbottom - pillarsChartTop)/2; mCanvas.drawLine(left,y,right, y, LineGrayPaint); // 中間的值 String totalCount = getPriceText(maxCount/2/10000); float maginLeft = left - textGrayPaint.measureText(totalCount)- 5; mCanvas.drawText(totalCount, maginLeft, y,textGrayPaint); // 上面的值 totalCount = getPriceText(maxCount/10000); maginLeft = left - textGrayPaint.measureText(totalCount)- 5; mCanvas.drawText(totalCount, maginLeft, pillarsChartTop,textGrayPaint); // 下面的值 totalCount = "萬手"; maginLeft = left - textGrayPaint.measureText(totalCount) - 5; mCanvas.drawText(totalCount, maginLeft, pillarsChartbottom,textGrayPaint); int pStart = left; float pStartY; double heightScale = (pillarsChartbottom - pillarsChartTop)/maxCount; Paint paint = new Paint(); paint.setAntiAlias(true); paint.setStyle(Paint.Style.FILL); if (flag == StockService.FLAG){ for(MinuteInfo info:minuteInfos){ pStart += per * per16;// 每一天實(shí)際所占的數(shù)據(jù)是4/6,加上1/6 pStartY = (float) (pillarsChartTop + (maxCount - info.getVolume()) * heightScale); dirty = new Rect(pStart, (int) pStartY, (int) (pStart + per * per46), pillarsChartbottom-2); paint.setColor(info.getColor()); // 畫背景圖的矩形 mCanvas.drawRect(dirty, paint); pStart += per * per56;// 右邊的間距 5/6 } }else{ for(SingleStockInfo info:infos){ pStart += per * per16;// 每一天實(shí)際所占的數(shù)據(jù)是4/6,加上1/6 pStartY = (float) (pillarsChartTop + (maxCount - info.getTotalCount()) * heightScale); dirty = new Rect(pStart, (int) pStartY, (int) (pStart + per * per46), pillarsChartbottom-2); paint.setColor(info.getColor()); // 畫背景圖的矩形 mCanvas.drawRect(dirty, paint); pStart += per * per56;// 右邊的間距 5/6 } } }
/** * 分時(shí)圖 */ @Override public void drawHoursChart(){ double heightScale = (KChartbottom - kChartTop)/(highPrice - lowPrice); int cLeft = left; int cTop = 0; Path path = new Path(); path.moveTo(cLeft, KChartbottom-2); int position = 0; int perPointX = perXPoint[position];// 記錄第一條垂直虛線的x坐標(biāo) for(MinuteInfo info:minuteInfos){ cLeft += per * per16; cTop = (int) (kChartTop + (highPrice - info.getNow()) * heightScale); path.lineTo(cLeft + per * per26, cTop); if (cLeft >= perPointX){ // 恰好畫到第一條垂直虛線的地方,需要畫下面的時(shí)間 String text = KChartUtil.getMinute(info.getMinute()); float textWidth = textGrayPaint.measureText(text); int textHeight = (int) (textGrayPaint.descent()- textGrayPaint.ascent()); mCanvas.drawText(text, perPointX - textWidth/2, KChartbottom + textHeight, textGrayPaint); if (!(position == perXPoint.length-1)){ Log.e(TAG, perPointX+"----------"+info.getMinute()+"---"+text); perPointX = perXPoint[++position]; } } cLeft += per * per56;// 右邊的間距 5/6 } path.lineTo(cLeft, KChartbottom-2); Paint LinePaint = new Paint(); LinePaint.setColor(Color.BLUE); LinePaint.setAntiAlias(true); LinePaint.setStrokeWidth(1); LinePaint.setStyle(Style.STROKE); // LinePaint.setStyle(Style.STROKE); mCanvas.drawPath(path, LinePaint); LinePaint.setAlpha(50); LinePaint.setStyle(Style.FILL); mCanvas.drawPath(path, LinePaint); }
新年伊始,中國股市走出世界罕見,前無古人后無來者的極端行情,股市有風(fēng)險(xiǎn),投資需謹(jǐn)慎。
這句話是題外話了,重點(diǎn)還是希望對(duì)大家學(xué)習(xí)Android程序設(shè)計(jì)有所幫助。
- Android獲取應(yīng)用程序名稱(ApplicationName)示例
- Android 避免APP啟動(dòng)閃黑屏的解決辦法(Theme和Style)
- ANDROID 完美退出APP的實(shí)例代碼
- 怎么發(fā)布打包并發(fā)布自己的Android應(yīng)用(APP)
- 通過Html網(wǎng)頁調(diào)用本地安卓(android)app程序代碼
- android實(shí)現(xiàn)通知欄下載更新app示例
- 一看就懂的Android APP開發(fā)入門教程
- Android開發(fā)中避免應(yīng)用無響應(yīng)的方法(Application Not Responding、ANR)
- Android 應(yīng)用APP加入聊天功能
相關(guān)文章
Android開發(fā)筆記之:用Enum(枚舉類型)取代整數(shù)集的應(yīng)用詳解
本篇文章是對(duì)Android中用Enum(枚舉類型)取代整數(shù)集的應(yīng)用進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05Android下拉刷新控件PullToRefresh實(shí)例解析
這篇文章主要為大家詳細(xì)解析了Android下拉刷新控件PullToRefresh實(shí)例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-09-09Android高仿京東垂直循環(huán)滾動(dòng)新聞欄
通過自定義的LinearLayout,并且textView能夠循環(huán)垂直滾動(dòng),而且條目可以點(diǎn)擊,顯示區(qū)域最多顯示2個(gè)條目,并且還有交替的屬性垂直移動(dòng)的動(dòng)畫效果,通過線程來控制滾動(dòng)的實(shí)現(xiàn)2016-03-03Android實(shí)現(xiàn)知乎選項(xiàng)卡動(dòng)態(tài)隱藏效果實(shí)例
選項(xiàng)卡相信對(duì)大家來說應(yīng)該不陌生,最近發(fā)現(xiàn)知乎選項(xiàng)卡的動(dòng)態(tài)隱藏效果不錯(cuò),下面這篇文章主要給大家介紹了關(guān)于Android實(shí)現(xiàn)知乎選項(xiàng)卡動(dòng)態(tài)隱藏效果的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2018-02-02android textview設(shè)置字體的行距和字間距
這篇文章主要介紹了android textview設(shè)置字體的行距和字間距的方法,非常簡單實(shí)用,有需要的小伙伴可以參考下2016-05-05Android實(shí)現(xiàn)便于批量操作可多選的圖片ListView實(shí)例
這篇文章主要介紹了Android實(shí)現(xiàn)便于批量操作可多選的圖片ListView功能實(shí)現(xiàn)方法,涉及ListView針對(duì)多圖操作的相關(guān)技巧,需要的朋友可以參考下2016-08-08Android自定義view實(shí)現(xiàn)多色進(jìn)度條GradientProgressView的繪制
我們常使用shape實(shí)現(xiàn)漸變色,但是shape的極限卻只有三色,如果有超過三種顏色的View的要求,那么我們就不得不去自定義View來實(shí)現(xiàn)這個(gè)需求,所以下面我們就來看看如何自定義view實(shí)現(xiàn)多色進(jìn)度條的繪制吧2023-08-08Android中Glide實(shí)現(xiàn)超簡單的圖片下載功能
本篇文章主要介紹了Android中Glide實(shí)現(xiàn)超簡單的圖片下載功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-03-03