问题
I am using a binding adapter to have mutable text in one of my views. I believe I have it implemented correctly (and it's working in other places), but for the case of mutableText, it's getting the error AAPT: error: attribute mutableText not found
I've looked through some other answers on here, but none of them have been able to solve the issue.
Here is my layout file:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="viewModel"
type="com.example.nhlstats.ui.game.GameViewModel" />
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/awayTeam"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="12dp">
<ImageView
android:id="@+id/awayTeamLogo"
android:layout_height="48dp"
android:layout_width="0dp"
android:layout_weight="1"
tools:src="@drawable/ic_launcher_background"/>
<TextView
android:id="@+id/awayTeamName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
android:layout_gravity="center_vertical"
app:mutableText="@{viewModel.getAwayTeamName()}"
tools:text="CHI Blackhawks"/>
<TextView
android:id="@+id/awayScore"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="center_vertical"
app:mutableText="@{viewModel.getAwayTeamScore().toString()}"
tools:text="0"/>
<TextView
android:id="@+id/gameTime"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="center_vertical"
app:mutableText="@{viewModel.getTimeRemaining()"
tools:text="14:26 3rd"/>
</LinearLayout>
<LinearLayout
android:id="@+id/homeTeam"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp">
<ImageView
android:id="@+id/homeTeamLogo"
android:layout_height="48dp"
android:layout_width="0dp"
android:layout_weight="1"
tools:src="@drawable/ic_launcher_background"/>
<TextView
android:id="@+id/homeTeamName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
android:layout_gravity="center_vertical"
app:mutableText="@{viewModel.getHomeTeamName()}"
tools:text="CAR Hurricanes"/>
<TextView
android:id="@+id/homeScore"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="2"
app:mutableText="@{viewModel.getHomeTeamScore().toString()}"
tools:text="4"/>
</LinearLayout>
</LinearLayout>
</layout>
and my BindingAdpater function:
@BindingAdapter("mutableText")
fun setMutableText(view: TextView, text: MutableLiveData<String>?) {
val parentActivity:AppCompatActivity? = view.getParentActivity()
if (parentActivity != null && text != null) {
text.observe(parentActivity, Observer { value ->
view.text = value?:""
})
}
}
GameViewModel:
class GameViewModel:BaseViewModel() {
private val awayTeamName = MutableLiveData<String>()
private val homeTeamName = MutableLiveData<String>()
private val awayTeamScore = MutableLiveData<Int>()
private val homeTeamScore = MutableLiveData<Int>()
private val timeRemaining = MutableLiveData<String>()
fun bind(response: Game) {
awayTeamName.value = response.gameData.teams.get(0).name
homeTeamName.value = response.gameData.teams.get(1).name
awayTeamScore.value = response.liveData.linescore.teams.get(1).goals
homeTeamScore.value = response.liveData.linescore.teams.get(0).goals
timeRemaining.value = response.liveData.linescore.currentPeriodOrdinal + " " + response.liveData.linescore.currentPeriodTimeRemaining
}
fun getAwayTeamName(): MutableLiveData<String> {
return awayTeamName
}
fun getHomeTeamName(): MutableLiveData<String> {
return homeTeamName
}
fun getAwayTeamScore(): MutableLiveData<Int> {
return awayTeamScore
}
fun getHomeTeamScore(): MutableLiveData<Int> {
return homeTeamScore
}
fun getTimeRemaining(): MutableLiveData<String> {
return timeRemaining
}
}
MainActivity:
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var viewModel: GameListViewModel
private var errorSnackbar: Snackbar? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
binding.gameList.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
viewModel = ViewModelProviders.of(this).get(GameListViewModel::class.java)
viewModel.errorMessage.observe(this, Observer { errorMessage ->
if (errorMessage != null)
showError(errorMessage)
else
hideError()
})
binding.viewModel = viewModel
}
private fun showError(@StringRes errorMessage:Int) {
errorSnackbar = Snackbar.make(binding.root, errorMessage, Snackbar.LENGTH_INDEFINITE)
errorSnackbar?.setAction(R.string.retry, viewModel.errorClickListener)
errorSnackbar?.show()
}
private fun hideError() {
errorSnackbar?.dismiss()
}
}
Thanks in advance.
回答1:
As per your GameViewModel
, I don't think you need any custom binding adapter.
Please check the edited code.
GameViewModel
class GameViewModel:BaseViewModel() {
// Make the variables public and access directly instead of exposing them via get() method.
val awayTeamName = MutableLiveData<String>()
val homeTeamName = MutableLiveData<String>()
val awayTeamScore = MutableLiveData<String>() // Change to String
val homeTeamScore = MutableLiveData<String>() // Change to String
val timeRemaining = MutableLiveData<String>()
fun bind(response: Game) {
awayTeamName.value = response.gameData.teams.get(0).name
homeTeamName.value = response.gameData.teams.get(1).name
awayTeamScore.value = response.liveData.linescore.teams.get(1).goals.toString() // Convert to String from here only instead of writing it in XML!
homeTeamScore.value = response.liveData.linescore.teams.get(0).goals.toString()
timeRemaining.value = response.liveData.linescore.currentPeriodOrdinal + " " + response.liveData.linescore.currentPeriodTimeRemaining
}
}
Your layout file(Notice I accessed the variables directly and removed the toString() as we are now only exposing MutableLiveData
with String
and yes, we are not using your custom binding adapter.
<data>
<variable
name="viewModel"
type="com.example.nhlstats.ui.game.GameViewModel" />
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/awayTeam"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="12dp">
<ImageView
android:id="@+id/awayTeamLogo"
android:layout_height="48dp"
android:layout_width="0dp"
android:layout_weight="1"
tools:src="@drawable/ic_launcher_background"/>
<TextView
android:id="@+id/awayTeamName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
android:layout_gravity="center_vertical"
android:text="@{viewModel.awayTeamName}"
tools:text="CHI Blackhawks"/>
<TextView
android:id="@+id/awayScore"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="center_vertical"
android:text="@{viewModel.awayTeamScore}"
tools:text="0"/>
<TextView
android:id="@+id/gameTime"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="center_vertical"
android:text="@{viewModel.timeRemaining}"
tools:text="14:26 3rd"/>
</LinearLayout>
<LinearLayout
android:id="@+id/homeTeam"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp">
<ImageView
android:id="@+id/homeTeamLogo"
android:layout_height="48dp"
android:layout_width="0dp"
android:layout_weight="1"
tools:src="@drawable/ic_launcher_background"/>
<TextView
android:id="@+id/homeTeamName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
android:layout_gravity="center_vertical"
android:text="@{viewModel.homeTeamName}"
tools:text="CAR Hurricanes"/>
<TextView
android:id="@+id/homeScore"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="2"
android:text="@{viewModel.homeTeamScore}"
tools:text="4"/>
</LinearLayout>
</LinearLayout>
I haven't tested this so let me know how this goes for you.
回答2:
Your binding adapter receives a nullable value, while the viewModel returns a non-null value. Change the text parameter to be non-null, just like the function in the viewModel returns it.
You can also do shorter encapsulation, for example:
private val _data = MutableLiveData<String>()
val data: LiveData<String>
get() = _data
This way you don't have to write long and boring getters like in Java.
来源:https://stackoverflow.com/questions/58576992/android-binding-adapter-attribute-not-found