graph_bar
Android用のグラフのライブラリーもあるみたいですけど、いまいちなのでオリジナルな棒グラフを作成してみます。プログラミングの大した技量が僕には無いので至ってシンプルな棒グラフです。ちなみに、オリジナルな棒グラフは配布中のカウンターアプリにも乗せているので、試してみてください(営業)。


カスタムビューの作成

クラス名は何でも良いんですけど、棒グラフだし、僕のイニシャルはHSなので、HsGraphBarというクラスを作成します。extendsするのは、Viewでもイイ気がするんですが、なんとなくLinearLayoutを拡張します。
package jp.blogcms.livedoor.hsgraph;

import android.content.Context;
import android.graphics.Canvas;
import android.widget.LinearLayout;

public class HsGraphBar extends LinearLayout{
	
	/**
	 * コンストラクター
	 * Viewのクラス内では、getContext()でcontextの取得ができる
	 * 
	 * @param context
	 */
	public HsGraphBar(Context context) {
		super(context);
	}
	
	/**
	 * AddViewなんかで実際にviewが追加されたときに呼び出される
	 * 
	 */
	@Override
	protected void onAttachedToWindow(){
		super.onAttachedToWindow();

	}
	
	/**
	 * タテヨコの大きさを決める時に呼び出される
	 * Viewの大きさを指定したい場合は、ここで指定
	 * 
	 */
	@Override
	protected void onMeasure(int width, int height) {
		super.onMeasure(width, height);
	}
	
	
	/**
	 * レイアウトを決める時に呼び出される
	 * 
	 */
	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		super.onLayout(changed, l, t, r, b);
	}
	
	/**
	 * 描画する!
	 * 
	 */
	@Override
	public void onDraw(Canvas canvas) {
		super.onDraw(canvas);
	}
}
Viewのきっと使うであろうイベントです。この上の並んだ順番で、それぞれのメソッドはViewを作成するときに必ず呼び出されます。グラフ作成で大事なのは、onMeasureとonDarawかと思います。使わないイベントは消しちゃって構いません。まぁ、正直なところandroidのViewの生成のされ方に関して、僕も大して理解してません・・・。察しの通り、上記はイベントを書いただけなんで、内容は無いよう・・・。



グラフの大きさを指定する

まずは、グラフの大きさを決めたいと思います。サイズ決めって凡化を考えると凄く面倒なので、ここでは、View内でグラフの大きさを指定してしまいます!

高さは、300dpくらいがイイなとじっちゃんが言ってたので300とします。幅は、親のLinearLayoutの幅をそのまま使うようにします。xmlで書くときのmatch_parent(fill_parent)です。

 まず、HsGraphBarクラス内に高さを持たせたフィールドを宣言します。じっちゃんが300って言ってたので300。
int graphHeight = 300;
 

で、幅と合わせて大きさをonMeasuredイベント内で宣言します

	@Override
	protected void onMeasure(int width, int height) {
		super.onMeasure(width, height);
		
		//グラフのサイズを指定
		setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, graphHeight));
	}
解りにくいんですけど、このHsGraphBarがLinearLayoutを拡張しているからLinearLayout.LayoutParamsを使っているのでは無くて、親の要素がLinearLayoutだからLinearLayout.LayoutParamsです。つまるところ、HsGraphBarは、LinearLayoutの下でしか生きていけません。これ、たぶん、駄目な書き方です。はい。 本当は、クラスの外から指定してやるのが正解なんだろーけど、まぁ、いいんじゃないのかっていう。これで、グラフの大きさが指定できました!ひゃっほーい!




グラフにするデータを用意する

必要なデータは、
  • グラフ一本一本の値
  • x軸のラベル
で、グラフの値とx軸のラベルは、それぞれリンクしているとします。Javaをよく知らないし、そもそもプログラミングがよくわかってないので、そのまんま配列を使ってデータを扱いたい。そう僕は思います。そんな訳でHsGraph内にフィールドを作成します

//グラフの値
List<Integer>values = new ArrayList<Integer>();

