Android行情走势图
2016-06-30 10:34
483 查看
程序开发一个不会重复造轮子,但如果有时间自己造一个也不错。比如图表的,别人的开源代码集成了很多图表,而且修改图表的样式不方便,自己写一个的话就可以随意的更改样式,想咋改就咋改,而且改起来方便快捷。下面分享一下我自己写的一个行情走势图表。
请尊重作者劳动成果,转载请标明原文地址:
http://blog.csdn.net/u010053224/article/details/51788318
添加行情图表的自定义属性文件,xData为X轴最大值,yData为Y轴最大值,ySplitCount为Y轴分段数量。
就上面一点代码这么简单,还有一个XY轴数据类。
请尊重作者劳动成果,转载请标明原文地址:
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{ @Override public 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); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @Override protected 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{ @Override public 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; } }
相关文章推荐
- android 简单登陆注册的实现
- Android 事件总线Otto使用入门一
- 使用Action、Data属性启动系统Activity
- android ant混淆时遇到的错误总结
- 单例模式的几种用法比较
- Data、Type属性与intent-filter配置
- Android在屏幕适配时出现异常java.lang.UnsupportedOperationException: Can't convert to dimension: type=0x1
- Android之LayoutInflater的使用
- DataBinding中如何使用Listener
- Android中数据库的简单运用
- android使用google gcm接收push消息需要注意的地方
- Android Bundle类 学习总结
- 浅析Android 开源框架ImageLoader的用法
- 简单实用的Android ORM框架TigerDB
- android 中几种adapter的总结
- Android之FLAG_ACTIVITY_SINGLE_TOP
- Android模拟后台进程被杀
- Android仿qq下拉刷新及向左滑动列表----PullToRefresh, SwipeMenuListView开源项目整合
- Android中SD卡的读写以及SD卡状态的获取
- Android如何减少压缩包的体积