您的位置:首页 > 移动开发 > Android开发

Android行情走势图

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

请尊重作者劳动成果,转载请标明原文地址:
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;
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息