正在更新来自 Activity 的 UI 片段
Updating UI fragment from Activity
我目前正在开发 Android NFC 应用程序。此应用程序包含一个 NavigationDrawer,我可以在其中访问 3 个不同的片段,每个片段对应 3 个不同的 NFC 功能。
我的目标是当 onNewIntent 方法被调用时,当检测到 NFC 标签时,我用标签中的信息更新 UI。
起初,UI 的更新是同步完成的,但我们的想法是将来通过使用协程来实现异步。
问题很简单,当调用onNewIntent函数时UI没有更新,你能帮我吗?
MainActivity:
private val memoryViewModel: MemoryViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
checkNFC()
mNfcAdapter = NfcAdapter.getDefaultAdapter(this)
setNfcIntent()
configureToolbar()
val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
val navView: NavigationView = findViewById(R.id.nav_view)
val navController = findNavController(R.id.nav_host_fragment)
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
appBarConfiguration = AppBarConfiguration(setOf(
R.id.nav_memory, R.id.nav_tag, R.id.nav_product), drawerLayout)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
// Inflate the menu; this adds items to the action bar if it is present.
menuInflater.inflate(R.menu.main, menu)
return true
}
override fun onSupportNavigateUp(): Boolean {
val navController = findNavController(R.id.nav_host_fragment)
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}
private fun configureToolbar() {
val toolbar: Toolbar = findViewById(R.id.toolbar)
setSupportActionBar(toolbar)
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)
Log.d(TAG, "Card ID: " + Tools.byteArrayToHex(tag!!.id))
val techList = tag!!.techList
updateUI()
//Check that the discovered tag is a vicinity tag
if (techList[0] == "android.nfc.tech.NfcV") {
val tagUid = tag!!.id
nfcvTag = NfcV.get(tag)
//ISO/IEC 15693 tags can be operated in two modes:
// Select mode and Addressed mode.
//To work in the select mode it is needed to send a SELECT
// command at the beginning of communic.
//In the address mode, the tag UID is sent within each command.
//This application works in SELECT MODE.
val select_command: ByteArray = RFCommands.cmd_select
System.arraycopy(tagUid, 0, select_command, 2, 8)
if (nfcvTag != null) {
try {
nfcvTag!!.connect()
val select_respo: ByteArray = nfcvTag!!.transceive(select_command)
Log.d(TAG, "Select response: " +
Tools.byteArrayToHex(select_respo))
} catch (e: IOException) {
e.printStackTrace()
}
}
}
}
private fun updateUI() {
memoryViewModel.setManufacturer(nfcManufacturer.getValue(tag!!.id[6].toInt()))
}
内存片段:
class MemoryFragment : Fragment() {
private lateinit var memoryViewModel: MemoryViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
memoryViewModel =
ViewModelProvider(this).get(MemoryViewModel::class.java)
val root = inflater.inflate(R.layout.fragment_memory, container, false)
val icManuf: TextView = root.findViewById(R.id.ic_manufacturer_value)
memoryViewModel.icManufacturer.observe(viewLifecycleOwner, Observer {
icManuf.text = it
})
return root
}
}
MemoryViewModel:
class MemoryViewModel : ViewModel() {
// The current IC Manufacturer
private val _icManufacturer = MutableLiveData<String>()
val icManufacturer: LiveData<String>
get() = _icManufacturer
init {
_icManufacturer.value = ""
}
fun setManufacturer(value: String) {
_icManufacturer.value = value
}
}
在 mobile_nagivation.xml 文件中创建了 3 个不同的片段:
<?xml version="1.0" encoding="utf-8"?>
<navigation 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:id="@+id/mobile_navigation"
app:startDestination="@+id/nav_memory">
<fragment
android:id="@+id/nav_memory"
android:name="com.bodet.bodettag.ui.memory.MemoryFragment"
android:label="@string/menu_memory"
tools:layout="@layout/fragment_memory" />
<fragment
android:id="@+id/nav_tag"
android:name="com.bodet.bodettag.ui.tag.TagFragment"
android:label="@string/menu_tag_settings"
tools:layout="@layout/fragment_tag" />
<fragment
android:id="@+id/nav_product"
android:name="com.bodet.bodettag.ui.product.ProductFragment"
android:label="@string/menu_product_settings"
tools:layout="@layout/fragment_product" />
</navigation>
activity_main_drawer.xml 文件包含与片段具有相同 ID 的项目:
<item android:title="@string/menu_nfc">
<menu>
<group
android:id="@+id/menu_top"
android:checkableBehavior="single">
<item
android:id="@+id/nav_memory"
android:icon="@drawable/ic_baseline_contactless_24"
android:title="@string/menu_memory" />
<item
android:id="@+id/nav_tag"
android:icon="@drawable/ic_baseline_memory_24"
android:title="@string/menu_tag_settings" />
<item
android:id="@+id/nav_product"
android:icon="@drawable/ic_icon_visio_x7_hov"
android:title="@string/menu_product_settings" />
</group>
</menu>
</item>
您的问题是 ViewModelProviders
returns 个不同的 viewModel
实例,当您尝试分配一些 _icManufacturer
值时,它会在 activity的 ViewModel,但不在片段的 ViewModel 中。
您应该添加以下代码
片段:
fun setManufacturerValue(icManufacturer: String) {
// val icManuf: TextView = root.findViewById(R.id.ic_manufacturer_value)
// icManuf.text = icManufacturer
// or
// memoryViewModel.setManufacturer(icManufacturer)
}
Activity:
private fun updateUI() {
val navController = findNavController(R.id.nav_host_fragment)
val fragment = navController.currentDestination as? MemoryFragment
fragment?.setManufacturer(nfcManufacturer.getValue(tag!!.id[6].toInt()))
}
如果您想同时更新所有片段,请使用共享视图模型
查看文档:https://developer.android.com/topic/libraries/architecture/viewmodel#sharing
在你的情况下:
private val memoryViewModel: MemoryViewModel by activityViewModels()
我目前正在开发 Android NFC 应用程序。此应用程序包含一个 NavigationDrawer,我可以在其中访问 3 个不同的片段,每个片段对应 3 个不同的 NFC 功能。
我的目标是当 onNewIntent 方法被调用时,当检测到 NFC 标签时,我用标签中的信息更新 UI。
起初,UI 的更新是同步完成的,但我们的想法是将来通过使用协程来实现异步。
问题很简单,当调用onNewIntent函数时UI没有更新,你能帮我吗?
MainActivity:
private val memoryViewModel: MemoryViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
checkNFC()
mNfcAdapter = NfcAdapter.getDefaultAdapter(this)
setNfcIntent()
configureToolbar()
val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
val navView: NavigationView = findViewById(R.id.nav_view)
val navController = findNavController(R.id.nav_host_fragment)
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
appBarConfiguration = AppBarConfiguration(setOf(
R.id.nav_memory, R.id.nav_tag, R.id.nav_product), drawerLayout)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
// Inflate the menu; this adds items to the action bar if it is present.
menuInflater.inflate(R.menu.main, menu)
return true
}
override fun onSupportNavigateUp(): Boolean {
val navController = findNavController(R.id.nav_host_fragment)
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}
private fun configureToolbar() {
val toolbar: Toolbar = findViewById(R.id.toolbar)
setSupportActionBar(toolbar)
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)
Log.d(TAG, "Card ID: " + Tools.byteArrayToHex(tag!!.id))
val techList = tag!!.techList
updateUI()
//Check that the discovered tag is a vicinity tag
if (techList[0] == "android.nfc.tech.NfcV") {
val tagUid = tag!!.id
nfcvTag = NfcV.get(tag)
//ISO/IEC 15693 tags can be operated in two modes:
// Select mode and Addressed mode.
//To work in the select mode it is needed to send a SELECT
// command at the beginning of communic.
//In the address mode, the tag UID is sent within each command.
//This application works in SELECT MODE.
val select_command: ByteArray = RFCommands.cmd_select
System.arraycopy(tagUid, 0, select_command, 2, 8)
if (nfcvTag != null) {
try {
nfcvTag!!.connect()
val select_respo: ByteArray = nfcvTag!!.transceive(select_command)
Log.d(TAG, "Select response: " +
Tools.byteArrayToHex(select_respo))
} catch (e: IOException) {
e.printStackTrace()
}
}
}
}
private fun updateUI() {
memoryViewModel.setManufacturer(nfcManufacturer.getValue(tag!!.id[6].toInt()))
}
内存片段:
class MemoryFragment : Fragment() {
private lateinit var memoryViewModel: MemoryViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
memoryViewModel =
ViewModelProvider(this).get(MemoryViewModel::class.java)
val root = inflater.inflate(R.layout.fragment_memory, container, false)
val icManuf: TextView = root.findViewById(R.id.ic_manufacturer_value)
memoryViewModel.icManufacturer.observe(viewLifecycleOwner, Observer {
icManuf.text = it
})
return root
}
}
MemoryViewModel:
class MemoryViewModel : ViewModel() {
// The current IC Manufacturer
private val _icManufacturer = MutableLiveData<String>()
val icManufacturer: LiveData<String>
get() = _icManufacturer
init {
_icManufacturer.value = ""
}
fun setManufacturer(value: String) {
_icManufacturer.value = value
}
}
在 mobile_nagivation.xml 文件中创建了 3 个不同的片段:
<?xml version="1.0" encoding="utf-8"?>
<navigation 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:id="@+id/mobile_navigation"
app:startDestination="@+id/nav_memory">
<fragment
android:id="@+id/nav_memory"
android:name="com.bodet.bodettag.ui.memory.MemoryFragment"
android:label="@string/menu_memory"
tools:layout="@layout/fragment_memory" />
<fragment
android:id="@+id/nav_tag"
android:name="com.bodet.bodettag.ui.tag.TagFragment"
android:label="@string/menu_tag_settings"
tools:layout="@layout/fragment_tag" />
<fragment
android:id="@+id/nav_product"
android:name="com.bodet.bodettag.ui.product.ProductFragment"
android:label="@string/menu_product_settings"
tools:layout="@layout/fragment_product" />
</navigation>
activity_main_drawer.xml 文件包含与片段具有相同 ID 的项目:
<item android:title="@string/menu_nfc">
<menu>
<group
android:id="@+id/menu_top"
android:checkableBehavior="single">
<item
android:id="@+id/nav_memory"
android:icon="@drawable/ic_baseline_contactless_24"
android:title="@string/menu_memory" />
<item
android:id="@+id/nav_tag"
android:icon="@drawable/ic_baseline_memory_24"
android:title="@string/menu_tag_settings" />
<item
android:id="@+id/nav_product"
android:icon="@drawable/ic_icon_visio_x7_hov"
android:title="@string/menu_product_settings" />
</group>
</menu>
</item>
您的问题是 ViewModelProviders
returns 个不同的 viewModel
实例,当您尝试分配一些 _icManufacturer
值时,它会在 activity的 ViewModel,但不在片段的 ViewModel 中。
您应该添加以下代码
片段:
fun setManufacturerValue(icManufacturer: String) {
// val icManuf: TextView = root.findViewById(R.id.ic_manufacturer_value)
// icManuf.text = icManufacturer
// or
// memoryViewModel.setManufacturer(icManufacturer)
}
Activity:
private fun updateUI() {
val navController = findNavController(R.id.nav_host_fragment)
val fragment = navController.currentDestination as? MemoryFragment
fragment?.setManufacturer(nfcManufacturer.getValue(tag!!.id[6].toInt()))
}
如果您想同时更新所有片段,请使用共享视图模型
查看文档:https://developer.android.com/topic/libraries/architecture/viewmodel#sharing
在你的情况下:
private val memoryViewModel: MemoryViewModel by activityViewModels()