【问题标题】:Jetpack compose. Date time picker喷气背包组成。日期时间选择器
【发布时间】:2020-02-26 15:26:04
【问题描述】:

jetpack compose 是否有日期时间选择器视图,或者我应该自己创建它?我试图用谷歌搜索它,但我找不到可以使用的组件。

【问题讨论】:

    标签: android android-jetpack android-jetpack-compose


    【解决方案1】:

    他们似乎仍在努力,与此同时,如果您需要一些非常简单的东西,您可以使用 compose 与 Android 视图的互操作:

    @Composable
    fun DatePicker(onDateSelected: (LocalDate) -> Unit, onDismissRequest: () -> Unit) {
        val selDate = remember { mutableStateOf(LocalDate.now()) }
    
        //todo - add strings to resource after POC
        Dialog(onDismissRequest = { onDismissRequest() }, properties = DialogProperties()) {
            Column(
                modifier = Modifier
                    .wrapContentSize()
                    .background(
                        color = MaterialTheme.colors.surface,
                        shape = RoundedCornerShape(size = 16.dp)
                    )
            ) {
                Column(
                    Modifier
                        .defaultMinSize(minHeight = 72.dp)
                        .fillMaxWidth()
                        .background(
                            color = MaterialTheme.colors.primary,
                            shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp)
                        )
                        .padding(16.dp)
                ) {
                    Text(
                        text = "Select date".toUpperCase(Locale.ENGLISH),
                        style = MaterialTheme.typography.caption,
                        color = MaterialTheme.colors.onPrimary
                    )
    
                    Spacer(modifier = Modifier.size(24.dp))
    
                    Text(
                        text = selDate.value.format(DateTimeFormatter.ofPattern("MMM d, YYYY")),
                        style = MaterialTheme.typography.h4,
                        color = MaterialTheme.colors.onPrimary
                    )
    
                    Spacer(modifier = Modifier.size(16.dp))
                }
    
                CustomCalendarView(onDateSelected = {
                    selDate.value = it
                })
    
                Spacer(modifier = Modifier.size(8.dp))
    
                Row(
                    modifier = Modifier
                        .align(Alignment.End)
                        .padding(bottom = 16.dp, end = 16.dp)
                ) {
                    TextButton(
                        onClick = onDismissRequest
                    ) {
                        //TODO - hardcode string
                        Text(
                            text = "Cancel",
                            style = MaterialTheme.typography.button,
                            color = MaterialTheme.colors.onPrimary
                        )
                    }
    
                    TextButton(
                        onClick = {
                            onDateSelected(selDate.value)
                            onDismissRequest()
                        }
                    ) {
                        //TODO - hardcode string
                        Text(
                            text = "OK",
                            style = MaterialTheme.typography.button,
                            color = MaterialTheme.colors.onPrimary
                        )
                    }
    
                }
            }
        }
    }
    
    @Composable
    fun CustomCalendarView(onDateSelected: (LocalDate) -> Unit) {
        // Adds view to Compose
        AndroidView(
            modifier = Modifier.wrapContentSize(),
            factory = { context ->
                CalendarView(ContextThemeWrapper(context, R.style.CalenderViewCustom))
            },
            update = { view ->
                view.minDate = // contraints
                view.maxDate = // contraints
    
                view.setOnDateChangeListener { _, year, month, dayOfMonth ->
                    onDateSelected(
                        LocalDate
                            .now()
                            .withMonth(month + 1)
                            .withYear(year)
                            .withDayOfMonth(dayOfMonth)
                    )
                }
            }
        )
    }
    
    <style name="CalenderViewCustom" parent="ThemeOverlay.MaterialComponents.MaterialCalendar">
            <item name="colorAccent"><day selection color></item>
            <item name="colorOnPrimary"><text color></item>
            <item name="colorSurface"><background color></item>
    </style>
    

    你最终会得到这样的结果:

    【讨论】:

    • 此解决方案要求最低 API 级别为 26。
    • @ritwikshanker 如果您需要在较低的 API 中使用此解决方案,只需将 LocalDate 更改为 Date
    • 我已经按照@ShadeToD 的建议对代码进行了一些调整,现在它与 Material Design 3 兼容。看看我的要点:gist.github.com/ArnyminerZ/31b82f559e3bfe42ef336f72854c5854
    【解决方案2】:

    使用库

    在撰写本文时,它尚未实施。但是有一个开源库Compose Material Dialogs

    没有库

    您也可以在没有库的情况下执行此操作,您可以将其放在 onclick 操作中

    private fun showDatePicker() {
        val picker = MaterialDatePicker.Builder.datePicker().build()
        activity?.let {
            picker.show(it.supportFragmentManager, picker.toString())
            picker.addOnPositiveButtonClickListener {
    
            }
        }
    }
    

    【讨论】:

      【解决方案3】:

      目前,jetpack compose 处于 Alpha 状态,androidx.ui.* 中没有 DateTime Picker。 但是,稍后的android团队会添加它或使jetpack compose与android.widget.DatePicker等其他组件互操作 或者我们可以使用 jetpack 组合从 stratch 开发漂亮的日期时间选择器 flutter does

      目前,

      class DateTimeActivity : AppCompatActivity() {
      
          override fun onCreate(savedInstanceState: Bundle?) {
              super.onCreate(savedInstanceState)
      
              val c = Calendar.getInstance()
              val year = c.get(Calendar.YEAR)
              val month = c.get(Calendar.MONTH)
              val day = c.get(Calendar.DAY_OF_MONTH)
              val mHour = c[Calendar.HOUR_OF_DAY]
              val mMinute = c[Calendar.MINUTE]
      
              val datePickerDialog = DatePickerDialog(
                  this, DatePickerDialog.OnDateSetListener
                  { datePicker: DatePicker, day: Int, month: Int, year: Int ->
                      setContent {
                          Column {
                              Text("$day, $month, $year")
                          }
                      }
                  }, year, month, day
              )
      
      
              val timePickerDialog = TimePickerDialog(
                  this,
                  TimePickerDialog.OnTimeSetListener { view, hourOfDay, minute ->
                      setContent {
                          Column {
                              Text("$hourOfDay:$minute")
                          }
                      }
                  }, mHour, mMinute, false
              )
      
      
              setContent {
                  Column {
                      Button(text = "Date",
                          style = OutlinedButtonStyle(),
                          onClick = {
                              datePickerDialog.show()
                          })
                      Button(text = "Time",
                          style = OutlinedButtonStyle(),
                          onClick = {
                              timePickerDialog.show()
                          })
                  }
              }
          }
      }
      

      【讨论】:

        【解决方案4】:

        您可以在 jetpack compose 中显示几行日期选择器

         AndroidView(
                    { CalendarView(it) },
                    modifier = Modifier.wrapContentWidth(),
                    update = { views ->
                        views.setOnDateChangeListener { calendarView, i, i2, i3 ->
                        }
                    }
                )
        

        【讨论】:

        • 这很好。你也能抽出时间吗?
        【解决方案5】:

        没有第三方库,使用Android View解决方案:

        import android.app.TimePickerDialog
        
        @Composable
        fun ShowTimePicker(context: Context, initHour: Int, initMinute: Int) {
            val time = remember { mutableStateOf("") }
            val timePickerDialog = TimePickerDialog(
                context,
                {_, hour : Int, minute: Int ->
                    time.value = "$hour:$minute"
                }, initHour, initMinute, false
            )
            Button(onClick = {
                timePickerDialog.show()
            }) {
                Text(text = "Open Time Picker")
            }
        }
        

        感谢Kiran-Bahalaskar

        【讨论】:

          【解决方案6】:

          你可以使用 DatePickerDialog

          val calendar = Calendar.getInstance()
                      val listener = DatePickerDialog.OnDateSetListener { view, year, month, dayOfMonth ->
                          calendar[Calendar.DAY_OF_MONTH] = dayOfMonth
                          calendar[Calendar.MONTH] = month
                          calendar[Calendar.YEAR] = year
                          
                          selectedDate = calendar.timeInMillis
                      }
                      
                      DatePickerDialog(
                          context,
                          listener,
                          calendar[Calendar.YEAR],
                          calendar[Calendar.MONTH],
                          calendar[Calendar.DAY_OF_MONTH]
                      ).show()
          

          【讨论】:

            【解决方案7】:

            我创建了一个小型库,用于在 Compose 中呈现日历:https://github.com/boguszpawlowski/ComposeCalendar。它本身不是日期选择器,但您可以将其配置为类似日期选择器。

            【讨论】:

              【解决方案8】:

              据我所知,Jetpack Compose 中仍然缺少许多小部件。实现它们的唯一方法是使用 AndroidView 和膨胀布局。这是我的时间选择器代码: XML 文件:

                  <?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">
              
                  <TimePicker
                      android:id="@+id/simpleTimePicker"
                      android:layout_width="0dp"
                      android:layout_height="wrap_content"
                      android:datePickerMode="spinner"
                      android:timePickerMode="spinner"
                      app:layout_constraintEnd_toEndOf="parent"
                      app:layout_constraintStart_toStartOf="parent"
                      app:layout_constraintTop_toTopOf="parent" />
              
                  <Button
                      android:id="@+id/btn_set_time"
                      android:layout_width="wrap_content"
                      android:layout_height="wrap_content"
                      android:text="Set Time"
                      android:textColor="#FFFFFF"
                      app:layout_constraintEnd_toEndOf="parent"
                      app:layout_constraintTop_toBottomOf="@+id/simpleTimePicker"
                      tools:ignore="TextContrastCheck" />
              
                  <Button
                      android:id="@+id/btn_cancel"
                      android:layout_width="wrap_content"
                      android:layout_height="wrap_content"
                      android:text="Cancel"
                      android:textColor="#FFFFFF"
              
                      app:layout_constraintEnd_toStartOf="@+id/btn_set_time"
                      app:layout_constraintTop_toBottomOf="@+id/simpleTimePicker"
                      tools:ignore="TextContrastCheck" />
                  </androidx.constraintlayout.widget.ConstraintLayout>
              

              还有我在 Kotlin 的 Jetpack 撰写窗口中的代码:

               Dialog(onDismissRequest = { timePickerStart = false }) {
                                      AndroidView(
                                          factory = { context: Context ->
                                              LayoutInflater.from(context).inflate(R.layout.customtimepicker, null, false)
                                          },
                                          modifier = Modifier.fillMaxWidth()
                                              .background(Color.White),
                                          update = { view ->
                                              val setTimebutton = view.findViewById<Button>(R.id.btn_set_time)
                                              val cancelButton = view.findViewById<Button>(R.id.btn_cancel)
                                              val simpleTimePicker = view.findViewById<TimePicker>(R.id.simpleTimePicker)
                                              simpleTimePicker.setIs24HourView(true)
                                              setTimebutton.setOnClickListener {
                                                  var hour = simpleTimePicker.hour
                                                  var minute = simpleTimePicker.minute
                                                  addNotePlusViewModel.startTime.value = convertToInstant(hour, minute)
                                                  timePickerStart = false
                                              }
                                              cancelButton.setOnClickListener {
                                                  timePickerStart = false
                                              }
                                          }
                                      )
                                  }
              

              【讨论】:

                【解决方案9】:

                你可以这样做

                import androidx.annotation.StringRes
                import androidx.compose.runtime.Composable
                import androidx.compose.runtime.DisposableEffect
                import androidx.compose.runtime.remember
                import com.google.android.material.datepicker.CalendarConstraints
                import com.google.android.material.datepicker.MaterialDatePicker
                import com.google.android.material.datepicker.MaterialPickerOnPositiveButtonClickListener
                import java.util.*
                import kotlin.time.Duration.Companion.hours
                import kotlin.time.DurationUnit
                
                @Composable
                fun rememberDatePickerDialog(
                    @StringRes title: Int,
                    select: Date? = null,
                    bounds: CalendarConstraints? = null,
                    onDateSelected: (Date) -> Unit = {},
                ): MaterialDatePicker<Long> {
                    val datePicker = remember {
                        MaterialDatePicker.Builder.datePicker()
                            .setSelection((select?.time
                                ?: Date().time) + 24.hours.toLong(DurationUnit.MILLISECONDS))
                            .setCalendarConstraints(bounds)
                            .setTitleText(title)
                            .build()
                    }
                
                    DisposableEffect(datePicker) {
                        val listener = MaterialPickerOnPositiveButtonClickListener<Long> {
                            if (it != null) onDateSelected(Date(it))
                        }
                        datePicker.addOnPositiveButtonClickListener(listener)
                        onDispose {
                            datePicker.removeOnPositiveButtonClickListener(listener)
                        }
                    }
                
                    return datePicker
                }
                

                FragmentManager 用于显示选择器(单击按钮时)。 在使用以下内容之前,请确保您的活动从 FragmentActivity 扩展。

                @Composable
                fun rememberFragmentManager(): FragmentManager {
                    val context = LocalContext.current
                    return remember(context) {
                        (context as FragmentActivity).supportFragmentManager
                    }
                }
                

                示例用例。

                val DATE_FORMAT = SimpleDateFormat("dd/MM/yyyy", Locale.getDefault())
                var date by remember {
                  mutableStateOf(TextFieldValue(DATE_FORMAT.format(Date())))
                }
                val fragmentManager = rememberFragmentManager()
                
                val datePicker = rememberDatePickerDialog(
                    select = DATE_FORMAT.parse(date.text),
                    title = R.string.select_date,
                ) { date = TextFieldValue(DATE_FORMAT.format(it)) }
                
                TextField(
                  readOnly = true,
                  value = date,
                  onValueChange = { date = it },
                  trailingIcon = {
                    IconButton({ datePicker.show(fragmentManager, "Date") }) {
                       Icon(Icons.Default.DateRange,contentDescription = null)
                    }
                  },
                )
                

                【讨论】:

                • 它看起来很酷,但有一个严重的问题:在活动旋转时,选择器将保留在屏幕上,但监听器将未连接。此外,由于当拾取器未附加到片段管理器时,稍后在稍后调用ondispose时,无法将日期选择器忽略日期选择器。 span>
                【解决方案10】:

                Wrap 创建了MaterialDatePicker(感谢@Mitch),但也尝试搜索已包装的Fragment——这在方向改变的情况下很重要——如果有的话,你必须将监听器重新附加到实例。

                @Composable
                fun rememberDatePickerDialog(
                    title: String,
                    selection: LocalDate? = null,
                    bounds: CalendarConstraints? = null,
                    onDateSelected: (LocalDate) -> Unit = {},
                ): DatePicker {
                    val fm = rememberFragmentManager()
                    val tag = "date_picker"
                    val datePicker = remember {
                        @Suppress("UNCHECKED_CAST")
                        fm.findFragmentByTag(tag) as? MaterialDatePicker<Long> ?: createMaterialDatePicker(
                            selection = selection,
                            bounds = bounds,
                            title = title
                        )
                    }
                
                    DisposableEffect(datePicker) {
                        val listener = MaterialPickerOnPositiveButtonClickListener<Long> {
                            if (it != null) onDateSelected(Instant.ofEpochMilli(it).toLocalDateUTC())
                        }
                        datePicker.addOnPositiveButtonClickListener(listener)
                        onDispose {
                            datePicker.removeOnPositiveButtonClickListener(listener)
                        }
                    }
                
                    return DatePicker(datePicker, fm, tag)
                }
                
                class DatePicker(
                        private val materialDatePicker: MaterialDatePicker<Long>,
                        private val fragmentManager: FragmentManager,
                        private val tag: String,
                    ) {
                    
                        fun show() = materialDatePicker.show(fragmentManager, tag)
                    
                        fun hide() = materialDatePicker.dismiss()
                }
                
                private fun createMaterialDatePicker(
                    title: String,
                    selection: LocalDate?,
                    bounds: CalendarConstraints?,
                ) = MaterialDatePicker.Builder.datePicker()
                    .setSelection(selection?.toEpochMillisUTC())
                    .setCalendarConstraints(bounds)
                    .setTitleText(title)
                    .build()
                
                
                @Composable
                fun rememberFragmentManager(): FragmentManager {
                    val context = LocalContext.current
                    return remember(context) {
                        (context as FragmentActivity).supportFragmentManager
                    }
                }
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2022-06-17
                  • 1970-01-01
                  • 2022-06-12
                  • 1970-01-01
                  • 2021-10-27
                  • 1970-01-01
                  • 1970-01-01
                  • 2020-03-24
                  相关资源
                  最近更新 更多