After developing a translation of LinkLITE for Android, I decided to share with you my experience in this area by publishing the java class used to draw a graph (line). This article is the first of a series of two on this subject. The second deal, the java class to draw a pie graph, following the same principle.
First talk, the class defining the type of data to draw:
package com.gafmedia.Graph;
public class LineChartItem {
public String Date;
public int Connection;
}
In the case of our application, we have: X: Date (String), and Y: Connect (int).
A graph is composed of several points, they will be placed in an array of type :
List<LineChartItem> LineChartArray = new ArrayList<LineChartItem>(0);
To fill this table you can use, for example, a loop like this:
...
for (int i = StartIndex; i < EndIndex; i++) {
ItemNode = (Element) AllNodes.item(i);
Day = ItemNode.getAttribute("day");
Month = ItemNode.getAttribute("month");
Year = ItemNode.getAttribute("year");
try {
Connection = NumberFormater.parse(ItemNode.getAttribute("value")).intValue();
} catch (java.text.ParseException e) {
e.printStackTrace();
}
ChartItem = new LineChartItem();
ChartItem.Date = Day + "-" + Month + "-" + Year;
ChartItem.Connection = Connection;
LineChartArray.add(ChartItem);
}
...
Once your table is full, you create your graph to handle it as follows:
...
View_LineChart mTmpChartView = new View_LineChart(this);
mTmpChartView.setGeometry(mHolderWidth, mHolderHeight, CHART_LEFT_GAP, CHART_RIGHT_GAP, CHART_GAP_TOP, CHART_GAP_BOTTOM);
if(mWhiteGraph == true){
mTmpChartView.setSkinParams(mWhiteBgColor, mWhiteGridColor, mWhiteLineColor, mWhiteTxtColor, mFillGraph, mAverageLine, mZoomChart);
} else {
mTmpChartView.setSkinParams(mBlackBgColor, mBlackGridColor, mBlackLineColor, mBlackTxtColor, mFillGraph, mAverageLine, mZoomChart);
}
mTmpChartView.setData(LineChartArray, mUrl);
mTmpChartView.invalidate();
...
Explanation of used methods:
public void setGeometry(int width, int height, int GapLeft, int GapRight, int GapTop, int GapBottom) {
...
}
This method defines the geometric parameters of your View: size (width, height), then the border settings (GapLeft, GapRight, GapTop, GapBottom). This method is called before anything
else!.
public void setSkinParams(int bgColor, int gridColor, int lineColor, int textColor, boolean FillGraph, boolean AverageLine, boolean ZoomChart) {
...
}
The second method is called, defines the various display settings, first the different colors to use (bgColor (background), GridColor (Grid), LineColor (Line), textColor (Text)), then
the different things to show or not: (FillGraph) indicates whether the graph is full, or just display as a line (AverageLine) indicates whether the line of average values of Y is
shown or not, and finally (ZoomChart) indicates whether the graph from scratch Also on display or if it is between the minimum and maximum value of Y.
public void setData( List<LineChartItem> ChartArray, String Url) {
..
}
The last method defined values of the graph. Calling this method unlocks the method OnDraw (), avoiding throwing an exception if the data are not defined.
Finally, here is the class View_LineChart.java, to include in your application:
package com.gafmedia.Graph;
import com.gafmedia.Graph.LineChartItem;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
public class View_LineChart extends View {
public static final int LABEL_X_SHOW = 7;
public static final int LABEL_Y_SHOW = 6;
private Canvas mCanvas = null;
private Paint mBGPaint = new Paint();
private Paint mLinePaint = new Paint();
private Paint mGridPaintFull = new Paint();
private Paint mGridPaintDot = new Paint();
private Paint mTitlePaint = new Paint();
private Paint mAveragePaint = new Paint();
private Paint mAverageTxtPaint = new Paint();
private boolean isReadyToDraw = false;
private int mWidth;
private int mHeight;
private boolean mFillGraph;
private boolean mAverageLine;
private boolean mZoomChart;
private int mGapLeft;
private int mGapRight;
private int mGapTop;
private int mGapBottom;
private int mBgColor;
private int mGridColor;
private int mLineColor;
private int mTextColor;
private String mUrl;
List<LineChartItem> mLineChartArray = new ArrayList<LineChartItem>();
//--------------------------------------------------------------------------------------
public View_LineChart (Context context){
super(context);
}
//--------------------------------------------------------------------------------------
public View_LineChart(Context context, AttributeSet attrs) {
super(context, attrs);
}
//--------------------------------------------------------------------------------------
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (isReadyToDraw == false) return;
mCanvas = canvas;
//--------------------------------------------------------------------------
mBGPaint.setStyle(Paint.Style.FILL);
mBGPaint.setColor(mBgColor);
mCanvas.drawPaint(mBGPaint);
//--------------------------------------------------------------------------
mLinePaint.setColor(mLineColor);
if (mFillGraph == true){
mLinePaint.setStyle(Paint.Style.FILL_AND_STROKE);
} else {
mLinePaint.setStyle(Paint.Style.FILL);
}
mLinePaint.setAntiAlias(true);
mLinePaint.setStrokeWidth(1.5f);
//--------------------------------------------------------------------------
mGridPaintDot.setColor(mGridColor);
mGridPaintDot.setAntiAlias(false);
mGridPaintDot.setStyle(Paint.Style.STROKE);
mGridPaintDot.setStrokeWidth(0.5f);
mGridPaintDot.setPathEffect( new DashPathEffect(new float[] {5,5 }, 1) );
//--------------------------------------------------------------------------
mGridPaintFull.setColor(mGridColor);
mGridPaintFull.setAntiAlias(false);
mGridPaintFull.setStyle(Paint.Style.STROKE);
mGridPaintFull.setStrokeWidth(1.0f);
//--------------------------------------------------------------------------
mTitlePaint.setStyle(Paint.Style.FILL);
mTitlePaint.setTextAlign(Paint.Align.LEFT);
mTitlePaint.setColor(mTextColor);
mTitlePaint.setTextSize(10);
//--------------------------------------------------------------------------
Rect TextRect = new Rect();
//----------------------------------------------------------------------------------
// Get Y Max Values
//----------------------------------------------------------------------------------
String StrLablel;
LineChartItem ChartItem;
float y;
int Index;
int value;
float OldX;
float OldY;
float NewX;
float NewY;
int maxY = 0;
int minY = 0;
int ValuesCount = mLineChartArray.size();
float graphAverage = 0;
//----------------------------------------------------------------------------------
// GET Y MIN & MAX
//----------------------------------------------------------------------------------
for (int i = 0; i<ValuesCount; i++){
ChartItem = mLineChartArray.get(i);
if (maxY < ChartItem.Connection) maxY = ChartItem.Connection;
graphAverage += ChartItem.Connection;
}
if (mZoomChart == true){
minY = maxY;
for (int i = 0; i<ValuesCount; i++){
ChartItem = mLineChartArray.get(i);
if (minY > ChartItem.Connection) minY = ChartItem.Connection;
}
} else {
minY = 0;
}
graphAverage = graphAverage / (float)ValuesCount;
//----------------------------------------------------------------------------------
// DRAW Y AXIS
//----------------------------------------------------------------------------------
float step = (float) (maxY - minY) / (float) (LABEL_Y_SHOW);
float stepY = (float) (mHeight - mGapTop - mGapBottom) / (float) (maxY - minY);
for (int i = 0; i <= LABEL_Y_SHOW ; i++) {
y = (float) ((i * step) * stepY);
OldX = (float) (mGapLeft - 2);
OldY = (float) (mHeight - mGapBottom - y);
NewX = (float) (mWidth - mGapRight);
NewY = (float) (mHeight - mGapBottom - y);
if (i == 0) mCanvas.drawLine(OldX, OldY, NewX, NewY, mGridPaintFull);
else mCanvas.drawLine(OldX, OldY, NewX, NewY, mGridPaintDot);
//----------------------------------------------------------------------
value = (int) minY + (int)(i * step);
StrLablel = "" + value;
mCanvas.drawText(StrLablel, 2, NewY, mTitlePaint);
}
//----------------------------------------------------------------------------------
// DRAW X AXIS
//----------------------------------------------------------------------------------
float stepX = (float) (mWidth - mGapLeft - mGapRight) / (float) (LABEL_X_SHOW - 1);
float IndexStep = (float) ValuesCount / (float) LABEL_X_SHOW;
NewY = (float)(mHeight - mGapBottom);
String[] DateItem = new String[3];
for (int j = 0; j<LABEL_X_SHOW; j++){
Index = (int) ((float)(j)* (float)IndexStep);
if (Index >= ValuesCount) Index = ValuesCount - 1;
if (j == LABEL_X_SHOW -1) Index = ValuesCount - 1;
NewX = (float)( (float) mGapLeft + (float)(j*stepX) );
//--------------------------------------------------------------------------
if (j == 0) mCanvas.drawLine(NewX, mGapTop, NewX, NewY+3, mGridPaintFull);
else mCanvas.drawLine(NewX, mGapTop, NewX, NewY+3, mGridPaintDot);
//--------------------------------------------------------------------------
ChartItem = mLineChartArray.get(Index);
DateItem = ChartItem.Date.split("-");
StrLablel = DateItem[0] + "-" + DateItem[1];
mTitlePaint.getTextBounds(StrLablel, 0, StrLablel.length(), TextRect);
NewX = NewX - TextRect.right/2;
mCanvas.drawText(StrLablel, NewX, (mHeight - mGapBottom) + 13, mTitlePaint);
}
//--------------------------------------------------------------------------
// Draw Graph Lines
//--------------------------------------------------------------------------
float pointStepX = (float) (mWidth - mGapLeft - mGapRight) / (float) (ValuesCount-1);
float pointStepY = (float) (mHeight - mGapTop - mGapBottom) / (float) (maxY - minY );
float drawOffsetX = (float) mGapLeft;
float drawOffsetY = (float)( mHeight - mGapBottom);
//--------------------------------------------------------------------------
ChartItem = mLineChartArray.get(0);
OldX = drawOffsetX;
OldY = drawOffsetY - pointStepY * (float) (ChartItem.Connection - minY);
Path mPath = new Path();
for (int k = 0; k<ValuesCount; k++){
ChartItem = mLineChartArray.get(k);
NewX = drawOffsetX + ( pointStepX * (float) k );
NewY = drawOffsetY - ( pointStepY * (float) ( ChartItem.Connection - minY) );
if (mFillGraph == false){
mCanvas.drawLine(OldX, OldY, NewX, NewY, mLinePaint);
} else {
mPath.rewind();
mPath.moveTo(OldX, OldY);
mPath.lineTo(NewX, NewY);
mPath.lineTo(NewX, drawOffsetY);
mPath.lineTo(OldX, drawOffsetY);
mPath.close();
mCanvas.drawPath(mPath, mLinePaint);
}
OldX = NewX;
OldY = NewY;
}
//--------------------------------------------------------------------------
// Average Line
//--------------------------------------------------------------------------
if (mAverageLine == true){
mAveragePaint.setColor(0xffff0000);
mAveragePaint.setStyle(Paint.Style.FILL);
mAveragePaint.setAntiAlias(true);
mAveragePaint.setStrokeWidth(1.5f);
mAveragePaint.setPathEffect( new DashPathEffect(new float[] {5, 5}, 1) );
//--------------------------------------------------------------------------
mAverageTxtPaint.setStyle(Paint.Style.FILL);
mAverageTxtPaint.setTextAlign(Paint.Align.LEFT);
mAverageTxtPaint.setColor(0xffff0000);
mAverageTxtPaint.setTextSize(10);
//--------------------------------------------------------------------------
DecimalFormat FloatFormatter = new DecimalFormat("0.##");
String StrAverage = "" + FloatFormatter.format (graphAverage);
mAverageTxtPaint.getTextBounds(StrAverage, 0, StrAverage.length(), TextRect);
NewX = (float)( mWidth - mGapRight - TextRect.right) - 3.0f;
NewY = (float)( mGapTop - TextRect.top) + 3.0f;
mCanvas.drawText(StrAverage, NewX, NewY, mAverageTxtPaint);
NewY = drawOffsetY - ( pointStepY * (float) ( graphAverage - minY) );
mCanvas.drawLine(mGapLeft, NewY, mWidth - mGapRight, NewY, mAveragePaint);
}
//--------------------------------------------------------------------------
// Title
//--------------------------------------------------------------------------
ChartItem = mLineChartArray.get(0);
String Title = mLineChartArray.get(0).Date + " ... " + mLineChartArray.get(ValuesCount-1).Date;
mTitlePaint.getTextBounds(Title, 0, Title.length(), TextRect);
mCanvas.drawText(Title, (mWidth - TextRect.right)/2, 10, mTitlePaint);
//--------------------------------------------------------------------------
Title = mUrl;
mTitlePaint.getTextBounds(Title, 0, Title.length(), TextRect);
mCanvas.drawText(Title, (mWidth - TextRect.right)/2, 20, mTitlePaint);
}
//--------------------------------------------------------------------------------------
public void setGeometry(int width, int height, int GapLeft, int GapRight, int GapTop, int GapBottom) {
mWidth = width;
mHeight = height;
mGapLeft = GapLeft;
mGapRight = GapRight;
mGapTop = GapTop;
mGapBottom = GapBottom;
}
//--------------------------------------------------------------------------------------
public void setSkinParams(int bgColor, int gridColor, int lineColor, int textColor, boolean FillGraph, boolean AverageLine, boolean ZoomChart) {
mBgColor = bgColor;
mGridColor = gridColor;
mLineColor = lineColor;
mTextColor = textColor;
mFillGraph = FillGraph;
mAverageLine = AverageLine;
mZoomChart = ZoomChart;
}
//--------------------------------------------------------------------------------------
public void setData( List<LineChartItem> ChartArray, String Url) {
mLineChartArray = ChartArray;
mUrl = Url;
isReadyToDraw = true;
}
}
As always, you can download all files HERE.


