質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
RxJava

RxJavaは、リアクティブプログラミングができるReactiveXのJava向けの実装。軽量であり、その他のライブラリへの依存がなく、RxJavaのjarをパスに通せば使用できます。バージョン2.3からはAndroidもサポートしています。

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

Kotlin

Kotlinは、ジェットブレインズ社のアンドリー・ブレスラフ、ドミトリー・ジェメロフが開発した、 静的型付けのオブジェクト指向プログラミング言語です。

Q&A

解決済

1回答

839閲覧

RxPropertyでイベントの発生順を制御したい

nakasho_dev

総合スコア2655

RxJava

RxJavaは、リアクティブプログラミングができるReactiveXのJava向けの実装。軽量であり、その他のライブラリへの依存がなく、RxJavaのjarをパスに通せば使用できます。バージョン2.3からはAndroidもサポートしています。

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

Kotlin

Kotlinは、ジェットブレインズ社のアンドリー・ブレスラフ、ドミトリー・ジェメロフが開発した、 静的型付けのオブジェクト指向プログラミング言語です。

0グッド

1クリップ

投稿2018/01/15 15:06

編集2018/01/16 03:19

###前提・実現したいこと
RxProperty for Android の初心者です。KotlinでAndroidアプリを開発しています。

AndroidのDatePickerとTimePickerを使い時刻を指定する機能を作成しています。
現在時刻設定ボタンを設定し、ボタンをクリックしたら二つのPickerを現在時刻に合わせるとともに現在時刻を設定しているという状態(このサンプルではbuttonEnabled)を保持したいです。tapButton()がその処理です。
そして二つのPickerのいずれかが動かされたら、現在時刻を設定しているという状態を無効にしたいです。

【追記】
条件について強調していなかったのですが、「現在時刻を設定している」という状態はスピナーで現在時刻に設定しても有効にはしなくないです。
具体的にはYahoo乗換案内アプリでは時刻選択画面で現在時刻をタップしてホーム画面に戻った時のみ「現在時刻」という文字列が表示され、それ以外はスピナーで表示されていた固定の時刻が表示されています。これはスピナーを回して現在の時刻に設定しても固定の時刻が表示されます。
この「現在時刻」が設定されている状態で検索すると、検索ボタンを押した時刻を指定時刻として検索されます。
これを実現するためにはPickerでイベントが発生した際に保存している時刻と比較して同じだったら、という手法は適用できません。

###発生している問題・エラーメッセージ
現在時刻設定ボタンをクリックしてPickerの値を現在時刻に設定し、現在時刻を設定しているという状態を有効にする(buttonEnabled.set(false))処理が走るのですが、その後にPickerのOnDateChanged、または、OnTimeChangedが実行されて現在時刻を設定しているという状態が無効(buttonEnabled.set(true))になります。

01-15 14:22:06.579 D/MainViewModel: tapButton finished 01-15 14:22:06.596 D/MainViewModel: changeDateTimePicker finished 01-15 14:22:06.597 D/MainViewModel: changeDateTimePicker finished

tapButton内の以下の処理がchangeDateTimePickerが終了した後に実行されるように制御したいです。

Kotlin

1 buttonText.set("現在時刻設定中") 2 buttonEnabled.set(false) 3

または、現在時刻設定ボタンのクリックイベント時での時刻指定ではonDateChanged, onTimeChangedが発生しないようにしたいです。

###該当のソースコード

GitHubにコミットしています。

現在設定時刻ボタンのonClickイベント処理(tabButton)とPickerのOnDateChanged、OnTimeChangedのイベント処理(changeDateTimePicker)を以下に示します。

Kotlin

1 //Pickerを現在時刻に設定する 2 fun tapButton() { 3 val calendar = Calendar.getInstance() 4 year.set(calendar.get(Calendar.YEAR)) 5 month.set(calendar.get(Calendar.MONTH)) 6 day.set(calendar.get(Calendar.DAY_OF_MONTH)) 7 hour.set(calendar.get(Calendar.HOUR)) 8 minute.set(calendar.get(Calendar.MINUTE)) 9 10 // 現在時刻設定中なのでボタンテキストを変更、および、ボタンを無効化 11 buttonText.set("現在時刻設定中") 12 buttonEnabled.set(false) 13 Log.d("MainViewModel", "tapButton finished") 14 } 15 16 //Pickerが動かされたら現在時刻から変更されたとしてボタンを有効化、テキストを戻す 17 fun changeDateTimePicker() { 18 buttonText.set("現在時刻を設定") 19 buttonEnabled.set(true) 20 Log.d("MainViewModel", "changeDateTimePicker finished") 21 }

###試したこと

