diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 9f251a0..3f95c21 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -14,7 +14,7 @@ android { defaultConfig { applicationId = "com.example.androidtechsample" - minSdk = 23 + minSdk = 26 targetSdk = 31 versionCode = 1 versionName = "1.0" @@ -43,6 +43,7 @@ dependencies { implementation(project(":feature:twitter:twitter-core")) implementation(project(":feature:twitter:home")) implementation(project(":feature:material")) + implementation(project(":feature:chart")) implementation(Dep.AndroidX.appCompat) implementation(Dep.AndroidX.core) diff --git a/app/src/main/java/com/example/androidtechsample/ui/PlaygroundFragment.kt b/app/src/main/java/com/example/androidtechsample/ui/PlaygroundFragment.kt index f13ca70..1c5a578 100644 --- a/app/src/main/java/com/example/androidtechsample/ui/PlaygroundFragment.kt +++ b/app/src/main/java/com/example/androidtechsample/ui/PlaygroundFragment.kt @@ -44,33 +44,16 @@ class PlaygroundFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) with(binding) { - buttonPlaygroundToDesignMock.setOnClickListener { - navigator(R.id.to_fragment_biometric) - } - toMotionLayout.setOnClickListener { - navigator(R.id.to_fragment_motion) - } - toGroupieView.setOnClickListener { - navigator(R.id.to_fragment_groupie) - } - toSpanView.setOnClickListener { - navigator(R.id.to_fragment_span) - } - toNotificationView.setOnClickListener { - navigator(R.id.to_fragment_notification) - } - toListView.setOnClickListener { - navigator(R.id.to_fragment_list) - } - toCameraView.setOnClickListener { - cameraPermissionRequestLauncher.launch(REQUIRED_PERMISSIONS) - } - toComposeView.setOnClickListener { - navigator(R.id.to_fragment_compose) - } - toMaterialView.setOnClickListener { - navigator(R.id.to_fragment_material) - } + buttonPlaygroundToDesignMock.setOnClickListener { navigator(R.id.to_fragment_biometric) } + toMotionLayout.setOnClickListener { navigator(R.id.to_fragment_motion) } + toGroupieView.setOnClickListener { navigator(R.id.to_fragment_groupie) } + toSpanView.setOnClickListener { navigator(R.id.to_fragment_span) } + toNotificationView.setOnClickListener { navigator(R.id.to_fragment_notification) } + toListView.setOnClickListener { navigator(R.id.to_fragment_list) } + toCameraView.setOnClickListener { cameraPermissionRequestLauncher.launch(REQUIRED_PERMISSIONS) } + toComposeView.setOnClickListener { navigator(R.id.to_fragment_compose) } + toMaterialView.setOnClickListener { navigator(R.id.to_fragment_material) } + toChartView.setOnClickListener { navigator(R.id.to_fragment_chart) } } } } diff --git a/app/src/main/res/layout/fragment_playground.xml b/app/src/main/res/layout/fragment_playground.xml index 551bc62..7737326 100644 --- a/app/src/main/res/layout/fragment_playground.xml +++ b/app/src/main/res/layout/fragment_playground.xml @@ -110,6 +110,14 @@ android:layout_marginVertical="4dp" android:text="@string/playground_camera_view" /> + + diff --git a/feature/chart/src/main/java/com/example/chart/ChartFragment.kt b/feature/chart/src/main/java/com/example/chart/ChartFragment.kt new file mode 100644 index 0000000..f31142c --- /dev/null +++ b/feature/chart/src/main/java/com/example/chart/ChartFragment.kt @@ -0,0 +1,255 @@ +package com.example.chart + +import android.graphics.Color +import android.graphics.Typeface +import android.icu.text.SimpleDateFormat +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.content.ContextCompat +import androidx.fragment.app.Fragment +import com.example.chart.databinding.FragmentChartBinding +import com.github.mikephil.charting.components.XAxis.XAxisPosition +import com.github.mikephil.charting.data.BarData +import com.github.mikephil.charting.data.BarDataSet +import com.github.mikephil.charting.data.BarEntry +import com.github.mikephil.charting.data.CombinedData +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.formatter.ValueFormatter +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet +import java.time.LocalDate +import java.util.* + +class ChartFragment : Fragment() { + + private lateinit var binding: FragmentChartBinding + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + binding = FragmentChartBinding.inflate(inflater) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + createLineChart() + createCombinedChart() + } + + private fun createLineChart() { + //表示用サンプルデータの作成// + val follow = listOf( + Pair(createMonth(4), 1000), + Pair(createMonth(5), 970), + Pair(createMonth(6), 980), + Pair(createMonth(7), 1020), + Pair(createMonth(8), 1130), + Pair(createMonth(9), 1250), + Pair(createMonth(10), 1300), + Pair(createMonth(11), 1400), + ) + + // Entryにデータ格納 + val entryList = mutableListOf()//1本目の線 + follow.map { + entryList.add( + Entry(it.first.toFloat(), it.second.toFloat()) + ) + } + + // DataSetにデータ格納 + val lineDataSet = LineDataSet(entryList, "square") + // DataSetにフォーマット指定(3章で詳説) + // 線の色 + lineDataSet.color = ContextCompat.getColor(requireContext(), R.color.spindle_accent_primary) + // 線の太さ + lineDataSet.lineWidth = 3f + // 背景色を出すかの true false + lineDataSet.setDrawFilled(true) + // 背景色指定 + lineDataSet.fillDrawable = ContextCompat.getDrawable(requireContext(), R.drawable.grad_line_chart) + // 座標の大きさ + lineDataSet.circleRadius = 5f + // 座標の色 + lineDataSet.circleColors = listOf(ContextCompat.getColor(requireContext(), R.color.white)) + // 座標中心の表示 + lineDataSet.setDrawCircleHole(true) + // 座標中心の大きさ(circleRadiusより小さい値) + lineDataSet.circleHoleRadius = 4f + // 座標中心の色 + lineDataSet.circleHoleColor = + ContextCompat.getColor(requireContext(), R.color.spindle_accent_primary) + // データの値を記す。0fで記載なし。 + lineDataSet.valueTextSize = 0f + lineDataSet.label = "" + // 表示モード + lineDataSet.mode = LineDataSet.Mode.CUBIC_BEZIER + + //リストに格納 + val lineDataSets = mutableListOf() + lineDataSets.add(lineDataSet) + + // LineDataにLineDataSet格納 + val lineData = LineData(lineDataSets) + // LineChartにLineData格納 + binding.lineChart.data = lineData + // Chartのフォーマット指定 + binding.lineChart.apply { + // x軸設定 + xAxis.apply { + isEnabled = true + textColor = Color.BLACK + textSize = 12f + typeface = Typeface.DEFAULT_BOLD + position = XAxisPosition.BOTTOM + valueFormatter = MonthValueFormatter() + labelCount = 4 + + } + // y軸の設定 + axisRight.isEnabled = false + axisLeft.apply { + textSize = 12f + typeface = Typeface.DEFAULT_BOLD + valueFormatter = FollowValueFormatter() + } + // description + description.isEnabled = false + } + // アニメーション + binding.lineChart.animateXY(1000, 1000) + + // lineChart更新 + binding.lineChart.invalidate() + } + + private fun createMonth(month: Int): Int = LocalDate.of(2020, month, 1).monthValue + + private fun createCombinedChart() { + + //表示用サンプルデータの作成// + val weekData = listOf( + 150, + 90, + 200, + 150, + 100, + 250, + 150 + ) + + val averageData = List(9) { 150 } + + //①Entryにデータ格納 + val entryList = mutableListOf() + weekData.mapIndexed { index, it -> + entryList.add(BarEntry((index + 1).toFloat(), it.toFloat())) + } + val averageList = mutableListOf() + averageData.mapIndexed { index, it -> + averageList.add(Entry(index.toFloat(), it.toFloat())) + } + + // ②DataSetにデータ格納 + val barDataSet = BarDataSet(entryList, "square") + val lineDataSet = LineDataSet(averageList, "square") + + /** + * 棒グラフの設定 + */ + // 線の色 + barDataSet.color = ContextCompat.getColor(requireContext(), R.color.spindle_accent_primary) + barDataSet.label = "" + // 値の表示有無 + barDataSet.setDrawValues(false) + + /** + * 線グラフの設定 + */ + lineDataSet.color = ContextCompat.getColor(requireContext(), R.color.spindle_accent_secondary) + lineDataSet.lineWidth = 2f + lineDataSet.setDrawCircles(false) + lineDataSet.label = "" + lineDataSet.setDrawValues(false) + // 点線の指定 + lineDataSet.enableDashedLine(20f, 30f, 0f) + + + //④BarDataにBarDataSet格納 + val barData = BarData(barDataSet) + val averageLineData = LineData(lineDataSet) + + val combinedData = CombinedData() + combinedData.setData(barData) + combinedData.setData(averageLineData) + + // 棒の幅の調整 + barData.barWidth = 0.5f + + //⑤BarChartのフォーマット指定 + binding.combinedChart.apply { + // Zoomの制御 + setScaleEnabled(false) + // 棒グラフの左下のマーカーの制御 + legend.isEnabled = false + // 右下のdescription labelの制御 + description.isEnabled = false + + // x軸設定 + xAxis.apply { + isEnabled = true + textColor = Color.BLACK + position = XAxisPosition.BOTTOM + valueFormatter = object : ValueFormatter() { + + override fun getFormattedValue(value: Float): String { + // value == 0の時折線グラフのindex0に当たるのでreturnする + if (value.toInt() == 0) return "" + if (weekData.size >= value) { + val simpleDateFormat = SimpleDateFormat("M/d", Locale.getDefault()) + val entriesTimestampMills: MutableList = mutableListOf( + 1614524400000, // 2021/03/01 00:00:00 + 1614610800000, // 2021/03/02 00:00:00 + 1614697200000, // 2021/03/03 00:00:00 + 1614783600000, // 2021/03/04 00:00:00 + 1614870000000, // 2021/03/05 00:00:00 + 1614956400000, // 2021/03/06 00:00:00 + 1615042800000 // 2021/03/07 00:00:00 + ) + // value には 0, 1, 2... という index が入ってくるので + // index からタイムスタンプを取得する + val timestampMills = entriesTimestampMills[value.toInt() - 1] + val date = Date(timestampMills) + return simpleDateFormat.format(date) + } else { + return "" + } + } + } + axisMinimum = 0f + + } + // y軸設定 + axisRight.isEnabled = false + axisLeft.apply { + textColor = ContextCompat.getColor(requireContext(), R.color.black) + } + + data = combinedData + // 選択ハイライトの制御 + data.isHighlightEnabled = false + } + + // アニメーション + binding.combinedChart.animateXY(1000, 1000) + + binding.combinedChart.invalidate() + } +} diff --git a/feature/chart/src/main/java/com/example/chart/FollowValueFormatter.kt b/feature/chart/src/main/java/com/example/chart/FollowValueFormatter.kt new file mode 100644 index 0000000..5fe1e65 --- /dev/null +++ b/feature/chart/src/main/java/com/example/chart/FollowValueFormatter.kt @@ -0,0 +1,9 @@ +package com.example.chart + +import com.github.mikephil.charting.formatter.ValueFormatter + +class FollowValueFormatter : ValueFormatter() { + override fun getFormattedValue(value: Float): String { + return (value.toInt() / 1000.0).toString() + "千" + } +} diff --git a/feature/chart/src/main/java/com/example/chart/MonthValueFormatter.kt b/feature/chart/src/main/java/com/example/chart/MonthValueFormatter.kt new file mode 100644 index 0000000..7b607a1 --- /dev/null +++ b/feature/chart/src/main/java/com/example/chart/MonthValueFormatter.kt @@ -0,0 +1,9 @@ +package com.example.chart + +import com.github.mikephil.charting.formatter.ValueFormatter + +class MonthValueFormatter : ValueFormatter() { + override fun getFormattedValue(value: Float): String { + return value.toInt().toString() + "月" + } +} diff --git a/feature/chart/src/main/res/drawable/grad_line_chart.xml b/feature/chart/src/main/res/drawable/grad_line_chart.xml new file mode 100644 index 0000000..9c3706a --- /dev/null +++ b/feature/chart/src/main/res/drawable/grad_line_chart.xml @@ -0,0 +1,7 @@ + + + + diff --git a/feature/chart/src/main/res/layout/fragment_chart.xml b/feature/chart/src/main/res/layout/fragment_chart.xml new file mode 100644 index 0000000..4b3982f --- /dev/null +++ b/feature/chart/src/main/res/layout/fragment_chart.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + diff --git a/feature/core/src/main/res/navigation/nav_main.xml b/feature/core/src/main/res/navigation/nav_main.xml index 0c95b1e..fa30caa 100644 --- a/feature/core/src/main/res/navigation/nav_main.xml +++ b/feature/core/src/main/res/navigation/nav_main.xml @@ -34,6 +34,9 @@ + + + + diff --git a/feature/resources/src/main/res/values/colors.xml b/feature/resources/src/main/res/values/colors.xml index 46c7b49..aebc788 100644 --- a/feature/resources/src/main/res/values/colors.xml +++ b/feature/resources/src/main/res/values/colors.xml @@ -45,4 +45,8 @@ @color/deep_orange_200 + + #298737 + #82BE28 + diff --git a/feature/resources/src/main/res/values/strings.xml b/feature/resources/src/main/res/values/strings.xml index 6591d60..2bb9e6d 100644 --- a/feature/resources/src/main/res/values/strings.xml +++ b/feature/resources/src/main/res/values/strings.xml @@ -14,6 +14,7 @@ SpanView NotificationView CameraView + ChartView HelloCompose カメラの許可をしてください ListView @@ -98,4 +99,9 @@ Notification DM + + Chart + LineChart + BarChart + CombinedChart diff --git a/settings.gradle.kts b/settings.gradle.kts index 1114b19..7628088 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -9,3 +9,4 @@ include(":feature:ameba:followfeed") include(":feature:twitter:home") include(":feature:twitter:twitter-core") include(":feature:material") +include(":feature:chart")