//グラフのxラベル
List<String>xLabels = new ArrayList<String>();
 valuesとxLabelsは、別個のフィールドなんですけど、それぞれのインデックスは同じものを指してるようにします(してください)。LinkedListがいいんだぜ(キリッ って記事を見かけるんですけど、LinkedListがイイ人は使えばいいのかと。

ついでにHsGraph内にセッターも作成します
/**
 * 棒グラフの値を一度にセットする
 * 
 * 
 */
public void setValues(List<Integer>values){
	this.values = values;
}


/**
 * x軸のラベルを一度に全部セットする
 * 
 * 
 */
public void setXLabels(List<String>xLabels){
	this.xLabels = xLabels;
}	


ここまでのコードのまとめ

サクッと書いてサクッと寝るつもりだったんですけど、予想外に長くなってきたのでここまでのコードの全部を載せたいと思います。完成してません。
package jp.blogcms.livedoor.hsgraph;

import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.graphics.Canvas;
import android.widget.LinearLayout;

public class HsGraphBar extends LinearLayout{
	
	//グラフの高さ
	int graphHeight = 300;
	
	//グラフの値
	List<Integer>values = new ArrayList<Integer>();
	
	//グラフのxラベル
	List<String>xLabels = new ArrayList<String>();
	
	
	
	/**
	 * コンストラクター
	 * Viewのクラス内では、getContext()でcontextの取得ができる
	 * 
	 * @param context
	 */
	public HsGraphBar(Context context) {
		super(context);
	}
	
	
	/**
	 * 棒グラフの値を一度にセットする
	 * 
	 * 
	 */
	public void setValues(List<Integer>values){
		this.values = values;
	}
	
	
	/**
	 * x軸のラベルを一度に全部セットする
	 * 
	 * 
	 */
	public void setXLabels(List<String>xLabels){
		this.xLabels = xLabels;
	}	
	
	
	/**
	 * AddViewなんかで実際にviewが追加されたときに呼び出される
	 * 
	 */
	@Override
	protected void onAttachedToWindow(){
		super.onAttachedToWindow();

	}
	
	/**
	 * タテヨコの大きさを決める時に呼び出される
	 * Viewの大きさを指定したい場合は、ここで指定
	 * 
	 */
	@Override
	protected void onMeasure(int width, int height) {
		super.onMeasure(width, height);
		
		//グラフのサイズを指定
		setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, graphHeight));
	}
	
	
	/**
	 * レイアウトを決める時に呼び出される
	 * 
	 */
	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		super.onLayout(changed, l, t, r, b);
	}
	
	/**
	 * 描画する!
	 * 
	 */
	@Override
	public void onDraw(Canvas canvas) {
		super.onDraw(canvas);
	}
}

 

カスタムビューをプログラム上で追加

ちなみに、HsGraphBarをプログラム上で実際のレイアウトに追加する方法は、こんな感じです。Activity内やFragment内から呼び出してください。
		//グラフビューを作成
		HsGraphBar graph = new HsGraphBar(this);
		
		
		//グラフの値
		List<Integer>values = new ArrayList<Integer>();

		//グラフのxラベル
		List<String>xLabels = new ArrayList<String>();
		
		//偽のデータを用意します
		Random rnd = new Random(); 
		for(int i=0;i<5;i++){
			values.add(rnd.nextInt(100));
			xLabels.add(String.valueOf(i) + "番目");
		}
		
		//値をグラフに渡す
		graph.setValues(values);
		
		//xラベルをグラフに渡す
		graph.setXLabels(xLabels);
		
		
		
		//グラフを挿入
		//追加できるのはLinearLayoutのみ!
		((LinearLayout)findViewById(R.id.graphTarget)).addView(graph);

 

グラフをcanvasを使って描画する前に下準備

前置きが長くなりました。描画したいと思うのですが、一個だけ。デフォルトだとonDrawが呼び出されないので、呼び出すように設定します。

	public HsGraphBar(Context context) {
		super(context);
		
		setWillNotDraw(false);
	}

setWillNotDraw()に引数falseを渡しておきます。こうすることでonDrawが呼び出されます。



グラフをcanvasを使って描画する前に、データの整理

最大値を探し出す
グラフの棒の高さは、valuesそれぞれの値に基づいてきまります。で、最大値がグラフの高さとなるので、とりあえず、のちのち便利な最大値を保持するフィールドを宣言します。

	//valuesの中の最大値
	Integer maxValue = 0;
valuesの最大値を探し出します。
@Override
public void onDraw(Canvas canvas) {
	super.onDraw(canvas);
	
	//valuesの中の最大値を見つけ出す
	for(int i=0;i<values.size();i++){
		if(values.get(i)>maxValue){
			maxValue = values.get(i);
		}
	}
}
どのタイミングで探し出すのがベストなのかわかんないんですけど、ここではonDrawで呼び出してます。


グラフのサイズを決定する
細かいんですけど、このHsGraphのサイズが棒グラフそのままのサイズになるわけではありません。実際に棒グラフを描くスペースは、ラベルのサイズを考慮してやる必要があります。イメージとしてはこんな感じ。
graph_width_height

実際のグラフのサイズを計算していきます。

まずは、高さから計算していきましょう。計算したグラフの高さは、HsGraphクラス内にplotHeightフィールドを作成してそこで保持するようにします。
	@Override
	public void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		
		//valuesの中の最大値を見つけ出す
		for(int i=0;i<values.size();i++){
			if(values.get(i)>maxValue){
				maxValue = values.get(i);
			}
		}
		
		//ラベルのペイントを取る
		Paint txtPaint = getLabelPaint();
		
		//高さを出したいので、仮に一番最初のラベルの文字列を取得します
		String xLabel = xLabels.get(0); 
		
		//四角形
		Rect bounds = new Rect();
		
		//指定した文字列を入れる四角形を作成
		txtPaint.getTextBounds(xLabel,0,xLabel.length(),bounds);
		
		//四角形の高さを取得 = 文字列の高さ
		int xHeight = bounds.height();
		
		//グラフだけの高さを計算
		plotHeight = getMeasuredHeight()-xHeight;
		
		
		
	}
	
	/**
	 * ラベルの色・サイズはY軸、X軸ともに共通のを使用するので一つにまとめます
	 * Paintは、色やサイズなんかを保持するオブジェクトです。
	 * 
	 * 
	 */
	private Paint getLabelPaint(){

		Paint paint = new Paint();
		//アンチエイリアス
		paint.setAntiAlias(true);
		//文字の色
		paint.setColor(Color.parseColor("#777777"));
		//文字のサイズ
		paint.setTextSize(15);		
		
		
		return paint;		
	}


次にY軸の幅を出して、グラフの幅を計算したいと思います。ただY軸ラベルの値はまだ計算してないので、ついでに計算してyLabelsとして配列の中にいれたいと思います。まずは、y軸の線の数を決めます。フィールドで宣言するだけ。ばっちゃんが5個くらい線があるといいよって言ってたので5個線を書こう。そう思います。

//ヨコの線の数を指定します。5個くらい線を描こうかと思います・・・。
Integer yLineCount = 5;


	@Override
	public void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		
		//valuesの中の最大値を見つけ出す
		for(int i=0;i<values.size();i++){
			if(values.get(i)>maxValue){
				maxValue = values.get(i);
			}
		}
		
		//ラベルのペイントを取る
		Paint txtPaint = getLabelPaint();
		
		//高さを出したいので、仮に一番最初のラベルの文字列を取得します
		String xLabel = xLabels.get(0); 
		
		//四角形
		Rect bounds = new Rect();
		
		//指定した文字列を入れる四角形を作成
		txtPaint.getTextBounds(xLabel,0,xLabel.length(),bounds);
		
		//四角形の高さを取得 = 文字列の高さ
		int xHeight = bounds.height();
		
		//グラフだけの高さを計算
		plotHeight = getMeasuredHeight()-xHeight;
		
		
		//### Y軸ラベルを作成 ###
		
		//それぞれのYの値を計算。誤差を少なくする為に一旦floatに変えてます
		Float eachY = maxValue.floatValue()/yLineCount.floatValue();
		Float tmpY = 0F;
		
		//先にフィールドとして宣言したyLabelsを初期化
		yLabels = new ArrayList<String>();
		
		for(int i=0; i<yLineCount; i++){
			yLabels.add( String.valueOf(tmpY.intValue()));
			tmpY = tmpY + eachY;
		}
		
		
		
		
		
		
	}

xのラベルの高さをだしたようにyのラベルの幅を取得して、グラフの幅を計算します。
	public void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		
		//valuesの中の最大値を見つけ出す
		for(int i=0;i<values.size();i++){
			if(values.get(i)>maxValue){
				maxValue = values.get(i);
			}
		}
		
		//ラベルのペイントを取る
		Paint txtPaint = getLabelPaint();
		
		//高さを出したいので、仮に一番最初のラベルの文字列を取得します
		String txt = xLabels.get(0); 
		
		//四角形
		Rect bounds = new Rect();
		
		//指定した文字列を入れる四角形を作成
		txtPaint.getTextBounds(txt,0,txt.length(),bounds);
		
		//四角形の高さを取得 = 文字列の高さ
		int xHeight = bounds.height();
		
		//グラフだけの高さを計算
		plotHeight = getMeasuredHeight()-xHeight;
		
		
		//### Y軸ラベルを作成 ###		
		//それぞれのYの値を計算。誤差を少なくする為に一旦floatに変えてます
		Float eachY = maxValue.floatValue()/yLineCount.floatValue();
		Float tmpY = 0F;
		
		//先にフィールドとして宣言したyLabelsを初期化
		yLabels = new ArrayList<String>();
		
		for(int i=0; i<yLineCount; i++){
			yLabels.add( String.valueOf(tmpY.intValue()));
			tmpY = tmpY + eachY;
		}
		
		//y軸の幅を取得します。変数はxのを流用。
		//一番最後の値がでかそうなので一番最後の文字列を取得
		txt = yLabels.get(yLabels.size()-1);
		txtPaint.getTextBounds(txt,0,txt.length(),bounds);
		int yWidth = bounds.width();
		
		//グラフだけの幅を計算
		plotWidth = getMeasuredWidth() - yWidth;
		
		
		
		
		
		
	}

ちなみにgetMeasuredWidth()やgetMeasuredHeight()は、クラスの幅や高さを取るメソッドです。オリジナルなビューを作るときには良く使います。きっと。あと、書いてて思ったんですけど、この辺の計算は、onLayoutとかonMeasureなんかでした方がいいのかも・・・。onDrawでオブジェクトを作成すんなハゲってEclipseさんで電球マークが表示される・・・。




canvasを使ってグラフを描画する!

androidのdrawには、後から書いたものが上にでるの法則があるらしく、1.ラベル、2.グラフの背景、3.バー本体の順番に書いていきたいと思います。なにやらonDrawが長くなってきたので、それぞれメソッドにわけてみます。

	public void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		
		//valuesの中の最大値を見つけ出す
		for(int i=0;i<values.size();i++){
			if(values.get(i)>maxValue){
				maxValue = values.get(i);
			}
		}
		
		//ラベルのペイントを取る
		Paint txtPaint = getLabelPaint();
		
		//高さを出したいので、仮に一番最初のラベルの文字列を取得します
		String txt = xLabels.get(0); 
		
		//四角形
		Rect bounds = new Rect();
		
		//指定した文字列を入れる四角形を作成
		txtPaint.getTextBounds(txt,0,txt.length(),bounds);
		
		//四角形の高さを取得 = 文字列の高さ
		int xHeight = bounds.height();
		
		//グラフだけの高さを計算
		plotHeight = getMeasuredHeight()-xHeight;
		
		
		//### Y軸ラベルを作成 ###

		
		//それぞれのYの値を計算。誤差を少なくする為に一旦floatに変えてます
		Float eachY = maxValue.floatValue()/yLineCount.floatValue();
		Float tmpY = 0F;
		
		//先にフィールドとして宣言したyLabelsを初期化
		yLabels = new ArrayList<String>();
		
		for(int i=0; i<yLineCount; i++){
			yLabels.add( String.valueOf(tmpY.intValue()));
			tmpY = tmpY + eachY;
		}
		
		//y軸の幅を取得します。変数はxのを流用。
		//一番最後の値がでかそうなので一番最後の文字列を取得
		txt = yLabels.get(yLabels.size()-1);
		txtPaint.getTextBounds(txt,0,txt.length(),bounds);
		int yWidth = bounds.width();
		
		//グラフだけの幅を計算
		plotWidth = getMeasuredWidth() - yWidth;
		
		
		
		//描画する!
		drawLabels(canvas);
		drawBackground(canvas);
		drawBars(canvas);
		
	}
	
	/**
	 * ラベルの色・サイズはY軸、X軸ともに共通のを使用するので一つにまとめます
	 * Paintは、色やサイズなんかを保持するオブジェクトです。
	 * 
	 * 
	 */
	private Paint getLabelPaint(){

		Paint paint = new Paint();
		//アンチエイリアス
		paint.setAntiAlias(true);
		//文字の色
		paint.setColor(Color.parseColor("#777777"));
		//文字のサイズ
		paint.setTextSize(15);		
		
		
		return paint;		
	}
	
	
	/**
	 * ラベルを書く
	 * 
	 * @param canvas
	 */
	private void drawLabels(Canvas canvas){
		
	}
	
	
	/**
	 * 背景を書く
	 * 
	 * @param canvas
	 */
	private void drawBackground(Canvas canvas){
		
	}	
	
	
	/**
	 * バーを書く
	 * 
	 * @param canvas
	 */
	private void drawBars(Canvas canvas){
		
	}	
 
