回答編集履歴

3

private 化し忘れ

2021/12/14 10:38

投稿

jimbe
jimbe

スコア12659

test CHANGED
@@ -38,7 +38,7 @@
38
38
 
39
39
  public class MainActivity extends AppCompatActivity {
40
40
 
41
- MainViewModel vm;
41
+ private MainViewModel vm;
42
42
 
43
43
 
44
44
 

2

リストコピーに修正

2021/12/14 10:38

投稿

jimbe
jimbe

スコア12659

test CHANGED
@@ -328,9 +328,7 @@
328
328
 
329
329
 
330
330
 
331
- private List<RawData> rawDataList = new ArrayList<>();
332
-
333
- private MutableLiveData<List<RawData>> rawDataLiveData = new MutableLiveData<>(Collections.unmodifiableList(rawDataList));
331
+ private MutableLiveData<List<RawData>> rawDataLiveData = new MutableLiveData<>(Collections.emptyList());
334
332
 
335
333
  LiveData<List<RawData>> getValue() {
336
334
 
@@ -342,11 +340,13 @@
342
340
 
343
341
  void setValue(long time, float value) {
344
342
 
343
+ List<RawData> newList = new ArrayList(rawDataLiveData.getValue());
344
+
345
- rawDataList.add(new RawData(time, value));
345
+ newList.add(new RawData(time, value));
346
-
346
+
347
- while(rawDataList.size() > DATA_COUNT_MAX) rawDataList.remove(0);
347
+ while(newList.size() > DATA_COUNT_MAX) newList.remove(0);
348
-
348
+
349
- rawDataLiveData.setValue(rawDataLiveData.getValue());
349
+ rawDataLiveData.setValue(Collections.unmodifiableList(newList));
350
350
 
351
351
  }
352
352
 

1

nullポインタの可能性とマルチスレッドで危ないかもしれない点を少し修正(本当はcopyか...)

2021/12/13 18:53

投稿

jimbe
jimbe

スコア12659

test CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  MPAndroidChart の仕様を確認していませんので、 setEntries のデータの更新部分が(MPAndroidChart を分かっている人からすると)ダメかもしれません。
4
4
 
5
+ ViewPager を ViewPager2 にしています。
6
+
5
7
 
6
8
 
7
9
  MainActivity.java
@@ -12,65 +14,447 @@
12
14
 
13
15
 
14
16
 
17
+ import android.os.*;
18
+
19
+ import android.util.Log;
20
+
21
+
22
+
23
+ import com.google.android.material.tabs.*;
24
+
25
+
26
+
27
+ import androidx.appcompat.app.AppCompatActivity;
28
+
29
+ import androidx.lifecycle.ViewModelProvider;
30
+
31
+ import androidx.viewpager2.widget.ViewPager2;
32
+
33
+
34
+
35
+ import java.util.Random;
36
+
37
+
38
+
39
+ public class MainActivity extends AppCompatActivity {
40
+
41
+ MainViewModel vm;
42
+
43
+
44
+
45
+ @Override
46
+
47
+ protected void onCreate(Bundle savedInstanceState) {
48
+
49
+ super.onCreate(savedInstanceState);
50
+
51
+ setContentView(R.layout.activity_main);
52
+
53
+
54
+
55
+ vm = new ViewModelProvider(this).get(MainViewModel.class);
56
+
57
+
58
+
59
+ ViewPager2 viewPager2 = findViewById(R.id.view_pager2);
60
+
61
+ viewPager2.setAdapter(new MainPagerAdapter(this));
62
+
63
+
64
+
65
+ TabLayout tabLayout = findViewById(R.id.tab_layout);
66
+
67
+ new TabLayoutMediator(tabLayout, viewPager2,
68
+
69
+ (tab,position) -> tab.setText(MainPagerAdapter.TAB_TITLES[position]) //タブタイトル
70
+
71
+ ).attach();
72
+
73
+
74
+
75
+ feedMultiple();
76
+
77
+ }
78
+
79
+
80
+
81
+ @Override
82
+
83
+ protected void onDestroy() {
84
+
85
+ super.onDestroy();
86
+
87
+ if (thread != null) thread.interrupt();
88
+
89
+ }
90
+
91
+
92
+
93
+ private Thread thread;
94
+
95
+
96
+
97
+ void feedMultiple() {
98
+
99
+ if (thread != null) thread.interrupt();
100
+
101
+ Handler handler = new Handler(getMainLooper());
102
+
103
+ thread = new Thread(new Runnable() {
104
+
105
+ Random random = new Random();
106
+
107
+ @Override
108
+
109
+ public void run() {
110
+
111
+ Log.d("","start.");
112
+
113
+ for (int i=0; i<MainViewModel.DATA_COUNT_MAX*2; i++) {
114
+
115
+ long time = System.currentTimeMillis();
116
+
117
+ float value = (float)(Math.random()*40);
118
+
119
+ handler.post(() -> vm.setValue(time, value));
120
+
121
+ try {
122
+
123
+ Thread.sleep(1000);
124
+
125
+ } catch (InterruptedException e) {
126
+
127
+ break;
128
+
129
+ }
130
+
131
+ }
132
+
133
+ Log.d("","end.");
134
+
135
+ }
136
+
137
+ });
138
+
139
+ thread.start();
140
+
141
+ }
142
+
143
+ }
144
+
145
+ ```
146
+
147
+ res/layout/activity_main.xml
148
+
149
+ ```xml
150
+
151
+ <?xml version="1.0" encoding="utf-8"?>
152
+
153
+ <androidx.coordinatorlayout.widget.CoordinatorLayout
154
+
155
+ xmlns:android="http://schemas.android.com/apk/res/android"
156
+
157
+ xmlns:app="http://schemas.android.com/apk/res-auto"
158
+
159
+ xmlns:tools="http://schemas.android.com/tools"
160
+
161
+ android:layout_width="match_parent"
162
+
163
+ android:layout_height="match_parent"
164
+
165
+ tools:context=".MainActivity">
166
+
167
+
168
+
169
+ <com.google.android.material.appbar.AppBarLayout
170
+
171
+ android:layout_width="match_parent"
172
+
173
+ android:layout_height="wrap_content"
174
+
175
+ android:theme="@style/Theme.Q371702.AppBarOverlay">
176
+
177
+
178
+
179
+ <TextView
180
+
181
+ android:id="@+id/title"
182
+
183
+ android:layout_width="wrap_content"
184
+
185
+ android:layout_height="wrap_content"
186
+
187
+ android:gravity="center"
188
+
189
+ android:minHeight="?actionBarSize"
190
+
191
+ android:padding="@dimen/appbar_padding"
192
+
193
+ android:text="@string/app_name"
194
+
195
+ android:textAppearance="@style/TextAppearance.Widget.AppCompat.Toolbar.Title" />
196
+
197
+
198
+
199
+ <com.google.android.material.tabs.TabLayout
200
+
201
+ android:id="@+id/tab_layout"
202
+
203
+ android:layout_width="match_parent"
204
+
205
+ android:layout_height="wrap_content" />
206
+
207
+
208
+
209
+ </com.google.android.material.appbar.AppBarLayout>
210
+
211
+
212
+
213
+ <androidx.viewpager2.widget.ViewPager2
214
+
215
+ android:id="@+id/view_pager2"
216
+
217
+ android:layout_width="match_parent"
218
+
219
+ android:layout_height="match_parent"
220
+
221
+ app:layout_behavior="@string/appbar_scrolling_view_behavior" />
222
+
223
+
224
+
225
+ </androidx.coordinatorlayout.widget.CoordinatorLayout>
226
+
227
+ ```
228
+
229
+ MainPagerAdapter.java
230
+
231
+ ```java
232
+
233
+ package com.teratail.q371702;
234
+
235
+
236
+
237
+ import androidx.annotation.*;
238
+
239
+ import androidx.fragment.app.*;
240
+
241
+ import androidx.viewpager2.adapter.FragmentStateAdapter;
242
+
243
+
244
+
245
+ public class MainPagerAdapter extends FragmentStateAdapter {
246
+
247
+ static final String TAB_TITLES[] = new String[]{"TAB A","TAB B","Graph"};
248
+
249
+
250
+
251
+ public MainPagerAdapter(FragmentActivity fa) {
252
+
253
+ super(fa);
254
+
255
+ }
256
+
257
+
258
+
259
+ @NonNull
260
+
261
+ @Override
262
+
263
+ public Fragment createFragment(int position) {
264
+
265
+ switch(position) {
266
+
267
+ default: return new DummyFragment();
268
+
269
+ case 0: return new ControlFragment();
270
+
271
+ case 2: return new GraphFragment();
272
+
273
+ }
274
+
275
+ }
276
+
277
+
278
+
279
+ @Override
280
+
281
+ public int getItemCount() {
282
+
283
+ return TAB_TITLES.length;
284
+
285
+ }
286
+
287
+ }
288
+
289
+ ```
290
+
291
+ MainViewModel.java
292
+
293
+ ```java
294
+
295
+ package com.teratail.q371702;
296
+
297
+
298
+
299
+ import androidx.lifecycle.*;
300
+
301
+
302
+
303
+ import java.util.*;
304
+
305
+
306
+
307
+ class RawData {
308
+
309
+ final long time;
310
+
311
+ final float value;
312
+
313
+ RawData(long time, float value) {
314
+
315
+ this.time = time;
316
+
317
+ this.value = value;
318
+
319
+ }
320
+
321
+ }
322
+
323
+
324
+
325
+ public class MainViewModel extends ViewModel {
326
+
327
+ static final int DATA_COUNT_MAX = 120;
328
+
329
+
330
+
331
+ private List<RawData> rawDataList = new ArrayList<>();
332
+
333
+ private MutableLiveData<List<RawData>> rawDataLiveData = new MutableLiveData<>(Collections.unmodifiableList(rawDataList));
334
+
335
+ LiveData<List<RawData>> getValue() {
336
+
337
+ return rawDataLiveData;
338
+
339
+ }
340
+
341
+
342
+
343
+ void setValue(long time, float value) {
344
+
345
+ rawDataList.add(new RawData(time, value));
346
+
347
+ while(rawDataList.size() > DATA_COUNT_MAX) rawDataList.remove(0);
348
+
349
+ rawDataLiveData.setValue(rawDataLiveData.getValue());
350
+
351
+ }
352
+
353
+ }
354
+
355
+ ```
356
+
357
+ GraphFragment.java
358
+
359
+ ```java
360
+
361
+ package com.teratail.q371702;
362
+
363
+
364
+
365
+ import android.graphics.Color;
366
+
15
367
  import android.os.Bundle;
16
368
 
369
+ import android.view.LayoutInflater;
370
+
17
- import android.util.Log;
371
+ import android.view.View;
372
+
18
-
373
+ import android.view.ViewGroup;
374
+
375
+
376
+
19
-
377
+ import androidx.annotation.NonNull;
20
-
378
+
21
- import com.google.android.material.tabs.*;
379
+ import androidx.annotation.Nullable;
22
-
23
-
24
-
380
+
25
- import androidx.appcompat.app.AppCompatActivity;
381
+ import androidx.fragment.app.Fragment;
26
382
 
27
383
  import androidx.lifecycle.ViewModelProvider;
28
384
 
385
+
386
+
387
+ import com.github.mikephil.charting.charts.LineChart;
388
+
389
+ import com.github.mikephil.charting.components.XAxis;
390
+
391
+ import com.github.mikephil.charting.components.YAxis;
392
+
393
+ import com.github.mikephil.charting.components.YAxis.AxisDependency;
394
+
395
+ import com.github.mikephil.charting.data.Entry;
396
+
397
+ import com.github.mikephil.charting.data.LineData;
398
+
29
- import androidx.viewpager2.widget.ViewPager2;
399
+ import com.github.mikephil.charting.data.LineDataSet;
400
+
30
-
401
+ import com.github.mikephil.charting.formatter.ValueFormatter;
402
+
31
-
403
+ import com.github.mikephil.charting.utils.ColorTemplate;
404
+
405
+
406
+
32
-
407
+ import java.text.SimpleDateFormat;
408
+
33
- import java.util.Random;
409
+ import java.util.*;
34
-
35
-
36
-
410
+
411
+
412
+
37
- public class MainActivity extends AppCompatActivity {
413
+ public class GraphFragment extends Fragment {
38
-
414
+
39
- MainViewModel vm;
415
+ private MainViewModel mainViewModel;
416
+
417
+ private LineChart chart;
418
+
419
+
420
+
421
+ private static class DateXAxisFormatter extends ValueFormatter {
422
+
423
+ private SimpleDateFormat sdf = new SimpleDateFormat("MM:dd:ss");
424
+
425
+ private List<Long> xAxis = new ArrayList<>();
426
+
427
+
428
+
429
+ void clear() { xAxis.clear(); }
430
+
431
+ void add(long time) { xAxis.add(time); }
432
+
433
+
434
+
435
+ @Override
436
+
437
+ public String getFormattedValue(float value){
438
+
439
+ int i = (int)value;
440
+
441
+ if(i < 0 || xAxis.size() <= i) return "-"; //ガード
442
+
443
+ return sdf.format(new Date(xAxis.get(i)));
444
+
445
+ }
446
+
447
+ }
448
+
449
+ private DateXAxisFormatter dateXAxisFormatter = new DateXAxisFormatter();
40
450
 
41
451
 
42
452
 
43
453
  @Override
44
454
 
45
- protected void onCreate(Bundle savedInstanceState) {
455
+ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
46
-
47
- super.onCreate(savedInstanceState);
456
+
48
-
49
- setContentView(R.layout.activity_main);
457
+ return inflater.inflate(R.layout.fragment_graph, container, false);
50
-
51
-
52
-
53
- vm = new ViewModelProvider(this).get(MainViewModel.class);
54
-
55
-
56
-
57
- ViewPager2 viewPager2 = findViewById(R.id.view_pager2);
58
-
59
- viewPager2.setAdapter(new MainPagerAdapter(this));
60
-
61
-
62
-
63
- TabLayout tabLayout = findViewById(R.id.tab_layout);
64
-
65
- new TabLayoutMediator(tabLayout, viewPager2,
66
-
67
- (tab,position) -> tab.setText(MainPagerAdapter.TAB_TITLES[position]) //タブタイトル
68
-
69
- ).attach();
70
-
71
-
72
-
73
- feedMultiple();
74
458
 
75
459
  }
76
460
 
@@ -78,59 +462,155 @@
78
462
 
79
463
  @Override
80
464
 
81
- protected void onDestroy() {
82
-
83
- super.onDestroy();
84
-
85
- if (thread != null) thread.interrupt();
86
-
87
- }
88
-
89
-
90
-
91
- private Thread thread;
92
-
93
-
94
-
95
- void feedMultiple() {
96
-
97
- if (thread != null) thread.interrupt();
98
-
99
-
100
-
101
- thread = new Thread(new Runnable() {
102
-
103
- Random random = new Random();
104
-
105
- @Override
106
-
107
- public void run() {
108
-
109
- Log.d("","start.");
110
-
111
- for (int i=0; i<120*2; i++) {
112
-
113
- vm.postValue(System.currentTimeMillis(), (float)(Math.random()*40));
114
-
115
- try {
116
-
117
- Thread.sleep(1000);
118
-
119
- } catch (InterruptedException e) {
120
-
121
- break;
122
-
123
- }
124
-
125
- }
126
-
127
- Log.d("","end.");
465
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
466
+
467
+ super.onViewCreated(view, savedInstanceState);
468
+
469
+
470
+
471
+ mainViewModel = new ViewModelProvider(requireActivity()).get(MainViewModel.class);
472
+
473
+ mainViewModel.getValue().observe(getViewLifecycleOwner(), rawDataList -> setEntries(rawDataList));
474
+
475
+
476
+
477
+ chart = view.findViewById(R.id.line_chart);
478
+
479
+ initChart();
480
+
481
+ }
482
+
483
+
484
+
485
+ public void initChart(){
486
+
487
+
488
+
489
+ chart.getDescription().setEnabled(true);
490
+
491
+ chart.setTouchEnabled(true);
492
+
493
+ chart.setDragEnabled(true);
494
+
495
+ chart.setScaleEnabled(true);
496
+
497
+ chart.setDrawGridBackground(false);
498
+
499
+ chart.setPinchZoom(true);
500
+
501
+ chart.setBackgroundColor(Color.LTGRAY);
502
+
503
+
504
+
505
+ LineData data = new LineData();
506
+
507
+ data.setValueTextColor(Color.WHITE);
508
+
509
+ chart.setData(data);
510
+
511
+
512
+
513
+ XAxis xl = chart.getXAxis();
514
+
515
+ xl.setTextColor(Color.WHITE);
516
+
517
+ xl.setDrawGridLines(false);
518
+
519
+ xl.setAvoidFirstLastClipping(true);
520
+
521
+ xl.setEnabled(true);
522
+
523
+ xl.setValueFormatter(dateXAxisFormatter); //X軸のラベルを時刻にする
524
+
525
+
526
+
527
+ YAxis leftAxis = chart.getAxisLeft();
528
+
529
+ leftAxis.setTextColor(Color.WHITE);
530
+
531
+ leftAxis.setAxisMaximum(100f);
532
+
533
+ leftAxis.setAxisMinimum(0f);
534
+
535
+ leftAxis.setDrawGridLines(true);
536
+
537
+
538
+
539
+ YAxis rightAxis = chart.getAxisRight();
540
+
541
+ rightAxis.setEnabled(false);
542
+
543
+ }
544
+
545
+
546
+
547
+ public void setEntries(List<RawData> rawDataList) {
548
+
549
+ LineData data = chart.getData();
550
+
551
+ if(data != null) {
552
+
553
+
554
+
555
+ data.clearValues();
556
+
557
+ data.addDataSet(createSet());
558
+
559
+ dateXAxisFormatter.clear();
560
+
561
+ for(int i=0; i<rawDataList.size(); i++) {
562
+
563
+ RawData rawData = rawDataList.get(i);
564
+
565
+ dateXAxisFormatter.add(rawData.time);
566
+
567
+ data.addEntry(new Entry(i, rawData.value), 0);
128
568
 
129
569
  }
130
570
 
571
+ data.notifyDataChanged();
572
+
573
+
574
+
575
+ chart.notifyDataSetChanged();
576
+
577
+ chart.setVisibleXRangeMaximum(MainViewModel.DATA_COUNT_MAX);
578
+
579
+ chart.moveViewToX(data.getEntryCount());
580
+
131
- });
581
+ }
582
+
132
-
583
+ }
584
+
585
+
586
+
587
+ private LineDataSet createSet() {
588
+
589
+ LineDataSet set = new LineDataSet(null, "Dynamic Data");
590
+
591
+ set.setAxisDependency(AxisDependency.LEFT);
592
+
593
+ set.setColor(ColorTemplate.getHoloBlue());
594
+
595
+ set.setCircleColor(Color.WHITE);
596
+
133
- thread.start();
597
+ set.setLineWidth(2f);
598
+
599
+ set.setCircleRadius(4f);
600
+
601
+ set.setFillAlpha(65);
602
+
603
+ set.setFillColor(ColorTemplate.getHoloBlue());
604
+
605
+ set.setHighLightColor(Color.rgb(244, 117, 117));
606
+
607
+ set.setValueTextColor(Color.WHITE);
608
+
609
+ set.setValueTextSize(9f);
610
+
611
+ set.setDrawValues(false);
612
+
613
+ return set;
134
614
 
135
615
  }
136
616
 
@@ -138,498 +618,24 @@
138
618
 
139
619
  ```
140
620
 
141
- res/layout/activity_main.xml
621
+ res/layout/fragment_graph.xml
142
622
 
143
623
  ```xml
144
624
 
145
625
  <?xml version="1.0" encoding="utf-8"?>
146
626
 
147
- <androidx.coordinatorlayout.widget.CoordinatorLayout
627
+ <com.github.mikephil.charting.charts.LineChart
148
628
 
149
629
  xmlns:android="http://schemas.android.com/apk/res/android"
150
630
 
151
- xmlns:app="http://schemas.android.com/apk/res-auto"
152
-
153
- xmlns:tools="http://schemas.android.com/tools"
631
+ android:id="@+id/line_chart"
154
632
 
155
633
  android:layout_width="match_parent"
156
634
 
157
- android:layout_height="match_parent"
158
-
159
- tools:context=".MainActivity">
160
-
161
-
162
-
163
- <com.google.android.material.appbar.AppBarLayout
164
-
165
- android:layout_width="match_parent"
166
-
167
- android:layout_height="wrap_content"
168
-
169
- android:theme="@style/Theme.Q371702.AppBarOverlay">
170
-
171
-
172
-
173
- <TextView
174
-
175
- android:id="@+id/title"
176
-
177
- android:layout_width="wrap_content"
178
-
179
- android:layout_height="wrap_content"
180
-
181
- android:gravity="center"
182
-
183
- android:minHeight="?actionBarSize"
184
-
185
- android:padding="@dimen/appbar_padding"
186
-
187
- android:text="@string/app_name"
188
-
189
- android:textAppearance="@style/TextAppearance.Widget.AppCompat.Toolbar.Title" />
190
-
191
-
192
-
193
- <com.google.android.material.tabs.TabLayout
194
-
195
- android:id="@+id/tab_layout"
196
-
197
- android:layout_width="match_parent"
198
-
199
- android:layout_height="wrap_content" />
635
+ android:layout_height="match_parent" />
200
-
201
-
202
-
203
- </com.google.android.material.appbar.AppBarLayout>
204
-
205
-
206
-
207
- <androidx.viewpager2.widget.ViewPager2
208
-
209
- android:id="@+id/view_pager2"
210
-
211
- android:layout_width="match_parent"
212
-
213
- android:layout_height="match_parent"
214
-
215
- app:layout_behavior="@string/appbar_scrolling_view_behavior" />
216
-
217
-
218
-
219
- </androidx.coordinatorlayout.widget.CoordinatorLayout>
220
636
 
221
637
  ```
222
638
 
223
- MainPagerAdapter.java
224
-
225
- ```java
226
-
227
- package com.teratail.q371702;
228
-
229
-
230
-
231
- import androidx.annotation.*;
232
-
233
- import androidx.fragment.app.*;
234
-
235
- import androidx.viewpager2.adapter.FragmentStateAdapter;
236
-
237
-
238
-
239
- public class MainPagerAdapter extends FragmentStateAdapter {
240
-
241
- static final String TAB_TITLES[] = new String[]{"TAB A","TAB B","Graph"};
242
-
243
-
244
-
245
- public MainPagerAdapter(FragmentActivity fa) {
246
-
247
- super(fa);
248
-
249
- }
250
-
251
-
252
-
253
- @NonNull
254
-
255
- @Override
256
-
257
- public Fragment createFragment(int position) {
258
-
259
- switch(position) {
260
-
261
- default: return new DummyFragment();
262
-
263
- case 0: return new ControlFragment();
264
-
265
- case 2: return new GraphFragment();
266
-
267
- }
268
-
269
- }
270
-
271
-
272
-
273
- @Override
274
-
275
- public int getItemCount() {
276
-
277
- return TAB_TITLES.length;
278
-
279
- }
280
-
281
- }
282
-
283
- ```
284
-
285
- MainViewModel.java
286
-
287
- ```java
288
-
289
- package com.teratail.q371702;
290
-
291
-
292
-
293
- import androidx.lifecycle.*;
294
-
295
-
296
-
297
- import java.util.*;
298
-
299
-
300
-
301
- class RawData {
302
-
303
- final long time;
304
-
305
- final float value;
306
-
307
- RawData(long time, float value) {
308
-
309
- this.time = time;
310
-
311
- this.value = value;
312
-
313
- }
314
-
315
- }
316
-
317
-
318
-
319
- public class MainViewModel extends ViewModel {
320
-
321
- static final int DATA_COUNT_MAX = 120;
322
-
323
-
324
-
325
- private List<RawData> rawDataList = new ArrayList<>();
326
-
327
- private MutableLiveData<List<RawData>> rawDataLiveData = new MutableLiveData<>();
328
-
329
- LiveData<List<RawData>> getValue() {
330
-
331
- return rawDataLiveData;
332
-
333
- }
334
-
335
-
336
-
337
- void postValue(long time, float value) {
338
-
339
- rawDataList.add(new RawData(time, value));
340
-
341
- while(rawDataList.size() > DATA_COUNT_MAX) rawDataList.remove(0);
342
-
343
- rawDataLiveData.postValue(Collections.unmodifiableList(rawDataList)); //UIスレッドで実行させる
344
-
345
- }
346
-
347
- }
348
-
349
- ```
350
-
351
- GraphFragment.java
352
-
353
- ```java
354
-
355
- package com.teratail.q371702;
356
-
357
-
358
-
359
- import android.graphics.Color;
360
-
361
- import android.os.Bundle;
362
-
363
- import android.view.LayoutInflater;
364
-
365
- import android.view.View;
366
-
367
- import android.view.ViewGroup;
368
-
369
-
370
-
371
- import androidx.annotation.NonNull;
372
-
373
- import androidx.annotation.Nullable;
374
-
375
- import androidx.fragment.app.Fragment;
376
-
377
- import androidx.lifecycle.ViewModelProvider;
378
-
379
-
380
-
381
- import com.github.mikephil.charting.charts.LineChart;
382
-
383
- import com.github.mikephil.charting.components.XAxis;
384
-
385
- import com.github.mikephil.charting.components.YAxis;
386
-
387
- import com.github.mikephil.charting.components.YAxis.AxisDependency;
388
-
389
- import com.github.mikephil.charting.data.Entry;
390
-
391
- import com.github.mikephil.charting.data.LineData;
392
-
393
- import com.github.mikephil.charting.data.LineDataSet;
394
-
395
- import com.github.mikephil.charting.formatter.ValueFormatter;
396
-
397
- import com.github.mikephil.charting.utils.ColorTemplate;
398
-
399
-
400
-
401
- import java.text.SimpleDateFormat;
402
-
403
- import java.util.*;
404
-
405
-
406
-
407
- public class GraphFragment extends Fragment {
408
-
409
- private MainViewModel mainViewModel;
410
-
411
- private LineChart chart;
412
-
413
-
414
-
415
- private static class DateXAxisFormatter extends ValueFormatter {
416
-
417
- private SimpleDateFormat sdf = new SimpleDateFormat("MM:dd:ss");
418
-
419
- private List<Long> xAxis = new ArrayList<>();
420
-
421
-
422
-
423
- void clear() { xAxis.clear(); }
424
-
425
- void add(long time) { xAxis.add(time); }
426
-
427
-
428
-
429
- @Override
430
-
431
- public String getFormattedValue(float value){
432
-
433
- int i = (int)value;
434
-
435
- if(i < 0 || xAxis.size() <= i) return "-"; //ガード
436
-
437
- return sdf.format(new Date(xAxis.get(i)));
438
-
439
- }
440
-
441
- }
442
-
443
- private DateXAxisFormatter dateXAxisFormatter = new DateXAxisFormatter();
444
-
445
-
446
-
447
- @Override
448
-
449
- public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
450
-
451
- return inflater.inflate(R.layout.fragment_graph, container, false);
452
-
453
- }
454
-
455
-
456
-
457
- @Override
458
-
459
- public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
460
-
461
- super.onViewCreated(view, savedInstanceState);
462
-
463
-
464
-
465
- mainViewModel = new ViewModelProvider(requireActivity()).get(MainViewModel.class);
466
-
467
- mainViewModel.getValue().observe(getViewLifecycleOwner(), rawDataList -> setEntries(rawDataList));
468
-
469
-
470
-
471
- chart = view.findViewById(R.id.line_chart);
472
-
473
- initChart();
474
-
475
- }
476
-
477
-
478
-
479
- public void initChart(){
480
-
481
-
482
-
483
- chart.getDescription().setEnabled(true);
484
-
485
- chart.setTouchEnabled(true);
486
-
487
- chart.setDragEnabled(true);
488
-
489
- chart.setScaleEnabled(true);
490
-
491
- chart.setDrawGridBackground(false);
492
-
493
- chart.setPinchZoom(true);
494
-
495
- chart.setBackgroundColor(Color.LTGRAY);
496
-
497
-
498
-
499
- LineData data = new LineData();
500
-
501
- data.setValueTextColor(Color.WHITE);
502
-
503
- chart.setData(data);
504
-
505
-
506
-
507
- XAxis xl = chart.getXAxis();
508
-
509
- xl.setTextColor(Color.WHITE);
510
-
511
- xl.setDrawGridLines(false);
512
-
513
- xl.setAvoidFirstLastClipping(true);
514
-
515
- xl.setEnabled(true);
516
-
517
- xl.setValueFormatter(dateXAxisFormatter); //X軸のラベルを時刻にする
518
-
519
-
520
-
521
- YAxis leftAxis = chart.getAxisLeft();
522
-
523
- leftAxis.setTextColor(Color.WHITE);
524
-
525
- leftAxis.setAxisMaximum(100f);
526
-
527
- leftAxis.setAxisMinimum(0f);
528
-
529
- leftAxis.setDrawGridLines(true);
530
-
531
-
532
-
533
- YAxis rightAxis = chart.getAxisRight();
534
-
535
- rightAxis.setEnabled(false);
536
-
537
- }
538
-
539
-
540
-
541
- public void setEntries(List<RawData> rawDataList) {
542
-
543
- LineData data = chart.getData();
544
-
545
- if(data != null) {
546
-
547
-
548
-
549
- data.clearValues();
550
-
551
- data.addDataSet(createSet());
552
-
553
- dateXAxisFormatter.clear();
554
-
555
- for(int i=0; i<rawDataList.size(); i++) {
556
-
557
- RawData rawData = rawDataList.get(i);
558
-
559
- dateXAxisFormatter.add(rawData.time);
560
-
561
- data.addEntry(new Entry(i, rawData.value), 0);
562
-
563
- }
564
-
565
- data.notifyDataChanged();
566
-
567
-
568
-
569
- chart.notifyDataSetChanged();
570
-
571
- chart.setVisibleXRangeMaximum(MainViewModel.DATA_COUNT_MAX);
572
-
573
- chart.moveViewToX(data.getEntryCount());
574
-
575
- }
576
-
577
- }
578
-
579
-
580
-
581
- private LineDataSet createSet() {
582
-
583
- LineDataSet set = new LineDataSet(null, "Dynamic Data");
584
-
585
- set.setAxisDependency(AxisDependency.LEFT);
586
-
587
- set.setColor(ColorTemplate.getHoloBlue());
588
-
589
- set.setCircleColor(Color.WHITE);
590
-
591
- set.setLineWidth(2f);
592
-
593
- set.setCircleRadius(4f);
594
-
595
- set.setFillAlpha(65);
596
-
597
- set.setFillColor(ColorTemplate.getHoloBlue());
598
-
599
- set.setHighLightColor(Color.rgb(244, 117, 117));
600
-
601
- set.setValueTextColor(Color.WHITE);
602
-
603
- set.setValueTextSize(9f);
604
-
605
- set.setDrawValues(false);
606
-
607
- return set;
608
-
609
- }
610
-
611
- }
612
-
613
- ```
614
-
615
- res/layout/fragment_graph.xml
616
-
617
- ```xml
618
-
619
- <?xml version="1.0" encoding="utf-8"?>
620
-
621
- <com.github.mikephil.charting.charts.LineChart
622
-
623
- xmlns:android="http://schemas.android.com/apk/res/android"
624
-
625
- android:id="@+id/line_chart"
626
-
627
- android:layout_width="match_parent"
628
-
629
- android:layout_height="match_parent" />
630
-
631
- ```
632
-
633
639
 
634
640
 
635
641
  ※ DummyFragment, ControlFragment のコード・レイアウトは省略