我正在尝试按照 Google 提供的文档将 DatePicker 添加到我的 Android 应用程序中。我试图做到这一点,以便在按下按钮时出现选择器,允许用户输入日历日期。当我尝试在应用程序中加载此按钮时,它崩溃了,并在 logcat 中给出了 NullPointerException。我相信我需要扩充 XML 视图,但我不确定如何去做。
这是我的AssignmentEntryScreen.kt 文件,我正在其中尝试创建按钮(我已通过注释指示了按钮的相关代码所在位置:
//HERE
):
object AssignmentEntryDestination : NavigationDestination, ComponentActivity() {
override val route = "assignment_entry"
override val titleRes = R.string.assignment_entry_title
}
//Manages layout of the assignment entry screen
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AssignmentEntryScreen(
navigateBack: () -> Unit,
onNavigateUp: () -> Unit,
canNavigateBack: Boolean = true,
viewModel: AssignmentEntryViewModel = viewModel(factory = AppViewModelProvider.Factory)
) {
val coroutineScope = rememberCoroutineScope()
//Title text
Scaffold(
topBar = {
InventoryTopAppBar(
title = stringResource(AssignmentEntryDestination.titleRes),
canNavigateBack = canNavigateBack,
navigateUp = onNavigateUp
)
}
//Body with data entry field and save button
) { innerPadding ->
AssignmentEntryBody(
assignmentUiState = viewModel.assignmentUiState,
onAssignmentValueChange = viewModel::updateUiState,
//Save button
onSaveClick = {
coroutineScope.launch {
viewModel.saveAssignment()
navigateBack()
}
},
modifier = Modifier
.padding(
start = innerPadding.calculateStartPadding(LocalLayoutDirection.current),
top = innerPadding.calculateTopPadding(),
end = innerPadding.calculateEndPadding(LocalLayoutDirection.current),
)
.verticalScroll(rememberScrollState())
.fillMaxWidth()
)
}
}
//Body function to create field entry boxes and save button
@Composable
fun AssignmentEntryBody(
assignmentUiState: AssignmentUiState,
onAssignmentValueChange: (AssignmentDetails) -> Unit,
onSaveClick: () -> Unit,
modifier: Modifier = Modifier
) {
Column(
modifier = modifier.padding(dimensionResource(id = R.dimen.padding_medium)),
verticalArrangement = Arrangement.spacedBy(dimensionResource(id = R.dimen.padding_large))
) {
//Name and due date inputs
AssignmentInputForm(
assignmentDetails = assignmentUiState.assignmentDetails,
onValueChange = onAssignmentValueChange,
modifier = Modifier.fillMaxWidth()
)
//Save button
Button(
onClick = onSaveClick,
enabled = assignmentUiState.isEntryValid,
shape = MaterialTheme.shapes.small,
modifier = Modifier.fillMaxWidth()
) {
Text(text = stringResource(R.string.save_action))
}
}
}
//Name and due date input forums
@Composable
fun AssignmentInputForm(
assignmentDetails: AssignmentDetails,
modifier: Modifier = Modifier,
onValueChange: (AssignmentDetails) -> Unit = {},
enabled: Boolean = true
) {
Column(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(dimensionResource(id = R.dimen.padding_medium))
) {
//Name forum
Text(text = "Assignment")
OutlinedTextField(
value = assignmentDetails.name,
onValueChange = { onValueChange(assignmentDetails.copy(name = it)) },
label = { Text(stringResource(R.string.assignment_name_req)) },
colors = OutlinedTextFieldDefaults.colors(
focusedContainerColor = MaterialTheme.colorScheme.secondaryContainer,
unfocusedContainerColor = MaterialTheme.colorScheme.secondaryContainer,
disabledContainerColor = MaterialTheme.colorScheme.secondaryContainer,
),
modifier = Modifier.fillMaxWidth(),
enabled = enabled,
singleLine = true
)
//HERE //Date Picker button.
setContentView(R.layout.datepicker)
findViewById<Button>(R.id.pickDate).setOnClickListener {
val newFragment = DatePickerFragment()
newFragment.show(getFragmentManager(),"datePicker")
}
}
}
@Preview(showBackground = true)
@Composable
private fun AssignmentEntryScreenPreview() {
InventoryTheme {
AssignmentEntryBody(assignmentUiState = AssignmentUiState(
AssignmentDetails(
name = "Assignment name", dueDate = Date(5/3/2024)
)
), onAssignmentValueChange = {}, onSaveClick = {})
}
}
datePickerFragment.kt 本身:
package com.example.assignmentNotifier
import android.app.DatePickerDialog
import android.app.Dialog
import android.app.DialogFragment
import android.os.Bundle
import android.widget.DatePicker
import com.example.assignmentNotifier.ui.assignment.AssignmentEntryDestination.setContentView
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Locale
@Suppress("DEPRECATION", "OVERRIDE_DEPRECATION")
class DatePickerFragment : DialogFragment(), DatePickerDialog.OnDateSetListener {
private val calendar = Calendar.getInstance()
private val formatter = SimpleDateFormat("MMM. dd, yyyy", Locale.US)
override fun onCreateDialog(savedInstanceState: Bundle): Dialog {
// Use the current date as the default date in the picker.
setContentView(R.layout.datepicker)
val year = calendar.get(Calendar.YEAR)
val month = calendar.get(Calendar.MONTH)
val day = calendar.get(Calendar.DAY_OF_MONTH)
// Create a new instance of DatePickerDialog and return it.
return DatePickerDialog(activity, this, year, month, day)
}
override fun onDateSet(view: DatePicker, year: Int, month: Int, day: Int) {
calendar.set(year, month, day)
returnFormattedDate(calendar.timeInMillis)
}
private fun returnFormattedDate(timestamp: Long): String? {
return formatter.format(timestamp)
}
}
还有 R.layout.datepicker.xml 文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/pickDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Pick date"/>
</LinearLayout>
我的理解是,我需要在调用
findViewById<Button>(R.id.pickDate)
之前先膨胀 datepicker.xml,但我不确定如何执行此操作,甚至不确定应该将该代码放在哪里。我在网上找到的其他资源使用它们正在工作的片段或活动文件作为上下文来膨胀,但因为我不属于其中任何一个,所以这对我不起作用。
任何帮助将不胜感激。谢谢!
您不能像这样混合基于撰写和视图的 UI 层次结构。您可以设置可组合视图或设置根视图,但不能同时执行两者。因此,如果您使用 compose,则永远不会通过 setContentView 设置根视图。
如果您想在撰写层次结构中嵌入视图,则需要按照此处的步骤操作:https://developer.android.com/develop/ui/compose/migrate/interoperability-apis/views-in-compose基本上,您将其包装在合成中的 AndroidView 对象中。如果您需要膨胀 xml,则不会通过 setContentView 来完成。你用
LayoutInflater.from(context).inflate()