package ドメイン.worldclockimport android.app.Activityimport android.content.Contextimport android.content.Intentimport android.content.SharedPreferencesimport androidx.appcompat.app.AppCompatActivityimport android.os.Bundleimport android.view.Viewimport android.widget.Toastimport kotlinx.android.synthetic.main.activity_main.*class MainActivity : AppCompatActivity() { companion object { const val PREF_FILE_NAME = "prefs" const val PREF_SET_KEY = "time_zone" const val REQUEST_CODE = 1 } private lateinit var preferences: SharedPreferences private val addBtnClickListener = View.OnClickListener { val intent = Intent(this, TimeZoneSelectActivity::class.java) // 引数1: インテント, 引数2: 「どの画面から帰ってきたのか?」を判断するためのリクエストコード startActivityForResult(intent, REQUEST_CODE) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) preferences = getSharedPreferences(PREF_FILE_NAME, Context.MODE_PRIVATE) btnAdd.setOnClickListener(addBtnClickListener) // リストを表示する showWorldClocks() } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == REQUEST_CODE // リクエストコードが一致しているか? && resultCode == Activity.RESULT_OK // 結果が「OK(ポジティブな応答)」か? && data != null) { // データの中身はある(null でない)か? // データを取り出す val timeZone = data.getStringExtra(TimeZoneSelectActivity.EXTRA_TIME_ZONE) // *** SharedPreferences に保存する処理 *** // SharedPreferences からデータを取り出す // Set<*>.toMutableSet(): Set<*> を MutableSet<*> に変換する val timeZones = preferences.getStringSet(PREF_SET_KEY, mutableSetOf())?.toMutableSet() ?: mutableSetOf() // 取り出した Set にデータを追加する timeZones.add(timeZone) // SharedPreferences に「データを追加した Set」を保存する preferences.edit().putStringSet(PREF_SET_KEY, timeZones).apply() // リストを表示する showWorldClocks() } } /** * SharedPreferences からデータを取り出して ListView に表示する */ private fun showWorldClocks() { // SharedPreferences からデータを取り出す val timeZones = preferences.getStringSet(PREF_SET_KEY, setOf()) ?: setOf() // データからアダプターを作成して、リストにセットする // Set<*>.toTypedArray(): Set<*> を Array<*> に変換する clockListView.adapter = TimeZoneAdapter(this, timeZones.toTypedArray()) }}package ドメイン.worldclockimport android.annotation.SuppressLintimport android.content.Contextimport android.view.LayoutInflaterimport android.view.Viewimport android.view.ViewGroupimport android.widget.BaseAdapterimport android.widget.TextClockimport android.widget.TextViewimport java.util.*class TimeZoneAdapter( private val context: Context, private val timeZones: Array<String> = TimeZone.getAvailableIDs()) : BaseAdapter() { @SuppressLint("SetTextI18n") override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View { // Item(リストに表示される項目)の View(Widget) インスタンスを準備する // convertView(リサイクルできる Item)がある場合はそれを使い、 // ない場合は Inflater(createView()) を利用して新規作成する val view = convertView ?: createView(parent) // Item にデータをセットする // セットするデータを取得する val timeZoneId = getItem(position) val timeZone = TimeZone.getTimeZone(timeZoneId) // Item の tag properties から ViewHolder を取得して // ViewHolder の参照先(View(Widget))にデータをセットする (view.tag as ViewHolder).run { name.text = "${timeZone.displayName}(${timeZone.id})" clock.timeZone = timeZone.id } // セットしたデータを return する return view } override fun getItem(position: Int) = timeZones[position] override fun getItemId(position: Int) = position.toLong() override fun getCount() = timeZones.size private fun createView(parent: ViewGroup?): View { val view = LayoutInflater.from(context).inflate( R.layout.list_time_zone_row, parent, false ) view.tag = ViewHolder(view) return view } class ViewHolder(view: View) { // Item の中にある View(Widget) を findViewById() で探してくる // ※ ここでは「kotlinx.android.synthetic.main.」は使えないので注意 val name: TextView = view.findViewById(R.id.txtTimeZone) val clock: TextClock = view.findViewById(R.id.txcTime) }}package ドメイン.worldclockimport android.app.Activityimport android.content.Intentimport androidx.appcompat.app.AppCompatActivityimport android.os.Bundleimport android.widget.AdapterViewimport kotlinx.android.synthetic.main.activity_time_zone_select.*class TimeZoneSelectActivity : AppCompatActivity() { companion object { const val EXTRA_TIME_ZONE = "timeZone" } private val listItemClickListener = AdapterView.OnItemClickListener { parent, _, position, _ -> val timeZone = parent.adapter.getItem(position) as String // 遷移元へデータを返す用の Intent(空っぽ)を作成する val result = Intent() // Intent にデータを詰め込む result.putExtra(EXTRA_TIME_ZONE, timeZone) // 「OK(ポジティブな応答)」という結果とデータをセットする setResult(Activity.RESULT_OK, result) // Activity を終了させる finish() } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_time_zone_select) // 最初に「キャンセル(戻るボタンをクリック)」した時の // 「CANCEL(ネガティブな応答)」という結果をセットする setResult(Activity.RESULT_CANCELED) clockListView.adapter = TimeZoneAdapter(this) clockListView.onItemClickListener = listItemClickListener }}<?xml version="1.0" encoding="utf-8"?><androidx.constraintlayout.widget.ConstraintLayout 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"> <TextClock android:id="@+id/txcTime" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="8dp" android:layout_marginTop="8dp" android:layout_marginRight="8dp" android:layout_marginBottom="8dp" android:textSize="64sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.1" tools:text="10:00" /> <TextView android:id="@+id/txtTimeZone" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="8dp" android:layout_marginBottom="8dp" app:layout_constraintBottom_toTopOf="@+id/txcTime" app:layout_constraintEnd_toStartOf="@+id/txcTime" tools:text="日本標準時" /> <ListView android:id="@+id/clockList" android:layout_width="0dp" android:layout_height="0dp" android:layout_marginStart="8dp" android:layout_marginTop="8dp" android:layout_marginEnd="8dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/txcTime" tools:listitem="@layout/list_time_zone_row" /> <Button android:id="@+id/btnAdd" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="8dp" android:layout_marginBottom="8dp" android:text="@string/add_button" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" /></androidx.constraintlayout.widget.ConstraintLayout><?xml version="1.0" encoding="utf-8"?><androidx.constraintlayout.widget.ConstraintLayout 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=".TimeZoneSelectActivity"> <ListView android:id="@+id/clockList" android:layout_width="0dp" android:layout_height="0dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" tools:listitem="@layout/list_time_zone_row" /></androidx.constraintlayout.widget.ConstraintLayout><?xml version="1.0" encoding="utf-8"?><androidx.constraintlayout.widget.ConstraintLayout 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="wrap_content"> <TextClock android:id="@+id/txcTime" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:layout_marginEnd="8dp" android:layout_marginBottom="16dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" tools:text="10:00" /> <TextView android:id="@+id/txtTimeZone" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:layout_marginTop="16dp" android:layout_marginEnd="8dp" android:layout_marginBottom="16dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@+id/txcTime" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" tools:text="日本標準時" /></androidx.constraintlayout.widget.ConstraintLayout>