【问题标题】:Updating UI fragment from Activity从 Activity 更新 UI 片段
【发布时间】:2021-07-27 12:39:01
【问题描述】:

我目前正在开发一个 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()))
}

MemoryFragment

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>

【问题讨论】:

    标签: android kotlin user-interface android-fragments viewmodel


    【解决方案1】:

    如果您想同时更新所有片段,请使用共享视图模型

    查看文档:https://developer.android.com/topic/libraries/architecture/viewmodel#sharing

    在你的情况下:

    private val memoryViewModel: MemoryViewModel by activityViewModels()
    

    【讨论】:

    • 感谢您的帮助,但这并不能解决我的问题。
    • 为什么不呢?你有两个 MemoryViewModel 实例,如果你使用 sharedviewmodel,你只会得到一个实例。所以 MainActivity 更新视图模型,观察者更新 UI
    【解决方案2】:

    您的问题是 ViewModelProviders 返回不同的 viewModel 实例,当您尝试分配一些 _icManufacturer 值时,它会在活动的 ViewModel 中发生变化,但不会在片段的 ViewModel 中发生变化。

    你应该添加以下代码

    片段:

    fun setManufacturerValue(icManufacturer: String) {
        // val icManuf: TextView = root.findViewById(R.id.ic_manufacturer_value)
        // icManuf.text = icManufacturer
        // or
        // memoryViewModel.setManufacturer(icManufacturer)
    }
    

    活动:

    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()))
    }
    

    【讨论】:

    • 好的,我理解您描述的问题,这对我来说似乎是合乎逻辑的。但是,当我在 updateUI 方法中初始化 fragment 变量时,我得到了一个 java.lang.NullPointerException: null cannot be cast to non-null type。片段 ID 没问题,但我不明白。
    • @MartinDenion 您是通过 XML 还是在活动中创建片段?显示该代码
    • 我通过 XML 创建了片段,并使用它们在 onCreate() 方法中配置我的导航控制器(有关详细信息,请参阅我编辑的帖子)
    • 查看我编辑的帖子。由于导航控制器,您需要以不同的方式查找片段。
    • 我应用了您的更改,我不再有 NullPointerException 但它不会更新 UI。我不明白为什么......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多