Android行情走势图

来源:互联网 发布:商城系统源码下载 编辑:程序博客网 时间:2024/04/27 18:33

        程序开发一个不会重复造轮子,但如果有时间自己造一个也不错。比如图表的,别人的开源代码集成了很多图表,而且修改图表的样式不方便,自己写一个的话就可以随意的更改样式,想咋改就咋改,而且改起来方便快捷。下面分享一下我自己写的一个行情走势图表。

请尊重作者劳动成果,转载请标明原文地址:

http://blog.csdn.net/u010053224/article/details/51788318

1,使用预览

<cn.lib.ui.widget.LineChart        android:id="@+id/lc_gold"        android:layout_width="match_parent"        android:layout_height="@dimen/y125"        android:layout_marginLeft="@dimen/x10"        line:lineWidth="1.5dp"        line:ySplitCount="6" />
XML的layout里面加上行情布局,行情图表有个从左到右的伸展动态效果,图片效果如下。

2,代码分析

<pre name="code" class="html"><declare-styleable name="LineChart">        <attr name="xData" format="integer"/>        <attr name="yData" format="float"/>        <attr name="ySplitCount" format="integer" />        <attr name="lineWidth" format="dimension" />        <attr name="lineColor" format="reference|color"/>        <attr name="textSize" format="dimension"/>    </declare-styleable>
添加行情图表的自定义属性文件,xData为X轴最大值,yData为Y轴最大值,ySplitCount为Y轴分段数量。
/** * 当输入的x轴数据最大值大于之前的mXData值,mXMaxData=xMaxData,否则不变(y也一样) * @author HuangYuGuang * Create on 2015年8月19日 * @param datas 输入的xy数据 */public void setData(List<LineData> datas){mDatas = datas;int xMaxData = 0;float yMaxData = 0;float yMinData = 1000000;for(int i=0; i<mDatas.size(); i++){LineData data = mDatas.get(i);if(data.getX() > xMaxData) xMaxData = data.getX();if(data.getY() > yMaxData) yMaxData = data.getY();if(data.getY() < yMinData) yMinData = data.getY();}float h = yMaxData - yMinData;if(h == 0) h=0.05f;if(xMaxData != 0) mXMaxData = xMaxData;if(yMaxData != 0) mYMaxData = yMaxData+h*mChartDis;mYMinData = yMinData-h*mChartDis;new DrawThread().start();}
X轴和Y轴的最大值是根据传入的数值变化的,float mChartDis = 0.15f,走势图Y轴最顶端到图表顶端的间距是走势图最大值和最小值的0.15倍,Y轴最底端距离也是一样。

