简体   繁体   English

Android Kotlin:从 ViewModel 启动时设置片段 TextView 背景

[英]Android Kotlin: Setting fragment TextView background on start from ViewModel

I am confused by a certain inconsistency in my code, where only part of the data is loading.我对我的代码中的某些不一致感到困惑,其中只有部分数据正在加载。 I am trying to set up a grid of TextViews in my fragment, which read from a list variable called board on the ViewModel for that fragment.我正在尝试在我的片段中设置一个 TextViews 网格,该网格从该片段的 ViewModel 上名为board的列表变量中读取。 The TextView text is set as board[n].text from the view model, where n is its index in the list, and this loads just fine. TextView 文本被设置为来自视图模型的board[n].text ,其中 n 是它在列表中的索引,这加载得很好。 I am also trying to set the TextView background to one of three background resources, which are saved as an int board[n].marking on the view model.我还尝试将 TextView 背景设置为三个背景资源之一,这些资源在视图模型上保存为 int board[n].marking

This does not work.这不起作用。 It seems that it is trying to load the background for each TextView before board has been fully initialized in the view model, but it does not seem to try to do the same for the TextView text.似乎它正在尝试在视图模型中完全初始化board之前为每个 TextView 加载背景,但它似乎并没有尝试为 TextView 文本做同样的事情。 Here are the relevant parts of my code.这是我的代码的相关部分。 First, the XML layout:一、XML布局:

<?xml version="1.0" encoding="utf-8"?>
<layout
    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"
    tools:context=".screens.game.GameFragment">

    <data>
        <import type="android.view.View"/>
        <variable
            name="gameViewModel"
            type="com.example.mygametitle.screens.game.GameViewModel" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

(...)

        <TextView
            android:id="@+id/field13"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_marginTop="@dimen/board_vertical_margin"
            android:background="@{gameViewModel.board[2].marking}"
            android:onClick="@{ () -> gameViewModel.openGameDialog(2, field13)}"
            android:text="@{gameViewModel.board[2].text}"
(...)

There are 25 fields like that.有 25 个这样的字段。 All of the text loads properly, but none of the background images load.所有文本都正确加载,但没有任何背景图像加载。 If instead I hardcode the background I want, as such, it loads properly: android:background="@drawable/board_fieldbackground_checked" .相反,如果我硬编码我想要的背景,它会正确加载: android:background="@drawable/board_fieldbackground_checked" This won't work for me though, as I need to read what each entry's background is upon startup--they don't all start checked.不过,这对我不起作用,因为我需要在启动时阅读每个条目的背景——它们并没有全部开始检查。

On the view model, board is made by reading a set of 25 entries from a Room database, each including (among other info) a text string and a marking int.在视图模型上, board是通过从 Room 数据库读取一组 25 个条目来制作的,每个条目包括(除其他信息外)一个文本字符串和一个标记int。 These all update properly--if I use a debug function to print out the contents of my board, they all have the proper text and marking upon closing and reopening the fragment.这些都正确更新——如果我使用调试功能打印出我的板的内容,它们在关闭和重新打开片段时都有正确的文本和标记。 When the fragment opens, all the text is correct, but the backgrounds are not.当片段打开时,所有文本都是正确的,但背景不是。 Any ideas on why my backgrounds aren't loading the same way the text is?关于为什么我的背景没有以与文本相同的方式加载的任何想法?

Here's some of the relevant viewmodel code:这是一些相关的视图模型代码:

