Я сделал хороший способ обнаружить и обновить пользовательский интерфейс, когда это необходимо, когда дата изменилась. Он основан на широковещательном приемнике, который должен быть зарегистрирован в методе onStart и не зарегистрирован в методе onStop (отмена регистрации выполняется автоматически в моем коде).
build.gradle
implementation 'com.jakewharton.threetenabp:threetenabp:1.0.5'
LocalDateEx.kt
object LocalDateEx {
/**an alternative of LocalDate.now(), as it requires initialization using AndroidThreeTen.init(context), which takes a bit time (loads a file)*/
@JvmStatic
fun getNow(): LocalDate = Calendar.getInstance().toLocalDate()
}
fun Calendar.toLocalDate(): LocalDate = LocalDate.of(get(Calendar.YEAR), get(Calendar.MONTH) + 1, get(Calendar.DAY_OF_MONTH))
DateChangedBroadcastReceiver.kt
abstract class DateChangedBroadcastReceiver : BroadcastReceiver() {
private var curDate = LocalDateEx.getNow()
/**called when the receiver detected the date has changed. You should still check it yourself, because you might already be synced with the new date*/
abstract fun onDateChanged(previousDate: LocalDate, newDate: LocalDate)
@Suppress("MemberVisibilityCanBePrivate")
fun register(context: Context, date: LocalDate) {
curDate = date
val filter = IntentFilter()
filter.addAction(Intent.ACTION_TIME_CHANGED)
filter.addAction(Intent.ACTION_DATE_CHANGED)
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED)
context.registerReceiver(this, filter)
val newDate = LocalDateEx.getNow()
if (newDate != curDate) {
curDate = newDate
onDateChanged(date, newDate)
}
}
/**a convenient way to auto-unregister when activity/fragment has stopped. This should be called on the onStart method of the fragment/activity*/
fun registerOnStart(activity: AppCompatActivity, date: LocalDate, fragment: Fragment? = null) {
register(activity, date)
val lifecycle = fragment?.lifecycle ?: activity.lifecycle
lifecycle.addObserver(object : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onStop() {
Log.d("AppLog", "onStop, so unregistering")
lifecycle.removeObserver(this)
activity.unregisterReceiver(this@DateChangedBroadcastReceiver)
}
})
}
override fun onReceive(context: Context, intent: Intent) {
val newDate = LocalDateEx.getNow()
Log.d("AppLog", "got intent:" + intent.action + " curDate:" + curDate + " newDate:" + newDate)
if (newDate != curDate) {
Log.d("AppLog", "cur date is different, so posting event")
val previousDate = curDate
curDate = newDate
onDateChanged(previousDate, newDate)
}
}
}
MainActivity.kt
class MainActivity : AppCompatActivity() {
var today = LocalDateEx.getNow()
val receiver = object : DateChangedBroadcastReceiver() {
override fun onDateChanged(previousDate: LocalDate, newDate: LocalDate) {
Log.d("AppLog", "onDateChangedEvent:" + newDate + " previousDate:" + previousDate)
if (newDate != today) {
today = newDate
textView.text = "date updated:" + today
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d("AppLog", "onCreate")
setContentView(R.layout.activity_main)
textView.text = today.toString()
}
override fun onStart() {
super.onStart()
receiver.registerOnStart(this, today)
}
}
activity_main.xml
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity">
<TextView
android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center"/>
</FrameLayout>
В качестве альтернативы вышеизложенному, используя только класс календаря Android, без необходимости использовать другую библиотеку:
fun Calendar.equalInDateAlone(cal: Calendar): Boolean =
get(Calendar.YEAR) == cal.get(Calendar.YEAR) && get(Calendar.MONTH) == cal.get(Calendar.MONTH) && get(Calendar.DAY_OF_MONTH) == cal.get(Calendar.DAY_OF_MONTH)
fun Calendar.resetTimeFields(): Calendar {
set(Calendar.HOUR_OF_DAY, 0)
set(Calendar.SECOND, 0)
set(Calendar.MINUTE, 0)
set(Calendar.MILLISECOND, 0)
return this
}
abstract class DateChangedBroadcastReceiver : BroadcastReceiver() {
private var curDate = Calendar.getInstance().resetTimeFields()
/**called when the receiver detected the date has changed. You should still check it yourself, because you might already be synced with the new date*/
abstract fun onDateChanged(previousDate: Calendar, newDate: Calendar)
@Suppress("MemberVisibilityCanBePrivate")
fun register(context: Context, date: Calendar) {
curDate = (date.clone() as Calendar).resetTimeFields()
val filter = IntentFilter()
filter.addAction(Intent.ACTION_TIME_CHANGED)
filter.addAction(Intent.ACTION_DATE_CHANGED)
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED)
context.registerReceiver(this, filter)
val newDate = Calendar.getInstance().resetTimeFields()
if (!newDate.equalInDateAlone(curDate)) {
curDate = newDate.clone() as Calendar
onDateChanged(date, newDate)
}
}
/**a convenient way to auto-unregister when activity/fragment has stopped. This should be called on the onStart method of the fragment/activity*/
@Suppress("unused")
fun registerOnStart(activity: AppCompatActivity, date: Calendar, fragment: Fragment? = null) {
register(activity, date)
val lifecycle = fragment?.lifecycle ?: activity.lifecycle
lifecycle.addObserver(object : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onStop() {
// Log.d("AppLog", "onStop, so unregistering")
lifecycle.removeObserver(this)
activity.unregisterReceiver(this@DateChangedBroadcastReceiver)
}
})
}
override fun onReceive(context: Context, intent: Intent) {
val newDate = Calendar.getInstance().resetTimeFields()
// Log.d("AppLog", "got intent:" + intent.action + " curDate:" + curDate.toSimpleDateString() + " newDate:" + newDate.toSimpleDateString())
if (!newDate.equalInDateAlone(curDate)) {
// Log.d("AppLog", "cur date is different, so posting event")
val previousDate = curDate
curDate = newDate
onDateChanged(previousDate, newDate)
}
}
}
person
android developer
schedule
14.02.2018