如何在Android Fragment中显示和保存联系人姓名



我是Android开发和Kotlin的新手。我在阅读Android编程书籍时遇到了一个问题。这本书使用startActivityForResult()和onActivityResult()已弃用,所以我使用registerForActivityResult()代替,然后问题发生了…

问题是我从联系人应用程序中获得联系人名称,但不能在片段中的按钮上显示它或将其保存到数据库。

我对程序进行了调试,发现原因可能是联系人app返回后的执行顺序,顺序为:onCreate ->onCreateView→onViewCreated→ActivityResultCallback→crimeLiveData的观察者。在ActivityResultCallback中,我得到一个新的犯罪,而不是当我进入CrimeFragment时,所以将其保存到db没有效果,但可以设置猜疑按钮文本。在crimeLiveData的Observer中,我得到了我想要的犯罪,但没有联系人姓名或联系人姓名不变。updateUI()之后,将覆盖susectbutton文本并返回到原始值。因此,结果是片段的视图是不变的,尽管我已经按下了怀疑按钮并更改了联系人名称。

如果你知道如何显示我选择的联系人姓名并保存它,请告诉我,非常感谢!!

代码如下:

// The fragment where I have a suspectButton to show the contact name
class CrimeFragment : Fragment() {
private lateinit var solvedCheckBox: CheckBox
private lateinit var reportButton: Button
// the button to show contact name
private lateinit var suspectButton: Button
private lateinit var dateButton: Button
private lateinit var crime: Crime
private lateinit var titleField: EditText

// register a call back for returning from the contacts app
private val resultLauncher: ActivityResultLauncher<Intent> = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == Activity.RESULT_OK && it.data != null) {
val contactUri: Uri? = it.data?.data
val queryFields = arrayOf(ContactsContract.Contacts.DISPLAY_NAME)
val cursor = contactUri?.let { uri ->
requireActivity().contentResolver.query(
uri, queryFields, null, null, null)
}
// Get the contact name, save it to db, set the button text
cursor?.use {
if (it.count != 0) {
it.moveToFirst()
val suspect = it.getString(0)
crime.suspect = suspect
crimeDetailViewModel.saveCrime(crime)
suspectButton.text = suspect
}
}
}
}
private val crimeDetailViewModel: CrimeDetailViewModel by lazy {
ViewModelProvider(this).get(CrimeDetailViewModel::class.java)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
crime = Crime()
val crimeId: UUID = arguments?.getSerializable(ARG_CRIME_ID) as UUID
crimeDetailViewModel.loadCrime(crimeId)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_crime, container, false)
titleField = view.findViewById(R.id.crime_title) as EditText
dateButton = view.findViewById(R.id.crime_date) as Button
solvedCheckBox = view.findViewById(R.id.crime_solved) as CheckBox
reportButton = view.findViewById(R.id.crime_report) as Button
suspectButton = view.findViewById(R.id.crime_suspect) as Button
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
crimeDetailViewModel.crimeLiveData.observe(viewLifecycleOwner) { crime ->
crime?.let {
this.crime = crime
updateUI()
}
}
}
private fun updateUI() {
titleField.setText(crime.title)
dateButton.text = crime.date.toString()
solvedCheckBox.apply {
isChecked = crime.isSolved
jumpDrawablesToCurrentState()
}
if (crime.suspect.isNotEmpty()) {
suspectButton.text = crime.suspect
}
}
private fun getCrimeReport(): String {
val solvedString = if (crime.isSolved) getString(R.string.crime_report_solved) else getString(R.string.crime_report_unsolved)
val dateString = DateFormat.format(DATE_FORMAT, crime.date).toString()
val suspect = if (crime.suspect.isBlank()) getString(R.string.crime_report_no_suspect) else getString(R.string.crime_report_subject)
return getString(R.string.crime_report, crime.title, dateString, solvedString, suspect)
}
override fun onStart() {
super.onStart()
val titleWatcher = object : TextWatcher {
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
// left blank
}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
crime.title = p0.toString()
}
override fun afterTextChanged(p0: Editable?) {
// left blank
}
}
titleField.addTextChangedListener(titleWatcher)
solvedCheckBox.apply {
setOnCheckedChangeListener { _, isChecked ->
crime.isSolved = isChecked
}
}
dateButton.setOnClickListener {
DatePickerFragment.newInstance(crime.date).apply {
show(this@CrimeFragment.parentFragmentManager, DIALOG_DATE)
}
}
reportButton.setOnClickListener {
Intent(Intent.ACTION_SEND).apply {
type= "text/plain"
putExtra(Intent.EXTRA_TEXT, getCrimeReport())
putExtra(Intent.EXTRA_SUBJECT, getString(R.string.crime_report_subject))
}.also {
intent ->
val chooserIntent = Intent.createChooser(intent, getString(R.string.send_report))
startActivity(chooserIntent)
}
}
suspectButton.apply {
val pickContactIntent = Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI)
setOnClickListener {
resultLauncher.launch(pickContactIntent)
}
}
parentFragmentManager.setFragmentResultListener(REQUEST_KEY, viewLifecycleOwner) { requestKey, result ->
if (requestKey == DIALOG_DATE) {
crime.date = result.getSerializable(ARG_DATE) as Date
updateUI()
}
}
}
override fun onStop() {
super.onStop()
crimeDetailViewModel.saveCrime(crime)
}
companion object {
fun newInstance(crimeId: UUID): CrimeFragment {
val args = Bundle().apply {
putSerializable(ARG_CRIME_ID, crimeId)
}
return CrimeFragment().apply {
arguments = args
}
}
}
}
// ViewModel to the fragment
class CrimeDetailViewModel() : ViewModel() {
private val crimeRepository = CrimeRepository.get()
private val crimeIdLiveData = MutableLiveData<UUID>()
var crimeLiveData: LiveData<Crime?> = Transformations.switchMap(crimeIdLiveData) { crimeId ->
crimeRepository.getCrime(crimeId)
}
fun loadCrime(crimeId: UUID) {
crimeIdLiveData.value = crimeId
}
fun saveCrime(crime: Crime) {
crimeRepository.updateCrime(crime)
}
}
// Crime entity
@Entity
data class Crime(
@PrimaryKey val id: UUID = UUID.randomUUID(),
var title: String = "",
var date: Date = Date(),
var isSolved: Boolean = false,
var requiresPolice: Boolean = false,
var suspect: String = ""
)
// layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="16dp">
<TextView
style="?android:listSeparatorTextViewStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/crime_title_lable"/>
<EditText
android:id="@+id/crime_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/crime_title_hint"/>
<TextView
style="?android:listSeparatorTextViewStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/crime_details_label"/>
<Button
android:id="@+id/crime_date"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:text="Wed Nov 14 11:56 EST 2018"/>
<CheckBox
android:id="@+id/crime_solved"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/crime_solved_label"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/crime_suspect_text"
android:id="@+id/crime_suspect"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/crime_report_text"
android:id="@+id/crime_report"/>
</LinearLayout>

我找到了一个解决这个问题的方法。由于ActivityResultCallback和crimeLiveData的Observer的执行顺序不同,以下代码在ActivityResultCallback中不起作用:

crime.suspect = suspect
crimeDetailViewModel.saveCrime(crime)
suspectButton.text = suspect

所以我把它改成:

suspectButton.text = suspect

和undateUI()从:

if (crime.suspect.isNotEmpty()) {
suspectButton.text = crime.suspect
}

:

if (suspectButton.text.toString() != getString(R.string.crime_suspect_text)) {
crime.suspect = suspectButton.text.toString()
} else if (crime.suspect.isNotEmpty()) {
suspectButton.text = crime.suspect
}

现在,猜疑按钮可以反映我所做的更改。当离开此片段时,更改保存,问题解决。

最新更新