class GameViewModel(
    val database: BoardDatabaseDao,
    application: Application,
    val boardTitle: String) : AndroidViewModel(application)  {

    val BG_UNMARKED = R.drawable.board_fieldbackground_bordered
    val BG_CHECKED = R.drawable.board_fieldbackground_checked
    val BG_MISSED = R.drawable.board_fieldbackground_missed

    private val thisBoardEntries = MutableLiveData<List<BoardField>?>()

    private val _board = MutableLiveData<List<BoardField>>()
    val board: LiveData<List<BoardField>>
        get() = _board

    private suspend fun getEntries() : List<BoardField>? {
        Log.i("GameViewModel", "Running database.getFromParent(boardTitle), function getEntries().")
        val entries = database.getFromParent(boardTitle)
        return entries
    }

    init {
        viewModelScope.launch {
            Log.i("GameViewModel", "Start viewModelScope.launch on init block.")
            thisBoardEntries.value = getEntries()

            if (thisBoardEntries.value?.isEmpty()!!) {
                Log.i(
                    "GameViewModel",
                    "allEntries.value is EMPTY, seemingly: ${thisBoardEntries.value}, should be empty"
                )
            } else {
                Log.i(
                    "GameViewModel",
                    "allEntries.value is NOT empty, seemingly: ${thisBoardEntries.value}, should be size 25"
                )
                _board.value = thisBoardEntries.value
            }
        }
    }



    fun markFieldMissed(index: Int, view: TextView) {
        Log.i("GameViewModel", "My Textview looks like this: $view")

        _board.value!![index].marking = BG_MISSED
        view.setBackgroundResource(BG_MISSED)
        Log.i("GameViewModel", "Set background to $BG_MISSED")
        val color = getColor(getApplication(), R.color.white_text_color)
        view.setTextColor(color)

        viewModelScope.launch {
            val markedField = getEntryAtIndex(boardTitle, convertIndexToLocation(index))
            Log.i("GameViewModel", "I think markedField is $markedField")
            if (markedField != null) {
                markedField.marking = BG_MISSED
                update(markedField)
                Log.i("GameViewModel", "Updated field with $BG_MISSED marking on DB: $markedField")
            }
        }
    }

    fun markFieldChecked(index: Int, view: TextView) {
        _board.value!![index].marking = BG_CHECKED
        view.setBackgroundResource(BG_CHECKED)
        Log.i("GameViewModel", "Set background to $BG_CHECKED")
        val color = getColor(getApplication(), R.color.white_text_color)
        view.setTextColor(color)

        viewModelScope.launch {
            val markedField = getEntryAtIndex(boardTitle, convertIndexToLocation(index))
            Log.i("GameViewModel", "I think markedField is $markedField")
            if (markedField != null) {
                markedField.marking = BG_CHECKED
                update(markedField)
                Log.i("GameViewModel", "Updated field with $BG_CHECKED marking on DB: $markedField")
            }
        }
    }

    fun debugPrintEntries() {
        Log.i("GameViewModel", "DebugPrintEntries function: ${_board.value}")
    }


(2020-11-05) Edit 1: Part of the issue was indeed a resource not being read as such. (2020-11-05) 编辑 1:问题的一部分确实是没有被这样阅读的资源。 I made the following additions/changes in my layout XML, which gets me a bit further:我在我的布局 XML 中做了以下添加/更改,这让我更进一步:

    <data>
        <import type="androidx.core.content.ContextCompat"/>
(...)
    </data>

        <TextView
(...)
            android:background="@{ContextCompat.getDrawable(context, gameViewModel.BG_CHECKED)}"
(...)

With a hardcoded resource for BG_CHECKED as my background image, everything loads and displays nicely.使用 BG_CHECKED 的硬编码资源作为我的背景图像,一切都可以很好地加载和显示。 The problem is once again that the background is not read from board[4].marking (which contains BG_CHECKED as its value), although the text has no problem being read from board[4].text问题再次是背景不是从board[4].marking (其中包含 BG_CHECKED 作为其值)读取的,尽管从board[4].text读取文本没有问题

The following replacement in the layout XML does not work, causing an exception: Caused by: android.content.res.Resources$NotFoundException: Resource ID #0x0 with the line布局 XML 中的以下替换不起作用,导致异常: Caused by: android.content.res.Resources$NotFoundException: Resource ID #0x0 with the line

android:background="@{ContextCompat.getDrawable(context, gameViewModel.board[4].marking)}"

I haven't used data binding, but I think it might be because you're just providing an Int as the background, which happens to represent a resource ID - but the data binding doesn't know that, so it doesn't know it needs to resolve it to a drawable value in resources?我没有使用过数据绑定,但我认为这可能是因为你只是提供一个Int作为背景,它恰好代表一个资源 ID - 但数据绑定不知道这一点,所以它不知道它需要将其解析为资源中的可绘制值吗? When you set it manually you're explicitly telling it to do that by using the @drawable syntax当您手动设置它时,您通过使用@drawable语法明确告诉它这样做

Here's a blog where someone runs into something similar (well that situation anyway, but with colours) - their second solution is to add a ContextCompat import to the data block, and then use that to do a ContextCompat.getColor lookup in the data binding expression. 这是一个博客,其中有人遇到了类似的事情(无论如何都是这种情况,但是有颜色) - 他们的第二个解决方案是将ContextCompat导入添加到data块,然后使用它在数据绑定表达式中进行ContextCompat.getColor查找. Maybe you could do something similar to get the drawable you need也许你可以做一些类似的事情来获得你需要的 drawable

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何从 ViewModel 导航到 Fragment in Android Kotlin - How to navigate from ViewModel to a Fragment in Android Kotlin 在Kotlin中的片段上的textView上设置边距 - Setting margins on a textView on a fragment in kotlin 使用Kotlin从Fragment实例化ViewModel - Instanciate ViewModel from Fragment with Kotlin Android Kotlin 由 Delegates.observable 从后台线程进入 ViewModel - Android Kotlin by Delegates.observable from background Thread into ViewModel 片段中的DataBinding+ViewModel Android Kotlin - DataBinding+ViewModel in Fragment Android Kotlin 无法从一个片段观察 Viewmodel,但可以在 Android [Kotlin] 中的不同片段中工作 - Unable to observe Viewmodel from one fragment but works from different fragment in Android [Kotlin] 从Intent检索信息,然后将其设置为片段内的textview-Android - Retrieving Info from an Intent, then Setting it to a textview inside a Fragment - Android 使用 ViewModel (Android Studio, Kotlin) 将数据从片段 A 发送到片段 B - Sending data from fragment A to fragment B using ViewModel (Android Studio, Kotlin) android-在表格中设置textview的背景 - android - setting the background of a textview in a table Android Kotlin - 从另一个 ViewModel 继承一个 VIewModel - Android Kotlin - inherit a VIewModel from another ViewModel
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM