2011年11月12日土曜日

AFreeChartを動的に動かす

AFreeChartに関する情報が少ないと感じたので少しでも役に立てれば..

【やりたいこと】
Androidアプリでグラフを表示したい。
しかも動的(リアルタイム的な)に動かしたい!
※)TimeSeriesのサンプルをベースとする。

グラフ表示ライブラリを探した所いくつかあると言うことがわかった。
直感的にAFreeChartが一番良さそうな感じがしたので使うことにした!

AFreeChartの導入方法に関しては他のブログで書いている方が
いらっしゃるので割愛します。

サンプルソースを実行すると静的なグラフを表示することは簡単にできます。
動的にするにはAFreeChartが保有しているDatasetにデータを追加してあげると
自動的に再描画される。

なので、最初に思いつく方法としては動的にDatasetを更新して行けばできる、
だとうという方法です。
例えば、別スレッドを用意して100msごとにDatasetを更新するようなプログラムを
作って動かすとあることに気付くと思います。データ数が増えるに連れて、
恐らくGCが高頻度で発生し、
描画に時間がかかりUIスレッドに負担がかかり、
ANRを引き起こす可能性があります。

結果的にDatasetの更新をトリガーとして再描画して動的に見せる方法では、
限界があるということになります。

デフォルトのサンプルアプリだと、生成したAFreeChartインスタンスに
ChartChangeListenerがセットされているので、Dataset更新すると再描画が発生する。
それを抑えるためにはBaseView(511行目前後)でリスナーセットする処理をコメントアウトするか、
リスナーを登録を削除するか、
しないとダメです。

そうすると、Datasetを更新しても再描画されることはなくなります。
この状態だと再描画がなくてリアルタイムに見えません。

現段階での私のソリューションとしては以下のような感じです。

①Dataset更新用スレッド、再描画用スレッドの2つを生成する
②それぞれをstartさせる
③再描画スレッドは定期的にHandlerに対してView#postInvalidateを呼び出すだけ

パッと思いついただけの方法なので、もっと効率のいい方法があったら
ご教授願いますm(_ _)m

再描画スレッドは以下のような感じです。(一部抜粋)
もちろんこのままでは動かないので適宜に補完してください^^;

repaintThread = new Thread(new Runnable() {
  public void run() {
   while (!stop) {
    if (pause)
     pause();
    mainHandler.post(new Runnable() {
     public void run() {
      // repaint();
      timeChartView.postInvalidate();
     }
    });
    try {
     Thread.sleep(interval);
    } catch (InterruptedException e) {
     // TODO 自動生成された catch ブロック
     e.printStackTrace();
    }
   }
  }
 });
 repaintThread.start();

4 件のコメント:

  1. >>デフォルトのサンプルアプリだと、生成したAFreeChartインスタンスに
    ChartChangeListenerがセットされているので、Dataset更新すると再描画が発生する。
    それを抑えるためにはBaseView(511行目前後)でリスナーセットする処理をコメントアウトするか、
    リスナーを登録を削除するか、
    しないとダメです。

    リスナーを登録を削除する具体的な方法を教えていただけないでしょうか;
    そういったメソッドが存在するのでしょうか?

    返信削除
    返信
    1. カイトさま
      探すと言って放置状態になってしまってすみません。
      AFreeChartのサンプルコードにあるDemoView.java(BaseViewは自分で改名してしまったようです)というファイルの511行目付近に答えはあります。

      >リスナーを登録を削除する具体的な方法を教えていただけないでしょうか;
      >そういったメソッドが存在するのでしょうか?
      このデモコードではsetChartメソッド内で
      this.chart.addChangeListener(this);
      を実行しています。これはリスナー登録処理をしています。
      リスナーが登録された状態でDatasetを更新すると再描画処理されてしまうため、
      リアルタイムにデータを更新していると高頻度で再描画が発生してしまうということでした。

      ちなみにリスナー削除はthis.chart.removeChangeListener(this);です。
      this.chartは適宜に読み替えて下さい。

      削除
    2. Kawamura様

      ご解説頂きありがとうございます。
      私もお礼が遅くなってしまい申し訳ありません。

      大量の時系列データを表示するために質問させていただきました。
      最終的にはListViewにGraphViewをセットし、動かすという解決をしました。
      直接AFreeChartを動的に処理する方法も機会があれば試してみたいと思います。
      ありがとうございました。

      削除
  2. カイトさま
    コメントありがとうございます。ずいぶん前のソースなので帰ったら掘り返してみますので少しお待ちください。

    返信削除