Android行情走势图

Posted hygok

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android行情走势图相关的知识,希望对你有一定的参考价值。

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

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行情走势图的主要内容,如果未能解决你的问题,请参考以下文章

CCR炒币机器人:4.21比特币/BTC行情分析及CCR表现分享

highcharts去掉x轴,y轴,轴线以及刻度

新浪财经美股期货实时行情怎么看

扶浩彦:4.17午评,黄金行情走势精准剖析

比特币2021.5.16行情分析

区块链但丁:7.14BTC早间行情分析