Pickerに対しonDateChangedやonTimeChangedではなくonClickなどで、イベントを取れれば、現在時刻設定ボタンでの時刻指定でのイベントと重ならないとも考えたのですが、PickerにはonClickが実装されておらず実現できません。
tapButtonの処理中はEventListenerを無効にするという方法も考えましたが乱暴な気がします。
どのような対処が、より良いコードとなるのかが分からない状況です。

###補足情報(言語/FW/ツール等のバージョンなど)
Android Studio 3.0.1
Kotlin 1.1.51
io.reactivex.rxjava2:rxjava:2.0.7
com.github.k-kagurazaka.rx-property-android:rx-property:4.0.0
com.github.k-kagurazaka.rx-property-android:rx-property-kotlin:4.0.0
io.reactivex.rxjava2:rxkotlin:2.1.0

サンプルアプリキャプチャ

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

こんな感じでしょうか。

まず、 MainViewModel は、 DatePicker と TimePicker で入力された日付時刻を保持することに専念(_dateTimeStr)します。_dateTimeStr には、DatePicker の onDateChanged、 TimePicker の onTimeChanged を素直に受信して、それらから YYYYMMDDHHMM な文字列を設定します(※YYYYMMDDHHMMな文字列にしたのはデバッグが分かりやすかっただけです、ミリ秒とか Date/Calendar クラスでもよいと思います。)。

次に TwoWay Binding はやめて、 _dateTimeStr から 年月日と時刻それぞれの ReadonlyRxProperty を生成し、OneWay で DatePicker と TimePicker にバインドさせます。

最後に、ボタンを押した時に、その時点での現在日時を保存しておき(_savedCurrentDateTime)、ボタンのタイトルや使用可否は、保存された現在日時とDatePicker+TimePickerで入力された日時が一致しているかどうかで定義された ReadOnlyRxProperty として用意します。

データバインドする ReadOnlyRxProperty は全て _dateTimeStr から生成しているので、 _dateTimeStr が変われば連動して変化するのをイメージすると分かりやすいと思います。
そして _dateTimeStr を変化させるのは DatePicker/TimePicker を操作した時、またはボタンを押したとき、です。

MainViewModel.kt

kotlin

