Help us understand the problem. What is going on with this article?

Androidアプリ Spectrum Analyzer

More than 1 year has passed since last update.

0. はじめに

マイクから音を取得して周波数解析し、スペクトルを表示するSpectrum AnalyzerをAndroidアプリで作成。(リリースはしていません、リリースするレベルの物を作れないので)
しかも問題点が残ったまま。

グラフ描画ライブラリ【 MPAndroidChart 】を使用。

1. プロジェクトにMPAndroidChartを導入

build.gradleを編集してMPAndroidChartを導入する。
build.gradleはプロジェクトのルート直下とappフォルダ内の2種類がある。
ルート直下のbuild.gradleには以下を追加

build.gradle(Project)
allprojects {
    repositories {
        maven {
            url "https://jitpack.io"
        }
    }
}

appフォルダ内のbuild.gradleには以下を追加

build.gradle(app)
dependencies {
    implementation 'com.github.PhilJay:MPAndroidChart:v3.0.3'
}

2. ソースコード

import com.github.mikephil.charting.charts.LineChart;
import com.github.mikephil.charting.components.XAxis;
import com.github.mikephil.charting.components.YAxis;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.data.LineData;
import com.github.mikephil.charting.data.LineDataSet;
import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;

//MainActivity内
public LineChart lineChart;

//onCreate内
lineChart = findViewById(R.id.line_chart);
initChart();

//onCheckedChanged内(Switchを使っているので)
fft = new Thread(new Runnable() {
    @Override 
    public void run() {

        //フーリエ変換等処理後
        setData(decibelFrequencySpectrum);
    }
});
fft.start();

3. 初期化

グラフを初期化するinitChartメソッド

initChart
public void initChart() {

    lineChart.setTouchEnabled(true);
    lineChart.setDragEnabled(false);

    // Grid背景色
    lineChart.setDrawGridBackground(true);

    // no description text
    lineChart.getDescription().setEnabled(true);

    lineChart.setBackgroundColor(Color.LTGRAY);

    LineData data = new LineData();
    data.setValueTextColor(Color.BLACK);

    // add empty data
    lineChart.setData(data);

    // Grid縦軸を破線
    XAxis xAxis = lineChart.getXAxis();
    xAxis.setAxisMaximum(2048);
    xAxis.setAxisMinimum(0);
    xAxis.enableGridDashedLine(10f, 10f, 0f);
    xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);
    YAxis leftAxis = lineChart.getAxisLeft();
    // Y軸最大最小設定
    leftAxis.setAxisMaximum(0f);
    leftAxis.setAxisMinimum(-150f);
    // Grid横軸を破線
    leftAxis.enableGridDashedLine(10f, 10f, 0f);
    leftAxis.setDrawZeroLine(true);

    // 右側の目盛り
    lineChart.getAxisRight().setEnabled(false);
}

4. 描画

マイクで録音し、フーリエ変換したデータを描画するsetDataメソッド

setData
public void setData(double[] data) {

    ArrayList<Entry> values = new ArrayList<>();

    for (int i = 0; i < data.length; i++) {
        values.add(new Entry(i, (int)data[i], null, null));
    }

    LineDataSet set1;

    if (lineChart.getData() != null && lineChart.getData().getDataSetCount() > 0) {
        set1 = (LineDataSet) lineChart.getData().getDataSetByIndex(0);
        set1.setValues(values);
        lineChart.getData().notifyDataChanged();
        lineChart.notifyDataSetChanged();
    } else {
        // create a dataset and give it a type
        set1 = new LineDataSet(values, "Spectrum");

        set1.setDrawIcons(false);
        set1.setColor(Color.rgb(0, 0, 240));
        set1. setDrawCircles(false);
        set1.setLineWidth(0.5f);
        set1.setValueTextSize(0f);
        set1.setDrawFilled(false);
        set1.setFormLineWidth(1f);
        set1.setFormLineDashEffect(new DashPathEffect(new float[]{10f, 5f}, 0f));
        set1.setFormSize(15.f);
        set1.setDrawValues(true);

        ArrayList<ILineDataSet> dataSets = new ArrayList<>();
        dataSets.add(set1); // add the datasets

        // create a data object with the datasets
        LineData lineData = new LineData(dataSets);

        // set data
        lineChart.setData(lineData);
    }
}

5. アプリ画面

5-1. 開始画面

ちゃんとグラフが表示。左上のSwitchを押すと録音が開始されてスペクトルが描画される。

5-2. 録音時画面

Switchを押すと録音が開始されたがグラフが描画されない。Why?

5-3. グラフを触ってみると

描画された。手を離すとグラフが動かなくなる。また手を触れるとグラフが更新される。Why?

6. 改善方法を知りたい

initChart内のlineChart.setTouchEnabled(True)Falseに変えてみたら、今度は全く表示されなくなった。グラフを触っても変化なし。
改善方法を教えてください

7. 改善した (2019/07/29)

fftスレッド内のsetDataメソッドにグラフを更新するlineChart.invalidate()を追加。
それでもエラーが出た。原因はメインスレッド以外のスレッドでUIを変更しようとしたことによるエラー。
そこでHandlerを使用してメインスレッドにsetDataメソッドの処理を依頼することで解決。

新しいコードはこちら

import com.github.mikephil.charting.charts.LineChart;
import com.github.mikephil.charting.components.XAxis;
import com.github.mikephil.charting.components.YAxis;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.data.LineData;
import com.github.mikephil.charting.data.LineDataSet;
import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;

//MainActivity内
public LineChart lineChart;

//onCreate内
lineChart = findViewById(R.id.line_chart);
initChart();

//onCheckedChanged内(Switchを使っているので)
fft = new Thread(new Runnable() {
    @Override 
    public void run() {

        //フーリエ変換等処理後
        handler.post(new Runnable() {
            @Override 
            public void run() {
                setData(decibelFrequencySpectrum);
            }
        });
    }
});
fft.start();
setData
public void setData(double[] data) {

    ArrayList<Entry> values = new ArrayList<>();

    for (int i = 0; i < data.length; i++) {
        values.add(new Entry(i, (int)data[i], null, null));
    }

    LineDataSet set1;

    if (lineChart.getData() != null && lineChart.getData().getDataSetCount() > 0) {
        set1 = (LineDataSet) lineChart.getData().getDataSetByIndex(0);
        set1.setValues(values);
        lineChart.getData().notifyDataChanged();
        lineChart.notifyDataSetChanged();
        lineChart.invalidate();
    } else {
        // create a dataset and give it a type
        set1 = new LineDataSet(values, "Spectrum");

        set1.setDrawIcons(false);
        set1.setColor(Color.rgb(0, 0, 240));
        set1. setDrawCircles(false);
        set1.setLineWidth(0.5f);
        set1.setValueTextSize(0f);
        set1.setDrawFilled(false);
        set1.setFormLineWidth(1f);
        set1.setFormLineDashEffect(new DashPathEffect(new float[]{10f, 5f}, 0f));
        set1.setFormSize(15.f);
        set1.setDrawValues(true);

        ArrayList<ILineDataSet> dataSets = new ArrayList<>();
        dataSets.add(set1); // add the datasets

        // create a data object with the datasets
        LineData lineData = new LineData(dataSets);

        // set data
        lineChart.setData(lineData);
    }
}
tcb78
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away