/** * 动态画走线 * @author HuangYuGuang * Create on 2015年9月22日 * File Name LineChart.java */private class DrawThread extends Thread{@Overridepublic void run() {if(mDatas == null || mDatas.size() < 2) return;//不管数据多长都要在2000ms内画完走线int time = 2000/mDatas.size();for(int i=1; i<mDatas.size(); i++){try {Thread.sleep(time);} catch (InterruptedException e) {e.printStackTrace();}if(mDatas.size() > 60 && i%3 != 0 && i != mDatas.size()-1) continue; //缩短三分之二重绘的次数,防止重绘过于频繁变卡itemNum = i;postInvalidate();}}}
动态走势的效果,如果行情图有大于60个数据,就在数据下标为3的倍数才重绘一次,本次绘图到行情数据列表的下标itemNum就停止。画走势图就是把每个相邻的点用线段链接起来,所以并不是很难。

3,源码

下面贴出全部的java源码
import java.util.ArrayList;import java.util.List;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.LinearGradient;import android.graphics.Paint;import android.graphics.Paint.Style;import android.graphics.Path;import android.graphics.Rect;import android.graphics.Shader;import android.util.AttributeSet;import android.util.TypedValue;import android.view.View;import cn.golditfin.money.lib.R;import cn.golditfin.money.lib.entity.LineData;/** * @author HuangYuGuang * Create on 2015年8月19日 * File Name LineChart.java */public class LineChart extends View{private final float density = getResources().getDisplayMetrics().density;/** * 数字和表格的距离 */private float DISTANCE = 5 * density;/** * 顶部和底部的间隙为走势图高度的mChartDis倍 */private float mChartDis = 0.15f;/** * X轴的最大值 */private int mXMaxData;/** * Y轴的最大值 */private float mYMaxData;/** * Y轴的最小值 */private float mYMinData;/** * X轴分段的数量 */private int mXSplitCount = 6;/** * Y轴分段的数量 */private int mYSplitCount;/** * 画线的颜色 */private int mLineColor;/** * 画线的宽度 */private int mLineWidth;/** * 字体大小 */private int mTextSize;/** * XY轴的数据集合 */private List<LineData> mDatas;/** * 当前动态走线到第几个点 */private int itemNum;private Rect mTextBound;private Paint mPaint;public LineChart(Context context, AttributeSet attrs) {this(context, attrs,0);}public LineChart(Context context) {this(context,null);}public LineChart(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.LineChart, defStyleAttr, 0);mXMaxData = a.getInt(R.styleable.LineChart_xData, 60);mYMaxData = a.getFloat(R.styleable.LineChart_yData, 500);mYSplitCount = a.getInt(R.styleable.LineChart_ySplitCount, 6);mLineColor = a.getColor(R.styleable.LineChart_lineColor, 0xFFF5BE13);mLineWidth = a.getDimensionPixelSize(R.styleable.LineChart_lineWidth, (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getResources().getDisplayMetrics()));mTextSize = a.getDimensionPixelSize(R.styleable.LineChart_textSize, (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 14, getResources().getDisplayMetrics()));a.recycle();mPaint = new Paint();mTextBound = new Rect();mPaint.setTextSize(mTextSize);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);}@Overrideprotected void onDraw(Canvas canvas) {mPaint.setStyle(Style.FILL);String data = String.format("%.3f", mYMaxData);//保留了三位小数的宽度//计算y轴数字的宽高mPaint.getTextBounds(data, 0, data.length(), mTextBound);int yDataLen = mTextBound.width(); //Y轴数字最长的长度float leftDistance = yDataLen+DISTANCE; //表格到左边的距离float bottomDistance = mTextBound.height()+DISTANCE;//表格到底边的距离mPaint.getTextBounds(mXMaxData+"", 0, (mXMaxData+"").length(), mTextBound);float width = getWidth() - leftDistance - mTextBound.width(); //表格的宽float height = getHeight() - bottomDistance - mTextBound.height();//表格的高/*mPaint.setColor(0xFFF2EDED); //设置图表背景色canvas.drawRect(leftDistance, getHeight()-bottomDistance-height, leftDistance+width, getHeight()-bottomDistance, mPaint);*///写Y轴数字for(int i=0; i<=mYSplitCount; i++){mPaint.setColor(0xFF6D6D6D);String yData = String.format("%.3f", (mYMaxData - mYMinData)*i/mYSplitCount + mYMinData);mPaint.getTextBounds(yData, 0, yData.length(), mTextBound);canvas.drawText(yData, yDataLen-mTextBound.width(), getHeight()-height*i/mYSplitCount-bottomDistance+mTextBound.height()/2, mPaint);mPaint.setColor(0xffE5E2E2);if(i==0){mPaint.setStrokeWidth(2.2f);}else {mPaint.setStrokeWidth(1);}//画X轴线canvas.drawLine(leftDistance, getHeight()-bottomDistance-height*i/mYSplitCount, leftDistance+width, getHeight()-bottomDistance-height*i/mYSplitCount, mPaint);}//写X轴数字for(int i=0; i<7; i++){mPaint.setColor(0xFF6D6D6D);String xData = mXMaxData*i/mXSplitCount+"";mPaint.getTextBounds(xData, 0, xData.length(), mTextBound);//FIXME 暂时去掉x轴坐标//canvas.drawText(xData, width*i/mXSplitCount+leftDistance-mTextBound.width()/2, getHeight(), mPaint);mPaint.setColor(0xffE5E2E2);if(i==0){mPaint.setStrokeWidth(2.2f);}else {mPaint.setStrokeWidth(1);}//画Y轴线canvas.drawLine(leftDistance+width*i/mXSplitCount, getHeight()-bottomDistance, leftDistance+width*i/mXSplitCount, getHeight()-bottomDistance-height, mPaint);}//画走线mPaint.setStrokeWidth(mLineWidth);mPaint.setAntiAlias(true);if(mDatas == null || mDatas.size()<2) return;//因为postInvalidate()是把以前的都清除再重绘,所以前面已经画的也要重新画for(int i=1; i<=itemNum; i++){LineData data1 = mDatas.get(i-1);LineData data2 = mDatas.get(i);float startX = data1.getX()*width/mXMaxData+leftDistance;float startY = getHeight()-bottomDistance-(data1.getY() - mYMinData)*height/(mYMaxData - mYMinData);float stopX = data2.getX()*width/mXMaxData+leftDistance;float stopY = getHeight()-bottomDistance-(data2.getY() - mYMinData)*height/(mYMaxData - mYMinData);mPaint.setColor(mLineColor);canvas.drawLine(startX, startY, stopX, stopY, mPaint);//底部渐变色LinearGradient gradient = new LinearGradient(startX, getHeight()-bottomDistance, stopX, stopY, 0x50FEFAF0, 0x50FFD072, Shader.TileMode.MIRROR);mPaint.setShader(gradient);Path path = new Path();path.moveTo(startX, getHeight()-bottomDistance);path.lineTo(startX, startY);path.lineTo(stopX, stopY);path.lineTo(stopX, getHeight()-bottomDistance);path.close();canvas.drawPath(path, mPaint);mPaint.setShader(null);}}/** * 四舍五入保留3位小数 */private float round3Decimals(float num){return Math.round(num*1000)*1.0f/1000;}/** * 动态画走线 * @author HuangYuGuang * Create on 2015年9月22日 * File Name LineChart.java */private class DrawThread extends Thread{@Overridepublic void run() {if(mDatas == null || mDatas.size() < 2) return;//不管数据多长都要在2000ms内画完走线int time = 2000/mDatas.size();for(int i=1; i<mDatas.size(); i++){try {Thread.sleep(time);} catch (InterruptedException e) {e.printStackTrace();}if(mDatas.size() > 60 && i%3 != 0 && i != mDatas.size()-1) continue; //缩短三分之二重绘的次数,防止重绘过于频繁变卡itemNum = i;postInvalidate();}}}/** * 当输入的x轴数据最大值大于之前的mXData值,mXMaxData=xMaxData,否则不变(y也一样) * @author HuangYuGuang * Create on 2015年8月19日 * @param datas 输入的xy数据 */public void setData(List<LineData> datas){mDatas = datas;int xMaxData = 0;float yMaxData = 0;float yMinData = 1000000;for(int i=0; i<mDatas.size(); i++){LineData data = mDatas.get(i);if(data.getX() > xMaxData) xMaxData = data.getX();if(data.getY() > yMaxData) yMaxData = data.getY();if(data.getY() < yMinData) yMinData = data.getY();}float h = yMaxData - yMinData;if(h == 0) h=0.05f;if(xMaxData != 0) mXMaxData = xMaxData;if(yMaxData != 0) mYMaxData = yMaxData+h*mChartDis;mYMinData = yMinData-h*mChartDis;new DrawThread().start();}public void setYDatas(List<Float> yDatas){List<LineData> datas = new ArrayList<LineData>();for(int i=0; i<yDatas.size(); i++){datas.add(new LineData(i, yDatas.get(i)));}setData(datas);}}
就上面一点代码这么简单,还有一个XY轴数据类。
public class LineData {private int x;private float y;public LineData() {}public LineData(int x, float y) {this.x = x;this.y = y;}/** * @return the x */public int getX() {return x;}/** * @param x the x to set */public void setX(int x) {this.x = x;}/** * @return the y */public float getY() {return y;}/** * @param y the y to set */public void setY(float y) {this.y = y;}}







0 0