X軸とy軸のラベルを書く

 Yのラベルを書いていきます。
	private void drawLabels(Canvas canvas){
		
		Paint paint = getLabelPaint();
		
		
		
		//## Yのラベルを描く!
		//線のそれぞれの高さを計算
		Float yEach = plotHeight.floatValue()/yLineCount.floatValue();
		
		//yLabelsは、0から入ってるのでグラフの下の部分から描画します。
		Float yCurrent = plotHeight.floatValue();
		
		
		for(int i=0; i< yLabels.size();i++){
			//テキストを描画します。
			canvas.drawText(yLabels.get(i), 0, yCurrent, paint);
			
			//次の位置を計算
			yCurrent = yCurrent - yEach;
		}
		

	}
例によって誤差が怖いのでfloatにしてるんですが、最初からfloatで保持しとけばいいんじゃね?って驚愕の事実に前から薄々気づいていたのは内緒です。

canvas.drawText(yLabels.get(i), 0, yCurrent, paint);

  • 第一引数に文字列
  • 第二引数にxの位置
  • 第三引数にyの位置
  • 第三引数にPaintオブジェクト
yのラベルなのでxの位置は固定してyの位置だけグラフの下から上がっていってます。また、paintでalignの設定をしていないので、drawTextで指定するxとyは、文字列の開始位置となります。これ右揃えがいいんじゃねーのかと気づいたので変えてみます。

		Paint paint = getLabelPaint();
		//右揃えにする
		paint.setTextAlign(Align.RIGHT);
		
		
		//## Yのラベルを描く!
		//線のそれぞれの高さを計算
		Float yEach = plotHeight.floatValue()/yLineCount.floatValue();
		
		//yLabelsは、0から入ってるのでグラフの下の部分から描画します。
		Float yCurrent = plotHeight.floatValue();
		
		//前に計算した文字列の幅をもっかい計算するという愚行
		int xPos = getMeasuredWidth() - plotWidth;
		
		for(int i=0; i< yLabels.size();i++){
			//テキストを描画します。
			//xの位置は、右側を指定
			canvas.drawText(yLabels.get(i), xPos, yCurrent, paint);
			
			//次の位置を計算
			yCurrent = yCurrent - yEach;
		}
この場合のxは、文字列の右側が開始位置となります。


次、Xのラベル!Yと似たようなもんです!今度は、yの値を固定してxをずらしていきます!
		//中央ぞろえ
		paint.setTextAlign(Align.CENTER);
		
		//それぞれの幅を計算
		Float xEach = plotWidth.floatValue()/((Integer)xLabels.size()).floatValue();
		
		//さらにそれぞれの真ん中の位置を計算
		Float center = xEach/2;
		
		//yで計算したxの位置を流用。
		Float tmpX = xPlotStart + center;
		
		for(int i=0;i<xLabels.size();i++){
			canvas.drawText(xLabels.get(i), tmpX, getMeasuredHeight(), paint);
			tmpX = tmpX + xEach;
		}