hi,
for (int i = StartIndex; i < EndIndex; i++) {
ItemNode = (Element) AllNodes.item(i);
Day = ItemNode.getAttribute("day");
Month = ItemNode.getAttribute("month");
Year = ItemNode.getAttribute("year");
try {
Connection = NumberFormater.parse(ItemNode.getAttribute("value")).intValue();
} catch (java.text.ParseException e) {
e.printStackTrace();
}
ChartItem = new LineChartItem();
ChartItem.Date = Day + "-" + Month + "-" + Year;
ChartItem.Connection = Connection;
LineChartArray.add(ChartItem);
}
getting error with the nodes..i do not understand how to get AllNodes and getattribute("day") etc….
hi again,
this sample method is for extract data from xml file, and you must have a valid xml document which contain this data like :
…
<?xml version="1.0"?>
<root>
…
<item year="2009" month="09" day="01" value="15"/>
<item year="2009" month="09" day="02" value="26"/>
<item year="2009" month="09" day="03" value="32"/>
…
</root>
…
for your test you can replace this by :
…
Random mNumGen = new Random();
LineChartItem ChartItem;
int Day = 16;
for (int i = 0; i < 10; i++) { // 10 is the number of points for the chart !
ChartItem = new LineChartItem();
ChartItem.Date = "" + Day + "-06-2010";
ChartItem.Connection = mNumGen.nextInt(256);
LineChartArray.add(ChartItem);
Day ++;
}
…
thanks for reply..now i understand that
can u please explain me this as well
View_LineChart mTmpChartView = new View_LineChart(this);
mTmpChartView.setGeometry(mHolderWidth, mHolderHeight, CHART_LEFT_GAP, CHART_RIGHT_GAP, CHART_GAP_TOP, CHART_GAP_BOTTOM);
if(mWhiteGraph == true){
mTmpChartView.setSkinParams(mWhiteBgColor, mWhiteGridColor, mWhiteLineColor, mWhiteTxtColor, mFillGraph, mAverageLine, mZoomChart);
} else {
mTmpChartView.setSkinParams(mBlackBgColor, mBlackGridColor, mBlackLineColor, mBlackTxtColor, mFillGraph, mAverageLine, mZoomChart);
}
mTmpChartView.setData(LineChartArray, mUrl);
mTmpChartView.invalidate();
what would be the parameter value for this part.. (mWhiteGraph , mWhiteBgColor,..etc) as i tried and not getting graph properly.
thanks
Sample for constants and vars :
//—————————————————————-
// border constant => empty zone around the line chart
//—————————————————————-
private static final int CHART_LEFT_GAP = 20;
private static final int CHART_RIGHT_GAP = 16;
public static final int CHART_GAP_TOP = 24;
public static final int CHART_GAP_BOTTOM = 20;
//—————————————————————-
// Size of the parent view container => fix the size of line chart view to adjust it with his parent before the drawing
//—————————————————————-
private int mHolderWidth = 150;
private int mHolderHeight = 50;
//—————————————————————-
// fill the graph if true, or draw only a line if false
//—————————————————————-
private boolean mFillGraph = false;
//—————————————————————-
//Set to true => the background is white, else black !!!
//—————————————————————-
private boolean mWhiteGraph = false;
//—————————————————————-
// Draw the average line if true => Sum(Y_value)/Y_value_count
//—————————————————————-
private boolean mAverageLine = true;
//—————————————————————-
// zoom the graph between the min and max y value, else the graph is draw between 0 -> y max value
//—————————————————————-
private boolean mZoomChart = true;
//—————————————————————-
// colors used if mWhiteGraph == true
//—————————————————————-
private int mWhiteBgColor = 0xffffff00;
private int mWhiteTxtColor = 0xffffff00;
private int mWhiteGridColor = 0xffffff00;
private int mWhiteLineColor = 0xffffff00;
//—————————————————————-
// colors used if mWhiteGraph == false
//—————————————————————-
private int mBlackBgColor = 0xffffff00;
private int mBlackTxtColor = 0xffffff00;
private int mBlackGridColor = 0xffffff00;
private int mBlackLineColor = 0xffffff00;
thanks a lot…
Any way this can work off a csv file formated as so: yyyy-mm-dd hh:mm, data … or even a sqlite db in the droid?
The principle is the same for any data source.
The key is to format the date
.
This format has been used just because it is more convenient when displaying values on the axis X.
can you convert this to a real time barchart? Much appreciated. Seb
Hey Sebastian, sorry I don’t have any time to do this for now, but if it ‘s possible (time…) I add an method to this post for your request !
Hi, can you convert this to a real time barchart. it would be very helpful. Thanks
Could you post d complete project as it was done in case of Pie Chart…….
Thanx in advance
Hi , Thanks a lot. gr8 post. Could you please upload the project for android as it was done in the case of Pie Chart.
Please please.. I’m a newbie to android, I have tried but ending with errors.
Thanks in Advance.
Harry.