1package nakasho.github.io.datepickersample 2 3import android.arch.lifecycle.ViewModel 4import android.text.TextUtils 5import android.util.Log 6import jp.keita.kagurazaka.rxproperty.RxProperty 7import jp.keita.kagurazaka.rxproperty.toReadOnlyRxProperty 8import java.util.* 9 10class MainViewModel : ViewModel() { 11 private var _savedCurrentDateTime: String = "" // ボタン押下時の現在日時を保存(YYYYMMDDHHMM) 12 private val _dateTimeStr = RxProperty<String>() // DatePicker+TimePicker に表示する日付文字列(YYYYMMDDHHMM) 13 14 private var _buttonTapped: Boolean = false // 現在時刻ボタンが押されたらtrueに 15 16 // YYYYMMDDHHMM から各成分に該当する桁を数値に変換してバインディング用の RxProperty とする 17 val year = _dateTimeStr.map { dt -> dt.substring(0, 4) }.map { it->it.toInt() }.toReadOnlyRxProperty() 18 val month = _dateTimeStr.map { dt -> dt.substring(4, 6) }.map { it->it.toInt() - 1 }.toReadOnlyRxProperty() // month は 0 始まりなので 19 val day = _dateTimeStr.map { dt -> dt.substring(6, 8) }.map { it->it.toInt() }.toReadOnlyRxProperty() 20 val hour = _dateTimeStr.map { dt -> dt.substring(8, 10) }.map { it->it.toInt() }.toReadOnlyRxProperty() 21 val minute = _dateTimeStr.map { dt -> dt.substring(10, 12) }.map { it->it.toInt() }.toReadOnlyRxProperty() 22 23 // 現在時刻ボタンが押され、ボタン押下時の現在日時と DatePicker+TimePickerの日時が同じなら現在時刻設定中であり enabled=false とする 24 val buttonEnabled = _dateTimeStr.map { dt -> !_buttonTapped && !TextUtils.equals(dt, _savedCurrentDateTime) }.toReadOnlyRxProperty() 25 val buttonText = buttonEnabled.map { enable -> if (enable) "現在日時を設定" else "現在時刻設定中" }.toReadOnlyRxProperty() 26 27 init { 28 _dateTimeStr.set("201712312345") // DatePicker+TimePickerの既定値(YYYYMMDDHHMM) 29 } 30 31 //Pickerを現在時刻に設定する 32 //onClickが発生したら実行 33 fun tapButton() { 34 _buttonTapped = true 35 val cal = Calendar.getInstance() 36 val dateTimeStr = String.format("%4d%02d%02d%02d%02d", 37 cal.get(Calendar.YEAR), 38 cal.get(Calendar.MONTH) + 1, // month は 0 始まりなので 39 cal.get(Calendar.DAY_OF_MONTH), 40 cal.get(Calendar.HOUR), 41 cal.get(Calendar.MINUTE)) 42 _savedCurrentDateTime = dateTimeStr 43 _dateTimeStr.set(dateTimeStr) 44 } 45 46 fun changeDate(year:Int, monthIndex:Int, day:Int) { 47 val dateStr = String.format("%4d%02d%02d", year, monthIndex+1, day) // month は 0 始まりなので 48 Log.d("MainViewModel", "changeDate ${dateStr} finished") 49 50 // 日付部を変更して更新 51 var dateTimeStr = _dateTimeStr.get() 52 _dateTimeStr.set(dateStr + dateTimeStr.substring(8)) 53 } 54 55 fun changeTime(hour:Int, minute:Int) { 56 val timeStr = String.format("%02d%02d", hour, minute) 57 Log.d("MainViewModel", "changeTime ${timeStr} finished") 58 59 // 時刻部を変更して更新 60 var dateTimeStr = _dateTimeStr.get() 61 _dateTimeStr.set(dateTimeStr.substring(0, 8) + timeStr) 62 } 63}

activity_main.xml

xml

1<?xml version="1.0" encoding="utf-8"?> 2<layout xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:app="http://schemas.android.com/apk/res-auto" 4 xmlns:tools="http://schemas.android.com/tools"> 5 <data> 6 <variable 7 name="viewModel" 8 type="nakasho.github.io.datepickersample.MainViewModel"/> 9 </data> 10 11 <android.support.constraint.ConstraintLayout 12 android:layout_width="match_parent" 13 android:layout_height="match_parent" 14 tools:context="nakasho.github.io.datepickersample.MainActivity"> 15 <DatePicker 16 android:id="@+id/datePicker" 17 android:layout_width="wrap_content" 18 android:layout_height="wrap_content" 19 android:calendarViewShown="false" 20 android:datePickerMode="spinner" 21 android:spinnersShown="true" 22 android:year="@{viewModel.year.value}" 23 android:month="@{viewModel.month.value}" 24 android:day="@{viewModel.day.value}" 25 android:onDateChanged="@{(view, year, month, day)-> viewModel.changeDate(year, month, day)}" 26 app:layout_constraintLeft_toLeftOf="parent" 27 app:layout_constraintRight_toRightOf="parent" 28 app:layout_constraintTop_toTopOf="parent"/> 29 30 <TimePicker 31 android:id="@+id/timePicker" 32 android:layout_width="wrap_content" 33 android:layout_height="wrap_content" 34 android:timePickerMode="spinner" 35 android:hour="@{viewModel.hour.value}" 36 android:minute="@{viewModel.minute.value}" 37 android:onTimeChanged="@{(view, hour, minute)-> viewModel.changeTime(hour, minute)}" 38 app:layout_constraintLeft_toLeftOf="parent" 39 app:layout_constraintRight_toRightOf="parent" 40 app:layout_constraintTop_toBottomOf="@id/datePicker" /> 41 42 <Button 43 android:id="@+id/button" 44 android:layout_width="wrap_content" 45 android:layout_height="wrap_content" 46 android:text="@{viewModel.buttonText.value}" 47 android:onClick="@{()-> viewModel.tapButton()}" 48 android:enabled="@{viewModel.buttonEnabled.value}" 49 app:layout_constraintLeft_toLeftOf="parent" 50 app:layout_constraintRight_toRightOf="parent" 51 app:layout_constraintTop_toBottomOf="@id/timePicker" 52 app:layout_constraintBottom_toBottomOf="parent"/> 53</android.support.constraint.ConstraintLayout> 54</layout>

投稿2018/01/15 19:16

編集2018/01/16 03:51
amay077

総合スコア1075

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nakasho_dev

2018/01/16 03:21

回答ありがとうございます。 KotlinやRxPropertyの記述の仕方についても勉強になりました。 「現在時刻を設定している」という状態について説明不足だったので追記しました。 Yahoo乗換案内では実現している機能なので何かしら実現方法はあると考えています。
amay077

2018/01/16 03:53

「現在時刻を設定している」について、ちょっとコードを修正してみました。ボタンが押されたことをフラグで保持しておいて、buttonEnabled の定義で、そのフラグを加味することで実現できそうな気がしますね。
nakasho_dev

2018/01/16 11:03

迅速な回答ありがとうございました。非常に助かりました。 Pickerを回したり現在時刻ボタンを押したりを繰り返し操作されることを想定しているため、_buttonTappedをfalseにする機会が必要だったので、tapButtonの最後に実行したら期待通りの動作をしました。 ただ、実機では問題ないのですが、エミュレータだと_buttonTappedをfalseにする処理がイベント処理よりも先になってしまうため想定通りの操作とならないことがありました。HandlerクラスのpostDelayedで300ミリ秒くらい遅らせるとエミュレータでも想定通りの動作となりました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問