棒の幅なんですけど、ここでは、ビューの幅と棒の数から幅を割り出しています。ここの値は固定にしてもいいのかと。お好きなように・・・・。


で、実行してみるとこんな感じ
Screenshot_2013-06-22-11-43-28
 
 Y軸のラベルの位置をミスってますな。ラベルの文字の大きさの半分上に上げたほうがいいのかも・・・。
	/**
	 * ラベルを書く
	 * 
	 * @param canvas
	 */
	private void drawLabels(Canvas canvas){
		
		Paint paint = getLabelPaint();
		//右揃えにする
		paint.setTextAlign(Align.RIGHT);
		
		
		//## Yのラベルを描く!
		//線のそれぞれの高さを計算
		Float yEach = plotHeight.floatValue()/yLineCount.floatValue();
		
		
		
		//yLabelsは、0から入ってるのでグラフの下の部分から描画します。
		Float yCurrent = plotHeight.floatValue();
		
		//微調整。文字の大きさの半分だけずらす
		Rect bounds = new Rect();
		paint.getTextBounds(yLabels.get(0), 0, 1, bounds);
		yCurrent = yCurrent + (bounds.height()/2);
		
		
		//前に計算した文字列の幅をもっかい計算するという愚行
		int xPlotStart = getMeasuredWidth() - plotWidth;
		
		for(int i=0; i< yLabels.size();i++){
			//テキストを描画します。
			//xの位置は、右側を指定
			canvas.drawText(yLabels.get(i), xPlotStart, yCurrent, paint);
			
			//次の位置を計算
			yCurrent = yCurrent - yEach;
		}
		
		
		//## Xのラベルを描く!
		
		//中央ぞろえ
		paint.setTextAlign(Align.CENTER);
		
		//それぞれの幅を計算
		Float xEach = plotWidth.floatValue()/((Integer)xLabels.size()).floatValue();
		
		//さらにそれぞれの真ん中の位置を計算
		Float center = xEach/2;
		
		//yで計算したxの位置を流用。
		Float tmpX = xPlotStart + center;
		
		for(int i=0;i<xLabels.size();i++){
			canvas.drawText(xLabels.get(i), tmpX, getMeasuredHeight(), paint);
			tmpX = tmpX + xEach;
		}
		
		
	}


背景を書く!

背景色!
ほんのりと背景色を付けます。
	/**
	 * 背景を書く
	 * 
	 * @param canvas
	 */
	private void drawBackground(Canvas canvas){
		Paint paint = new Paint();
		paint.setAntiAlias(true);
		
		int plotStartX = getMeasuredWidth() - plotWidth;
		int plotStartY = 0;
		
		
		//background
		paint.setColor(Color.parseColor("#EFEFEF"));
		Rect rect = new Rect(plotStartX, 0, getMeasuredWidth(), plotHeight);
	    canvas.drawRect(rect, paint);			
	}	
drawRectは、四角形を描きます!引数がleft,top,right,bottomってなってるんですけど、それぞれの線の位置を思い浮かべれば解り易いのかと・・・。leftは四角形の左側の線。topは上の線。rightは右側で、bottomは下の線・・・・。



線を引く!

	    //## yの線を描く!
	    paint.setColor(Color.parseColor("#999999"));
		Float yEach = plotHeight.floatValue()/yLineCount.floatValue();

		//yLabelsは、0から入ってるのでグラフの下の部分から描画します。
		Float yCurrent = plotHeight.floatValue();
		
		//前に計算した文字列の幅をもっかい計算するという愚行
		int xPlotStart = getMeasuredWidth() - plotWidth;
		
		for(int i=0; i< yLabels.size();i++){
			//線を描きます
			canvas.drawLine(xPlotStart, yCurrent, getMeasuredWidth(), yCurrent, paint);
			
			//次の位置を計算
			yCurrent = yCurrent - yEach;
		}	
ほぼラベルを書いた時と一緒です!僕自身、コピペしてます!てか、ラベルの部分で描けば良かったのかも!まぁ、いいさ・・・。違うのが

canvas.drawLine(xPlotStart, yCurrent, getMeasuredWidth(), yCurrent, paint);

xの幅は固定で、yの位置だけ順番に上がっていってます。また、こんな感じにすれば、

	    //## yの線を描く!
		Float yEach = plotHeight.floatValue()/yLineCount.floatValue();

		//yLabelsは、0から入ってるのでグラフの下の部分から描画します。
		Float yCurrent = plotHeight.floatValue();
		
		//前に計算した文字列の幅をもっかい計算するという愚行
		int xPlotStart = getMeasuredWidth() - plotWidth;
		
		for(int i=0; i< yLabels.size();i++){
			
			if(i==0){
				paint.setStrokeWidth(2);
				paint.setColor(Color.parseColor("#777777"));
			}else{
				paint.setStrokeWidth(1);
				paint.setColor(Color.parseColor("#AAAAAA"));
			}
			
			//線を描きます
			canvas.drawLine(xPlotStart, yCurrent, getMeasuredWidth(), yCurrent, paint);
			
			//次の位置を計算
			yCurrent = yCurrent - yEach;
		}	

0のラインだけ強調されます。






棒を描く!

		Paint paint = new Paint();
		paint.setAntiAlias(true);		
		
		paint.setColor(Color.parseColor("#777777"));
		
		//それぞれの幅を計算
		Float xEach = plotWidth.floatValue()/((Integer)xLabels.size()).floatValue();
		
		//棒グラフに適度な余白を追加します。
		Float barSpace = 20F;
		
		//前に計算した文字列の幅をもっかい計算するという愚行
		Integer xPlotStart = getMeasuredWidth() - plotWidth;	
		
		//最初のxの位置を計算
		//xの開始位置
		Float tmpXLeft = xPlotStart.floatValue() + barSpace ;
		//xの終わり
		Float tmpXRight = xPlotStart + xEach - barSpace ;
		
		
		//valuesの値1に対する高さを計算
		Float eachHeight = plotHeight.floatValue()/maxValue.floatValue();
		
		for(int i=0;i<values.size();i++){
			canvas.drawRect(tmpXLeft, plotHeight -(eachHeight*values.get(i)), tmpXRight, plotHeight, paint);
			tmpXLeft = tmpXLeft + xEach;
			tmpXRight = tmpXRight + xEach;
		}		

xの開始位置(Left)は、yラベルの幅と棒の余白を考慮した場所となります。xの終了位置(Right)は、yラベルの幅と棒の幅を足して、余白部分を引いています。forの中で回しているんですけど、あとは、棒の大きさ分ずつヨコにずらしていってます



あと、yの位置なんですけど、まず、値1に対する高さを計算します。このvalue1に対する高さと実際の値を掛けたのが棒の上の位置(TOP)になります。yの下の位置(Bottom)は、グラフの高さで固定です。実行するとこんな感じ。


Screenshot_2013-06-22-12-42-22
 
グラフですね。

でまぁ、問題点はいろいろあるんですけど、パッと見た感じで
  • ラベルの大きさがぴったりすぎだ。ハゲ。
  • 棒グラフに上の余白がほしいぞ。ハゲ
  •  デザインどーにかしろよ。ハゲ。

ラベルの大きさは、plotWidthとplotHeightを出す部分で文字列の幅・高さを2倍なんかにしてやるといいのかも。棒グラフの上の余白は、maxValueを計算したときに、1.2とか1.3とかを掛けてやれば、余白が生まれます。デザインに関しては、しらねーよハゲ。 

 あと、yのライン数は5に指定してるんですけど、valuesの値が全て5未満だった場合は変なことになるのかも。


追加したらいいかもな機能

  • 棒グラフをタッチしたら反応する機能
  • スクロール

面倒くさくなってきたので、書かないですけど、タッチは、viewのイベントのonTouchEventを使って書けます!
スクロールは、HorizontalScrollViewなんかをうまい具合に仕込めばできます! 



まとめたファイル!

Screenshot_2013-06-22-13-15-12
若干修正して、Eclipseで作成したプロジェクトをまんまzip化したのがこちら!よくわからないんだけど、これってimportとかできんのかな・・・・?あと、このブログは無料ブログなんですけど、ファイル容量3MB中1.28MB使ってるらしい・・・。オワタ\(^o^)/