リビジョン b153e4bb
| learn/android/TempRecorderJetpackKt/.idea/compiler.xml | ||
|---|---|---|
|
<?xml version="1.0" encoding="UTF-8"?>
|
||
|
<project version="4">
|
||
|
<component name="CompilerConfiguration">
|
||
|
<bytecodeTargetLevel target="1.8" />
|
||
|
</component>
|
||
|
</project>
|
||
| learn/android/TempRecorderJetpackKt/.idea/misc.xml | ||
|---|---|---|
|
<?xml version="1.0" encoding="UTF-8"?>
|
||
|
<project version="4">
|
||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||
|
</component>
|
||
|
<component name="ProjectType">
|
||
| learn/android/TempRecorderJetpackKt/app/build.gradle | ||
|---|---|---|
|
apply plugin: 'com.android.application'
|
||
|
apply plugin: 'kotlin-android'
|
||
|
apply plugin: 'kotlin-android-extensions'
|
||
|
apply plugin: 'kotlin-kapt'
|
||
|
|
||
|
android {
|
||
|
compileSdkVersion 30
|
||
| ... | ... | |
|
}
|
||
|
|
||
|
buildFeatures {
|
||
|
viewBinding true
|
||
|
dataBinding true
|
||
|
}
|
||
|
compileOptions {
|
||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||
| ... | ... | |
|
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||
|
implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
|
||
|
implementation 'androidx.recyclerview:recyclerview:1.1.0'
|
||
|
implementation 'androidx.room:room-runtime:2.2.5'
|
||
|
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'
|
||
|
kapt "androidx.room:room-compiler:2.2.5"
|
||
|
implementation "androidx.room:room-ktx:2.2.5"
|
||
|
testImplementation 'junit:junit:4.12'
|
||
|
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
|
||
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
|
||
|
annotationProcessor 'androidx.room:room-compiler:2.2.5'
|
||
|
|
||
|
}
|
||
| learn/android/TempRecorderJetpackKt/app/src/main/java/com/torutk/android/temprecorder/jetpackkt/BindingUtils.kt | ||
|---|---|---|
|
package com.torutk.android.temprecorder.jetpackkt
|
||
|
|
||
|
import android.widget.TextView
|
||
|
import androidx.databinding.BindingAdapter
|
||
|
import com.torutk.android.temprecorder.jetpackkt.domain.Temperature
|
||
|
import java.time.format.DateTimeFormatter
|
||
|
|
||
|
|
||
|
@BindingAdapter("measuredAtFormatted")
|
||
|
fun TextView.setMeasuredAtFormatted(item: Temperature) {
|
||
|
val pattern = context.resources.getString(R.string.main_measured_at_format)
|
||
|
val formatter = DateTimeFormatter.ofPattern(pattern)
|
||
|
text = item.measuredAt.format(formatter)
|
||
|
}
|
||
| learn/android/TempRecorderJetpackKt/app/src/main/java/com/torutk/android/temprecorder/jetpackkt/MainActivity.kt | ||
|---|---|---|
|
import android.os.Bundle
|
||
|
import androidx.activity.viewModels
|
||
|
import androidx.appcompat.app.AppCompatActivity
|
||
|
import androidx.databinding.DataBindingUtil
|
||
|
import androidx.lifecycle.Observer
|
||
|
import com.torutk.android.temprecorder.jetpackkt.databinding.ActivityMainBinding
|
||
|
import java.time.LocalDateTime
|
||
| ... | ... | |
|
|
||
|
class MainActivity : AppCompatActivity() {
|
||
|
|
||
|
private lateinit var binding: ActivityMainBinding // ViewBinding
|
||
|
private val model: MainViewModel by viewModels() // ViewModel
|
||
|
private val temperatureViewModel: MainViewModel by viewModels { MainViewModel.Factory(this.application) }
|
||
|
|
||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||
|
super.onCreate(savedInstanceState)
|
||
|
binding = ActivityMainBinding.inflate(layoutInflater)
|
||
|
val binding: ActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
|
||
|
setContentView(binding.root)
|
||
|
|
||
|
// 検温日時の変更監視と表示設定
|
||
|
val measuredAtObserver = Observer<LocalDateTime> {
|
||
|
binding.textviewMainMeasuredat.text = it.format(DATE_TIME_VIEW_FORMATTER)
|
||
|
}
|
||
|
model.measuredAt.observe(this, measuredAtObserver)
|
||
|
temperatureViewModel.measuredAt.observe(this, measuredAtObserver)
|
||
|
// 検温日時の増減操作
|
||
|
binding.buttonMainIncminite.setOnClickListener { model.incrementMeasuredAt(10) }
|
||
|
binding.buttonMainDecminite.setOnClickListener { model.decrementMeasuredAt(10) }
|
||
|
binding.buttonMainIncminite.setOnClickListener { temperatureViewModel.incrementMeasuredAt(10) }
|
||
|
binding.buttonMainDecminite.setOnClickListener { temperatureViewModel.decrementMeasuredAt(10) }
|
||
|
|
||
|
// 体温入力用のNumberPicker設定
|
||
|
with (binding.numberpickerMainIntegral) {
|
||
| ... | ... | |
|
maxValue = 9
|
||
|
value = 5
|
||
|
}
|
||
|
|
||
|
// 体温の登録
|
||
|
binding.buttonMainSubmit.setOnClickListener {
|
||
|
val temperature = binding.numberpickerMainIntegral.value + binding.numberpickerMainFraction.value / 10f
|
||
|
temperatureViewModel.submitTemperature(temperature)
|
||
|
}
|
||
|
|
||
|
val adapter = TemperatureAdapter()
|
||
|
binding.recyclerviewMainRecord.adapter = adapter
|
||
|
|
||
|
temperatureViewModel.temperatureList.observe(this, Observer {
|
||
|
it?.let {
|
||
|
adapter.submitList(it)
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
|
||
|
override fun onResume() {
|
||
|
super.onResume()
|
||
|
model.currentMeasuredAt()
|
||
|
temperatureViewModel.currentMeasuredAt()
|
||
|
}
|
||
|
}
|
||
| learn/android/TempRecorderJetpackKt/app/src/main/java/com/torutk/android/temprecorder/jetpackkt/MainViewModel.kt | ||
|---|---|---|
|
package com.torutk.android.temprecorder.jetpackkt
|
||
|
|
||
|
import androidx.lifecycle.LiveData
|
||
|
import androidx.lifecycle.MutableLiveData
|
||
|
import androidx.lifecycle.ViewModel
|
||
|
import android.app.Application
|
||
|
import androidx.lifecycle.*
|
||
|
import com.torutk.android.temprecorder.jetpackkt.database.TemperatureDatabase
|
||
|
import com.torutk.android.temprecorder.jetpackkt.domain.Temperature
|
||
|
import com.torutk.android.temprecorder.jetpackkt.repository.TemperatureRepository
|
||
|
import kotlinx.coroutines.launch
|
||
|
import java.time.LocalDateTime
|
||
|
|
||
|
class MainViewModel : ViewModel() {
|
||
|
class MainViewModel(application: Application) : AndroidViewModel(application) {
|
||
|
private val temperatureRepository = TemperatureRepository(TemperatureDatabase.getInstance(application))
|
||
|
|
||
|
val temperatureList = temperatureRepository.temperatures
|
||
|
|
||
|
private val _measuredAt: MutableLiveData<LocalDateTime> = MutableLiveData(LocalDateTime.now())
|
||
|
val measuredAt: LiveData<LocalDateTime>
|
||
|
get() = _measuredAt
|
||
|
|
||
|
init {
|
||
|
refreshDataFromRepository()
|
||
|
}
|
||
|
|
||
|
fun incrementMeasuredAt(minutes: Long) {
|
||
|
_measuredAt.value = _measuredAt.value?.plusMinutes(minutes)
|
||
|
}
|
||
| ... | ... | |
|
fun currentMeasuredAt() {
|
||
|
_measuredAt.value = LocalDateTime.now()
|
||
|
}
|
||
|
|
||
|
private fun refreshDataFromRepository() {
|
||
|
viewModelScope.launch {
|
||
|
temperatureRepository.refreshTemperatures()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fun submitTemperature(measurement: Float) {
|
||
|
val temp = Temperature(
|
||
|
measuredAt = measuredAt.value ?: LocalDateTime.now(),
|
||
|
measurement = measurement
|
||
|
)
|
||
|
viewModelScope.launch {
|
||
|
temperatureRepository.insert(temp)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class Factory(val app: Application) : ViewModelProvider.Factory {
|
||
|
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||
|
if (modelClass.isAssignableFrom(MainViewModel::class.java)) {
|
||
|
@Suppress("UNCHECKED_CAST")
|
||
|
return MainViewModel(app) as T
|
||
|
}
|
||
|
throw IllegalArgumentException("Unable to construct viewmodel")
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
| learn/android/TempRecorderJetpackKt/app/src/main/java/com/torutk/android/temprecorder/jetpackkt/TemperatureAdapter.kt | ||
|---|---|---|
|
package com.torutk.android.temprecorder.jetpackkt
|
||
|
|
||
|
import android.view.LayoutInflater
|
||
|
import android.view.ViewGroup
|
||
|
import androidx.recyclerview.widget.DiffUtil
|
||
|
import androidx.recyclerview.widget.ListAdapter
|
||
|
import androidx.recyclerview.widget.RecyclerView
|
||
|
import com.torutk.android.temprecorder.jetpackkt.databinding.ItemTemperatureBinding
|
||
|
import com.torutk.android.temprecorder.jetpackkt.domain.Temperature
|
||
|
|
||
|
class TemperatureAdapter() : ListAdapter<Temperature, TemperatureAdapter.ViewHolder>(TemperatureDiffCallback()) {
|
||
|
|
||
|
class ViewHolder private constructor(val binding: ItemTemperatureBinding) : RecyclerView.ViewHolder(binding.root) {
|
||
|
fun bind(item: Temperature) {
|
||
|
binding.temperature = item
|
||
|
binding.executePendingBindings()
|
||
|
}
|
||
|
|
||
|
companion object {
|
||
|
fun from(parent: ViewGroup): ViewHolder {
|
||
|
val layoutInflater = LayoutInflater.from(parent.context)
|
||
|
val binding = ItemTemperatureBinding.inflate(layoutInflater, parent, false)
|
||
|
return ViewHolder(binding)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||
|
return ViewHolder.from(parent)
|
||
|
}
|
||
|
|
||
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||
|
val item = getItem(position)
|
||
|
holder.bind(item)
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
class TemperatureDiffCallback : DiffUtil.ItemCallback<Temperature>() {
|
||
|
override fun areItemsTheSame(oldItem: Temperature, newItem: Temperature): Boolean {
|
||
|
return oldItem.id == newItem.id
|
||
|
}
|
||
|
|
||
|
override fun areContentsTheSame(oldItem: Temperature, newItem: Temperature): Boolean {
|
||
|
return oldItem == newItem
|
||
|
}
|
||
|
|
||
|
}
|
||
| learn/android/TempRecorderJetpackKt/app/src/main/java/com/torutk/android/temprecorder/jetpackkt/database/LocalDateTimeConverter.kt | ||
|---|---|---|
|
package com.torutk.android.temprecorder.jetpackkt.database
|
||
|
|
||
|
import androidx.room.TypeConverter
|
||
|
import java.time.LocalDateTime
|
||
|
import java.time.format.DateTimeFormatter
|
||
|
|
||
|
class LocalDateTimeConverter {
|
||
|
@TypeConverter
|
||
|
fun fromLocalDateTime(dateTime: LocalDateTime): String {
|
||
|
return dateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
|
||
|
}
|
||
|
@TypeConverter
|
||
|
fun toLocalDateTime(dateTimeText: String): LocalDateTime {
|
||
|
return LocalDateTime.parse(dateTimeText) // Default format is ISO_LOCAL_DATE_TIME
|
||
|
}
|
||
|
}
|
||
| learn/android/TempRecorderJetpackKt/app/src/main/java/com/torutk/android/temprecorder/jetpackkt/database/TemperatureDao.kt | ||
|---|---|---|
|
package com.torutk.android.temprecorder.jetpackkt.database
|
||
|
|
||
|
import androidx.lifecycle.LiveData
|
||
|
import androidx.room.Dao
|
||
|
import androidx.room.Insert
|
||
|
import androidx.room.Query
|
||
|
import androidx.room.Update
|
||
|
|
||
|
@Dao
|
||
|
interface TemperatureDao {
|
||
|
@Insert
|
||
|
suspend fun insert(temperature: TemperatureEntity)
|
||
|
@Update
|
||
|
fun update(temperature: TemperatureEntity)
|
||
|
@Query("SELECT * FROM Temperatures WHERE id = :key")
|
||
|
fun get(key: Long): TemperatureEntity?
|
||
|
@Query("SELECT * FROM Temperatures ORDER BY id DESC")
|
||
|
fun getAllTemperatures(): LiveData<List<TemperatureEntity>>
|
||
|
}
|
||
| learn/android/TempRecorderJetpackKt/app/src/main/java/com/torutk/android/temprecorder/jetpackkt/database/TemperatureDatabase.kt | ||
|---|---|---|
|
package com.torutk.android.temprecorder.jetpackkt.database
|
||
|
|
||
|
import android.content.Context
|
||
|
import androidx.room.*
|
||
|
import com.torutk.android.temprecorder.jetpackkt.domain.Temperature
|
||
|
|
||
|
internal const val DATABASE_FILE_NAME = "temperatures.db"
|
||
|
|
||
|
@Database(entities = [TemperatureEntity::class], version = 1)
|
||
|
@TypeConverters(LocalDateTimeConverter::class)
|
||
|
abstract class TemperatureDatabase : RoomDatabase() {
|
||
|
abstract val temperatureDao: TemperatureDao
|
||
|
|
||
|
companion object {
|
||
|
@Volatile
|
||
|
private var INSTANCE: TemperatureDatabase? = null
|
||
|
|
||
|
fun getInstance(context: Context): TemperatureDatabase {
|
||
|
synchronized(this) {
|
||
|
var instance = INSTANCE
|
||
|
if (instance == null) {
|
||
|
instance = Room.databaseBuilder(
|
||
|
context.applicationContext,
|
||
|
TemperatureDatabase::class.java,
|
||
|
DATABASE_FILE_NAME
|
||
|
).fallbackToDestructiveMigration()
|
||
|
.build()
|
||
|
INSTANCE = instance
|
||
|
}
|
||
|
return instance
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
| learn/android/TempRecorderJetpackKt/app/src/main/java/com/torutk/android/temprecorder/jetpackkt/database/TemperatureEntity.kt | ||
|---|---|---|
|
package com.torutk.android.temprecorder.jetpackkt.database
|
||
|
|
||
|
import androidx.annotation.NonNull
|
||
|
import androidx.room.ColumnInfo
|
||
|
import androidx.room.Entity
|
||
|
import androidx.room.PrimaryKey
|
||
|
import com.torutk.android.temprecorder.jetpackkt.domain.Temperature
|
||
|
import java.time.LocalDateTime
|
||
|
|
||
|
@Entity(tableName = "Temperatures")
|
||
|
data class TemperatureEntity(
|
||
|
@PrimaryKey(autoGenerate = true)
|
||
|
var id: Long = 0L,
|
||
|
|
||
|
@ColumnInfo(name = "measured_at")
|
||
|
val measuredAt: LocalDateTime,
|
||
|
|
||
|
val measurement: Float
|
||
|
)
|
||
|
|
||
|
// Map database entities to domain entities
|
||
|
fun List<TemperatureEntity>.asDomainModel(): List<Temperature> {
|
||
|
return map {
|
||
|
Temperature(
|
||
|
id = it.id,
|
||
|
measuredAt = it.measuredAt,
|
||
|
measurement = it.measurement
|
||
|
)
|
||
|
}
|
||
|
}
|
||
| learn/android/TempRecorderJetpackKt/app/src/main/java/com/torutk/android/temprecorder/jetpackkt/domain/Temperature.kt | ||
|---|---|---|
|
package com.torutk.android.temprecorder.jetpackkt.domain
|
||
|
|
||
|
import java.time.LocalDateTime
|
||
|
|
||
|
data class Temperature(val id: Long = 0, val measuredAt: LocalDateTime, val measurement: Float)
|
||
|
|
||
| learn/android/TempRecorderJetpackKt/app/src/main/java/com/torutk/android/temprecorder/jetpackkt/repository/TemperatureRepository.kt | ||
|---|---|---|
|
package com.torutk.android.temprecorder.jetpackkt.repository
|
||
|
|
||
|
import androidx.lifecycle.LiveData
|
||
|
import androidx.lifecycle.Transformations
|
||
|
import com.torutk.android.temprecorder.jetpackkt.database.TemperatureDatabase
|
||
|
import com.torutk.android.temprecorder.jetpackkt.database.TemperatureEntity
|
||
|
import com.torutk.android.temprecorder.jetpackkt.database.asDomainModel
|
||
|
import com.torutk.android.temprecorder.jetpackkt.domain.Temperature
|
||
|
import kotlinx.coroutines.Dispatchers
|
||
|
import kotlinx.coroutines.withContext
|
||
|
|
||
|
class TemperatureRepository(private val database: TemperatureDatabase) {
|
||
|
val temperatures: LiveData<List<Temperature>> = Transformations.map(database.temperatureDao.getAllTemperatures()) {
|
||
|
it.asDomainModel()
|
||
|
}
|
||
|
|
||
|
suspend fun insert(temperature: Temperature) {
|
||
|
database.temperatureDao.insert(
|
||
|
TemperatureEntity(
|
||
|
measuredAt = temperature.measuredAt,
|
||
|
measurement = temperature.measurement
|
||
|
)
|
||
|
)
|
||
|
}
|
||
|
|
||
|
suspend fun refreshTemperatures() {
|
||
|
withContext(Dispatchers.IO) {
|
||
|
// リモートデータソースから取得したデータを、ローカルデータベースに格納する等
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
| learn/android/TempRecorderJetpackKt/app/src/main/res/layout/activity_main.xml | ||
|---|---|---|
|
<?xml version="1.0" encoding="utf-8"?>
|
||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||
|
xmlns:tools="http://schemas.android.com/tools"
|
||
|
android:layout_width="match_parent"
|
||
|
android:layout_height="match_parent"
|
||
|
tools:context=".MainActivity">
|
||
|
xmlns:tools="http://schemas.android.com/tools">
|
||
|
|
||
|
<TextView
|
||
|
android:id="@+id/textview_main_submittitle"
|
||
|
android:layout_width="0dp"
|
||
|
android:layout_height="wrap_content"
|
||
|
android:layout_marginStart="8dp"
|
||
|
android:text="@string/main_submittitle"
|
||
|
android:textSize="18sp"
|
||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||
|
app:layout_constraintRight_toRightOf="parent"
|
||
|
app:layout_constraintTop_toTopOf="parent" />
|
||
|
<data>
|
||
|
<variable
|
||
|
name="temperatureViewModel"
|
||
|
type="com.torutk.android.temprecorder.jetpackkt.MainViewModel" />
|
||
|
</data>
|
||
|
|
||
|
<TextView
|
||
|
android:id="@+id/textview_main_measuredat"
|
||
|
android:layout_width="0dp"
|
||
|
android:layout_height="wrap_content"
|
||
|
android:layout_marginTop="16dp"
|
||
|
android:fontFamily="monospace"
|
||
|
android:textAlignment="center"
|
||
|
android:textSize="36sp"
|
||
|
app:layout_constraintEnd_toEndOf="parent"
|
||
|
app:layout_constraintStart_toStartOf="parent"
|
||
|
app:layout_constraintTop_toBottomOf="@+id/textview_main_submittitle"
|
||
|
tools:text="09.27 21:22" />
|
||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||
|
android:layout_width="match_parent"
|
||
|
android:layout_height="match_parent"
|
||
|
tools:context=".MainActivity">
|
||
|
|
||
|
<Button
|
||
|
android:id="@+id/button_main_incminite"
|
||
|
android:layout_width="wrap_content"
|
||
|
android:layout_height="wrap_content"
|
||
|
android:layout_marginTop="16dp"
|
||
|
android:text="@string/main_incminite"
|
||
|
app:layout_constraintEnd_toStartOf="@+id/button_main_decminite"
|
||
|
app:layout_constraintHorizontal_bias="0.5"
|
||
|
app:layout_constraintStart_toStartOf="parent"
|
||
|
app:layout_constraintTop_toBottomOf="@+id/textview_main_measuredat" />
|
||
|
<TextView
|
||
|
android:id="@+id/textview_main_submittitle"
|
||
|
android:layout_width="0dp"
|
||
|
android:layout_height="wrap_content"
|
||
|
android:layout_marginStart="8dp"
|
||
|
android:text="@string/main_submittitle"
|
||
|
android:textSize="18sp"
|
||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||
|
app:layout_constraintRight_toRightOf="parent"
|
||
|
app:layout_constraintTop_toTopOf="parent" />
|
||
|
|
||
|
<Button
|
||
|
android:id="@+id/button_main_decminite"
|
||
|
android:layout_width="wrap_content"
|
||
|
android:layout_height="wrap_content"
|
||
|
android:text="@string/main_decminite"
|
||
|
app:layout_constraintBottom_toBottomOf="@+id/button_main_incminite"
|
||
|
app:layout_constraintEnd_toEndOf="parent"
|
||
|
app:layout_constraintHorizontal_bias="0.5"
|
||
|
app:layout_constraintStart_toEndOf="@+id/button_main_incminite"
|
||
|
app:layout_constraintTop_toTopOf="@+id/button_main_incminite" />
|
||
|
<TextView
|
||
|
android:id="@+id/textview_main_measuredat"
|
||
|
android:layout_width="0dp"
|
||
|
android:layout_height="wrap_content"
|
||
|
android:layout_marginTop="16dp"
|
||
|
android:fontFamily="monospace"
|
||
|
android:textAlignment="center"
|
||
|
android:textSize="36sp"
|
||
|
app:layout_constraintEnd_toEndOf="parent"
|
||
|
app:layout_constraintStart_toStartOf="parent"
|
||
|
app:layout_constraintTop_toBottomOf="@+id/textview_main_submittitle"
|
||
|
tools:text="09.27 21:22" />
|
||
|
|
||
|
<NumberPicker
|
||
|
android:id="@+id/numberpicker_main_integral"
|
||
|
android:layout_width="wrap_content"
|
||
|
android:layout_height="wrap_content"
|
||
|
android:layout_marginTop="16dp"
|
||
|
app:layout_constraintEnd_toStartOf="@+id/numberpicker_main_fraction"
|
||
|
app:layout_constraintHorizontal_bias="0.5"
|
||
|
app:layout_constraintStart_toStartOf="parent"
|
||
|
app:layout_constraintTop_toBottomOf="@+id/button_main_incminite" />
|
||
|
<Button
|
||
|
android:id="@+id/button_main_incminite"
|
||
|
android:layout_width="wrap_content"
|
||
|
android:layout_height="wrap_content"
|
||
|
android:layout_marginTop="16dp"
|
||
|
android:text="@string/main_incminite"
|
||
|
app:layout_constraintEnd_toStartOf="@+id/button_main_decminite"
|
||
|
app:layout_constraintHorizontal_bias="0.5"
|
||
|
app:layout_constraintStart_toStartOf="parent"
|
||
|
app:layout_constraintTop_toBottomOf="@+id/textview_main_measuredat" />
|
||
|
|
||
|
<NumberPicker
|
||
|
android:id="@+id/numberpicker_main_fraction"
|
||
|
android:layout_width="wrap_content"
|
||
|
android:layout_height="wrap_content"
|
||
|
app:layout_constraintBottom_toBottomOf="@+id/numberpicker_main_integral"
|
||
|
app:layout_constraintEnd_toStartOf="@+id/button_main_submit"
|
||
|
app:layout_constraintHorizontal_bias="0.5"
|
||
|
app:layout_constraintStart_toEndOf="@+id/numberpicker_main_integral"
|
||
|
app:layout_constraintTop_toTopOf="@+id/numberpicker_main_integral" />
|
||
|
<Button
|
||
|
android:id="@+id/button_main_decminite"
|
||
|
android:layout_width="wrap_content"
|
||
|
android:layout_height="wrap_content"
|
||
|
android:text="@string/main_decminite"
|
||
|
app:layout_constraintBottom_toBottomOf="@+id/button_main_incminite"
|
||
|
app:layout_constraintEnd_toEndOf="parent"
|
||
|
app:layout_constraintHorizontal_bias="0.5"
|
||
|
app:layout_constraintStart_toEndOf="@+id/button_main_incminite"
|
||
|
app:layout_constraintTop_toTopOf="@+id/button_main_incminite" />
|
||
|
|
||
|
<Button
|
||
|
android:id="@+id/button_main_submit"
|
||
|
android:layout_width="wrap_content"
|
||
|
android:layout_height="wrap_content"
|
||
|
android:text="@string/main_submit"
|
||
|
app:layout_constraintBottom_toBottomOf="@+id/numberpicker_main_fraction"
|
||
|
app:layout_constraintEnd_toEndOf="parent"
|
||
|
app:layout_constraintHorizontal_bias="0.5"
|
||
|
app:layout_constraintStart_toEndOf="@+id/numberpicker_main_fraction"
|
||
|
app:layout_constraintTop_toTopOf="@+id/numberpicker_main_fraction" />
|
||
|
<NumberPicker
|
||
|
android:id="@+id/numberpicker_main_integral"
|
||
|
android:layout_width="wrap_content"
|
||
|
android:layout_height="wrap_content"
|
||
|
android:layout_marginTop="16dp"
|
||
|
app:layout_constraintEnd_toStartOf="@+id/numberpicker_main_fraction"
|
||
|
app:layout_constraintHorizontal_bias="0.5"
|
||
|
app:layout_constraintStart_toStartOf="parent"
|
||
|
app:layout_constraintTop_toBottomOf="@+id/button_main_incminite" />
|
||
|
|
||
|
<TextView
|
||
|
android:id="@+id/textview_main_listtitle"
|
||
|
android:layout_width="0dp"
|
||
|
android:layout_height="wrap_content"
|
||
|
android:layout_marginStart="8dp"
|
||
|
android:layout_marginTop="16dp"
|
||
|
android:text="@string/main_listtitle"
|
||
|
android:textSize="18sp"
|
||
|
app:layout_constraintEnd_toEndOf="parent"
|
||
|
app:layout_constraintStart_toStartOf="parent"
|
||
|
app:layout_constraintTop_toBottomOf="@+id/numberpicker_main_integral" />
|
||
|
<NumberPicker
|
||
|
android:id="@+id/numberpicker_main_fraction"
|
||
|
android:layout_width="wrap_content"
|
||
|
android:layout_height="wrap_content"
|
||
|
app:layout_constraintBottom_toBottomOf="@+id/numberpicker_main_integral"
|
||
|
app:layout_constraintEnd_toStartOf="@+id/button_main_submit"
|
||
|
app:layout_constraintHorizontal_bias="0.5"
|
||
|
app:layout_constraintStart_toEndOf="@+id/numberpicker_main_integral"
|
||
|
app:layout_constraintTop_toTopOf="@+id/numberpicker_main_integral" />
|
||
|
|
||
|
<androidx.recyclerview.widget.RecyclerView
|
||
|
android:id="@+id/recyclerview_main_record"
|
||
|
android:layout_width="0dp"
|
||
|
android:layout_height="0dp"
|
||
|
android:layout_marginTop="16dp"
|
||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||
|
app:layout_constraintEnd_toEndOf="parent"
|
||
|
app:layout_constraintStart_toStartOf="parent"
|
||
|
app:layout_constraintTop_toBottomOf="@+id/textview_main_listtitle" />
|
||
|
<Button
|
||
|
android:id="@+id/button_main_submit"
|
||
|
android:layout_width="wrap_content"
|
||
|
android:layout_height="wrap_content"
|
||
|
android:text="@string/main_submit"
|
||
|
app:layout_constraintBottom_toBottomOf="@+id/numberpicker_main_fraction"
|
||
|
app:layout_constraintEnd_toEndOf="parent"
|
||
|
app:layout_constraintHorizontal_bias="0.5"
|
||
|
app:layout_constraintStart_toEndOf="@+id/numberpicker_main_fraction"
|
||
|
app:layout_constraintTop_toTopOf="@+id/numberpicker_main_fraction" />
|
||
|
|
||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||
|
<TextView
|
||
|
android:id="@+id/textview_main_listtitle"
|
||
|
android:layout_width="0dp"
|
||
|
android:layout_height="wrap_content"
|
||
|
android:layout_marginStart="8dp"
|
||
|
android:layout_marginTop="16dp"
|
||
|
android:text="@string/main_listtitle"
|
||
|
android:textSize="18sp"
|
||
|
app:layout_constraintEnd_toEndOf="parent"
|
||
|
app:layout_constraintStart_toStartOf="parent"
|
||
|
app:layout_constraintTop_toBottomOf="@+id/numberpicker_main_integral" />
|
||
|
|
||
|
<androidx.recyclerview.widget.RecyclerView
|
||
|
android:id="@+id/recyclerview_main_record"
|
||
|
android:layout_width="0dp"
|
||
|
android:layout_height="0dp"
|
||
|
android:layout_marginTop="16dp"
|
||
|
app:layoutManager="LinearLayoutManager"
|
||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||
|
app:layout_constraintEnd_toEndOf="parent"
|
||
|
app:layout_constraintStart_toStartOf="parent"
|
||
|
app:layout_constraintTop_toBottomOf="@+id/textview_main_listtitle" />
|
||
|
|
||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||
|
</layout>
|
||
| learn/android/TempRecorderJetpackKt/app/src/main/res/layout/item_temperature.xml | ||
|---|---|---|
|
<?xml version="1.0" encoding="utf-8"?>
|
||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||
|
|
||
|
<data>
|
||
|
<variable
|
||
|
name="temperature"
|
||
|
type="com.torutk.android.temprecorder.jetpackkt.domain.Temperature" />
|
||
|
</data>
|
||
|
|
||
|
<LinearLayout
|
||
|
android:layout_width="match_parent"
|
||
|
android:layout_height="wrap_content">
|
||
|
|
||
|
<TextView
|
||
|
android:id="@+id/measured_at"
|
||
|
android:layout_width="wrap_content"
|
||
|
android:layout_height="wrap_content"
|
||
|
android:layout_weight="1"
|
||
|
android:textAlignment="center"
|
||
|
android:textSize="16sp"
|
||
|
app:measuredAtFormatted="@{temperature}" />
|
||
|
|
||
|
<TextView
|
||
|
android:id="@+id/measurement"
|
||
|
android:layout_width="wrap_content"
|
||
|
android:layout_height="wrap_content"
|
||
|
android:layout_weight="1"
|
||
|
android:text="@{String.valueOf(temperature.measurement)}"
|
||
|
android:textAlignment="center"
|
||
|
android:textSize="16sp" />
|
||
|
</LinearLayout>
|
||
|
</layout>
|
||
| learn/android/TempRecorderJetpackKt/app/src/main/res/values-ja/strings.xml | ||
|---|---|---|
|
<?xml version="1.0" encoding="utf-8"?>
|
||
|
<resources>
|
||
|
<string name="app_name">検温くん(Jetpack Kotlin)</string>
|
||
|
<string name="main_submittitle">体温を登録</string>
|
||
|
<string name="main_incminite">10分後</string>
|
||
|
<string name="main_decminite">10分前</string>
|
||
|
<string name="main_submit">登録</string>
|
||
|
<string name="main_listtitle">検温の履歴</string>
|
||
|
</resources>
|
||
| learn/android/TempRecorderJetpackKt/app/src/main/res/values/strings.xml | ||
|---|---|---|
|
<string name="main_decminite">-10 Minites</string>
|
||
|
<string name="main_submit">Submit</string>
|
||
|
<string name="main_listtitle">My Temperature Record</string>
|
||
|
<string name="main_measured_at_format">MM.dd HH:mm</string>
|
||
|
</resources>
|
||
| learn/android/TempRecorderJetpackKt/build.gradle | ||
|---|---|---|
|
jcenter()
|
||
|
}
|
||
|
dependencies {
|
||
|
classpath "com.android.tools.build:gradle:4.0.1"
|
||
|
classpath 'com.android.tools.build:gradle:4.1.1'
|
||
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||
|
|
||
|
// NOTE: Do not place your application dependencies here; they belong
|
||
refs #167 Implements with DataBinding, LiveData, ViewModel, Repository and Room.