It seems I have some problem with kapt. I study Android and try to create app with Room and Coroutines.
I get this kind of mistakes:
enter image description here
and my onclicklistener at XML is underlined with red, and the mistake here is "Cannot find identifier 'onSave' ".
I also tried to set onclick listener at the fragment, but I got the same mistakes.
Neither invalidating caches nor rebuilding project helps.
AddViewModel.kt:
`package com.example.memorizewords.add
import android.app.Application
import androidx.lifecycle.*
import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY
import androidx.lifecycle.viewmodel.CreationExtras
import com.example.memorizewords.database.Word
import com.example.memorizewords.database.WordDatabase
import com.example.memorizewords.database.WordDatabaseDao
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
class AddViewModel(
val database: WordDatabaseDao?,
application: Application) : AndroidViewModel(application) {
private val _targetWord = MutableLiveData<String>()
private val _nativeWord = MutableLiveData<String>()
private suspend fun insert(word: Word) {
database?.insert(word)
}
private fun onSave() {
runBlocking {
viewModelScope.launch(Dispatchers.IO) {}
// val newWord = Word(0L, _targetWord.value!!, _nativeWord.value!!, true)
val newWord = Word(0L, "String", "String", true)
insert(newWord)
}
}
companion object {
val Factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory {
#Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(
modelClass: Class<T>,
extras: CreationExtras
): T {
val application = checkNotNull(extras[APPLICATION_KEY])
val dataSource = WordDatabase.getInstance(application).WordDatabaseDao
AddViewModel(dataSource, application)
return AddViewModel(dataSource, application) as T
}
}
}
}`
AddFragment.kt:
`package com.example.memorizewords.add
import android.os.Bundle
import android.view.LayoutInflater
import android.view.ViewGroup
import android.view.View
import android.widget.Toast
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import com.example.memorizewords.R
import com.example.memorizewords.databinding.FragmentAddBinding
class AddFragment : Fragment() {
private val viewModel: AddViewModel by viewModels { AddViewModel.Factory }
lateinit var targetWord: String
lateinit var nativeWord: String
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val binding: FragmentAddBinding = DataBindingUtil.inflate(
inflater, R.layout.fragment_add, container, false)
binding.setLifecycleOwner(this)
binding.addViewModel = viewModel
}`
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">
<data>
<variable
name="addViewModel"
type="com.example.memorizewords.add.AddViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:id="#+id/targetBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="#dimen/button_margin_left_right"
android:layout_marginEnd="#dimen/button_margin_left_right"
android:hint="#string/enterTarget"
android:textSize="#dimen/edit_text_size"
app:layout_constraintBottom_toTopOf="#+id/guideline2"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.Guideline
android:id="#+id/guideline2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.15" />
<EditText
android:id="#+id/nativeBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="#dimen/button_margin_left_right"
android:layout_marginEnd="#dimen/button_margin_left_right"
android:hint="#string/enterNative"
android:textSize="#dimen/edit_text_size"
app:layout_constraintBottom_toTopOf="#+id/guideline3"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/guideline2" />
<androidx.constraintlayout.widget.Guideline
android:id="#+id/guideline3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.3" />
<Button
android:id="#+id/saveButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="#dimen/save_button_margin"
android:layout_marginEnd="#dimen/save_button_margin"
android:text="#string/save"
android:textSize="#dimen/button_text_size"
android:onClick="#{() -> addViewModel.onSave()}" //onSave here is underlined with red
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/guideline3"
app:layout_constraintVertical_bias="0.1" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>`
module's build.gradle
`plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt'
id 'androidx.navigation.safeargs.kotlin'
}
android {
compileSdk 32
defaultConfig {
applicationId "com.example.memorizewords"
minSdk 21
targetSdk 32
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures {
dataBinding true
viewBinding true
}
}
dependencies {
// Android KTX
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.5.1'
implementation 'com.google.android.material:material:1.7.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
// ViewModel and LiveData
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.5.1'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1'
// Navigation
implementation 'androidx.navigation:navigation-fragment-ktx:2.5.3'
implementation 'androidx.navigation:navigation-ui-ktx:2.5.3'
// Room and Lifecycle dependencies
implementation "androidx.room:room-runtime:2.4.3"
kapt "androidx.room:room-compiler:2.4.3"
// Kotlin Extensions and Coroutines support for Room
implementation("androidx.room:room-ktx:2.4.3")
// Coroutines
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1"
// Testing
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.4'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0'
implementation 'com.jakewharton.timber:timber:4.7.1'
}`
app's build.gradle
`plugins {
id 'com.android.application' version '7.2.0' apply false
id 'com.android.library' version '7.2.0' apply false
id 'org.jetbrains.kotlin.android' version '1.7.10' apply false
id 'androidx.navigation.safeargs.kotlin' version '2.4.1' apply false
}
task clean(type: Delete) {
delete rootProject.buildDir
}`
Related
First of all, I'm new into kotlin and I found issues within my learning process. So I found these errors:
"Cannot find identifier 'Current Item"
The errors occured in the xml file. here is code of the xml file:
<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">
<data>
<variable
name="args"
type="com.example.todoapp.fragments.update.UpdateFragment" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="24dp"
tools:context=".fragments.update.UpdateFragment">
<EditText
android:id="#+id/current_title_et"
android:layout_width="0dp"
android:layout_height="60dp"
android:background="#drawable/custom_input"
android:ems="10"
android:hint="#string/title"
android:inputType="textPersonName"
android:paddingStart="24dp"
android:paddingEnd="24dp"
android:text="#{args.currentItem.title}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Spinner
android:id="#+id/current_priorities_spinner"
android:layout_width="0dp"
android:layout_height="60dp"
android:layout_marginTop="8dp"
android:background="#drawable/custom_input"
android:entries="#array/priorities"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:parsePriorityToInt="#{args.currentItem.priority}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/current_title_et" />
<EditText
android:id="#+id/current_description_et"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="8dp"
android:background="#drawable/custom_input"
android:ems="10"
android:gravity="top|start"
android:hint="#string/description"
android:inputType="textMultiLine"
android:paddingStart="24dp"
android:paddingTop="16dp"
android:paddingEnd="24dp"
android:text="#{args.currentItem.description}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/current_priorities_spinner" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
and this is the kt file
package com.example.todoapp.fragments.update
import android.app.AlertDialog
import android.os.Bundle
import android.view.*
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import com.example.todoapp.R
import com.example.todoapp.data.models.ToDoData
import com.example.todoapp.data.viewmodel.ToDoViewModel
import com.example.todoapp.databinding.FragmentUpdateBinding
import com.example.todoapp.fragments.SharedViewModel
class UpdateFragment : Fragment() {
private val args by navArgs<UpdateFragmentArgs>()
private val mSharedViewModel: SharedViewModel by viewModels()
private val mToDoViewModel: ToDoViewModel by viewModels()
private var _binding: FragmentUpdateBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
// Data binding
_binding = FragmentUpdateBinding.inflate(inflater, container, false)
// Set Menu
setHasOptionsMenu(true)
// Spinner Item Selected Listener
binding.currentPrioritiesSpinner.onItemSelectedListener = mSharedViewModel.listener
return binding.root
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.update_fragment_menu, menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.menu_save -> updateItem()
R.id.menu_delete -> confirmItemRemoval()
}
return super.onOptionsItemSelected(item)
}
private fun updateItem() {
val title = binding.currentTitleEt.text.toString()
val description = binding.currentDescriptionEt.text.toString()
val getPriority = binding.currentPrioritiesSpinner.selectedItem.toString()
val validation = mSharedViewModel.verifyDataFromUser(title, description)
if (validation) {
// Update Current Item
val updatedItem = ToDoData(
args.currentItem.id,
title,
mSharedViewModel.parsePriority(getPriority),
description
)
mToDoViewModel.updateData(updatedItem)
Toast.makeText(requireContext(), "Successfully updated!", Toast.LENGTH_SHORT).show()
// Navigate back
findNavController().navigate(R.id.action_updateFragment_to_listFragment)
} else {
Toast.makeText(requireContext(), "Please fill out all fields.", Toast.LENGTH_SHORT)
.show()
}
}
// Show AlertDialog to Confirm Item Removal
private fun confirmItemRemoval() {
val builder = AlertDialog.Builder(requireContext())
builder.setPositiveButton("Yes") { _, _ ->
mToDoViewModel.deleteItem(args.currentItem)
Toast.makeText(
requireContext(),
"Successfully Removed: ${args.currentItem.title}",
Toast.LENGTH_SHORT
).show()
findNavController().navigate(R.id.action_updateFragment_to_listFragment)
}
builder.setNegativeButton("No") { _, _ -> }
builder.setTitle("Delete '${args.currentItem.title}'?")
builder.setMessage("Are you sure you want to remove '${args.currentItem.title}'?")
builder.create().show()
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
this is the plugin and dependency
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: "kotlin-kapt"
apply plugin: "androidx.navigation.safeargs.kotlin"
apply plugin: "kotlin-parcelize"
android {
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "com.example.todoapp"
minSdkVersion 26
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
buildFeatures{
dataBinding = true
viewBinding = true
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
}
}
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation 'androidx.core:core-ktx:1.5.0'
implementation 'androidx.appcompat:appcompat:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
// Navigation Component
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'
// Room components
implementation "androidx.room:room-runtime:2.3.0"
kapt "androidx.room:room-compiler:2.3.0"
implementation "androidx.room:room-ktx:2.3.0"
androidTestImplementation "androidx.room:room-testing:2.3.0"
// Lifecycle components
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
implementation "androidx.lifecycle:lifecycle-common-java8:2.3.1"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1"
// Kotlin components
api "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0-native-mt"
api "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0-native-mt"
}
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.2.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.10"
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.3.5"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
Any help or explanation would be appreciated
I'm sorry to ask the repeatedly answered question but I just couldn't solve this relating to my specific case, maybe I'm missing something. The error is E/RecyclerView: No adapter attached; skipping layout and I'm not sure, is the problem with an adapter I set or the RecyclerView per se? Also, I was following a tutorial and this was the code that was presented.
(I tried brining the initRecyclerView() into the main onCreateView but no luck. Some answers say to set an empty adapter first and notify it with the changes later but I don't know how to do that.)
This is my HomeFragment:
open class HomeFragment() : Fragment() {
private lateinit var homeViewModel: HomeViewModel
private lateinit var binding: FragmentHomeBinding
private val language = arrayOf("English", "German", "Arabic", "Spanish", "Chinese", "French")
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_home, container, false)
val dao = VocabData.getInstance(this).VocabDao
val repository = VocabRepository(dao)
val factory = HomeViewModelFactory(repository, application = activity?.applicationContext as Application)
homeViewModel = ViewModelProvider(this, factory).get(HomeViewModel::class.java)
binding.myViewModel = homeViewModel
binding.lifecycleOwner = this
initRecyclerView()
homeViewModel.message.observe(viewLifecycleOwner, Observer {
it.getContentIfNotHandled()?.let {
Toast.makeText(requireContext(), it, Toast.LENGTH_LONG).show()
}
})
// create an adapter
val arrayAdapter =
ArrayAdapter(requireContext(), android.R.layout.simple_spinner_dropdown_item, language)
binding.spinner.adapter = arrayAdapter
// Set layout to use when the list of choices appear
arrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
// Set Adapter to Spinner
binding.spinner.setAdapter(arrayAdapter)
val button = binding.tagButton
button.setOnClickListener{
GlobalScope.launch(Dispatchers.IO) {
homeViewModel.tagger(binding.spinner)
}
}
return binding.root
}
private fun initRecyclerView(){
binding.vocabRecyclerView.layoutManager = LinearLayoutManager(requireContext())
displayVocabsList()
}
private fun displayVocabsList() {
homeViewModel.vocabs.observe(viewLifecycleOwner, Observer {
Log.i("MYTAG", it.toString())
binding.vocabRecyclerView.adapter = MyRecyclerViewAdapter(it, { selectedItem: Vocab -> listItemClicked(selectedItem) })
})
}
private fun listItemClicked(vocab: Vocab){
Toast.makeText(requireContext(), "selected sentence is ${vocab.sentString}", Toast.LENGTH_LONG).show()
}
}
And this is my RecyclerViewAdapter, which is really just boilerplate code:
class MyRecyclerViewAdapter(private val vocabsList: List<Vocab>, private val clickListener:(Vocab)->Unit) :
RecyclerView.Adapter<MyViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val binding : ListItemBinding =
DataBindingUtil.inflate(layoutInflater, R.layout.list_item, parent, false)
return MyViewHolder(binding)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.bind(vocabsList[position], clickListener)
}
override fun getItemCount(): Int {
return vocabsList.size
}
}
class MyViewHolder(private val binding: ListItemBinding):RecyclerView.ViewHolder(binding.root){
fun bind(vocab: Vocab, clickListener:(Vocab)->Unit){
binding.sentenceTextView.text = vocab.sentString
binding.listItemLayout.setOnClickListener{
clickListener(vocab)
}
}
}
I think that's all the related code. If you have an idea please let me know how to avoid this error/lag, thank you!
UPDATE
activity_main.xml
<layout xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/nav_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="0dp"
android:layout_marginEnd="0dp"
android:background="?android:attr/windowBackground"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:menu="#menu/bottom_nav_menu" >
<androidx.viewpager2.widget.ViewPager2
android:layout_width="match_parent"
android:layout_height="match_parent" />
</com.google.android.material.bottomnavigation.BottomNavigationView>
<androidx.fragment.app.FragmentContainerView
android:id="#+id/nav_host_fragment"
class = "androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:layout_constraintBottom_toTopOf="#id/nav_view"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="#navigation/mobile_navigation" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
fragment_home.xml
<layout xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="myViewModel"
type="com.jwanhsulaiman.talktag.ui.home.HomeViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="8dp"
tools:context=".ui.home.HomeFragment">
<EditText
android:id="#+id/editTextTextMultiLine"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:ems="10"
android:gravity="start|top"
android:hint="Input Sentence(s)\n"
android:inputType="textMultiLine"
android:text="#={myViewModel.inputVocab}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/spinner" />
<Button
android:id="#+id/tag_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:enabled="#{myViewModel.enabled}"
android:text="#={myViewModel.tagAll}"
android:textSize="14sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="#+id/editTextTextMultiLine" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/vocab_recycler_view"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/progressBar" />
<Button
android:id="#+id/clear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:onClick="#{()->myViewModel.deleteAll()}"
android:text="CLEAR"
app:layout_constraintEnd_toStartOf="#+id/progressBar"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/editTextTextMultiLine" />
<Spinner
android:id="#+id/spinner"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ProgressBar
android:id="#+id/progressBar"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:visibility="#{!myViewModel.barProgress}"
app:layout_constraintEnd_toStartOf="#+id/tag_button"
app:layout_constraintTop_toBottomOf="#+id/editTextTextMultiLine" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
HomeViewModel.kt
import android.annotation.SuppressLint
import android.app.Application
import android.view.View
import android.widget.Spinner
import androidx.databinding.Bindable
import androidx.databinding.BindingAdapter
import androidx.databinding.Observable
import androidx.databinding.ObservableField
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.jwanhsulaiman.talktag.Event
import com.jwanhsulaiman.talktag.R
import com.jwanhsulaiman.talktag.database.Vocab
import com.jwanhsulaiman.talktag.database.VocabRepository
import edu.stanford.nlp.tagger.maxent.MaxentTagger
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import opennlp.tools.sentdetect.SentenceDetectorME
import opennlp.tools.sentdetect.SentenceModel
#BindingAdapter("android:visibility")
fun setVisibility(view: View, visible: Boolean) {
view.visibility = if (visible) View.INVISIBLE else View.VISIBLE
}
#BindingAdapter("android:enabled")
fun setEnabled(view: View, enabled: Boolean) {
view.isEnabled = !enabled
}
#SuppressLint("StaticFieldLeak")
class HomeViewModel(private val repository: VocabRepository, application: Application) : AndroidViewModel(
application
), Observable {
private val context = getApplication<Application>().applicationContext
private val model: SentenceModel = SentenceModel(context.resources.openRawResource(R.raw.en))
private val sDetector = SentenceDetectorME(model)
private var senlist = mutableListOf<String?>()
val vocabs = repository.vocabs
#Bindable
var barProgress = ObservableField<Boolean>()
#Bindable
var enabled = ObservableField<Boolean>()
private fun makeVisible(){
this.barProgress.set(true) }
private fun makeInvisible(){
this.barProgress.set(false) }
private fun makeEnabled(){
this.enabled.set(true) }
private fun makeDisabled(){
this.enabled.set(false) }
#Bindable
val inputVocab = MutableLiveData<String>()
#Bindable
val tagAll = MutableLiveData<String>()
private val statusMessage = MutableLiveData<Event<String>>()
val message : LiveData<Event<String>>
get() = statusMessage
init {
tagAll.postValue("Tag!")
}
suspend fun tagger(spinner: Spinner){
if (inputVocab.value.isNullOrBlank()) {
statusMessage.postValue(Event("Please enter sentence"))
} else {
withContext(Dispatchers.IO) {
//tag words
makeEnabled()
makeVisible()
}
}
}
private suspend fun splitSens(vocab: String): MutableList<String?> {
withContext(Dispatchers.IO) {
//split sentences
}
return senlist
}
private suspend fun tagAll(vocab: String){
withContext(Dispatchers.IO){
insert(Vocab(0, vocab))
inputVocab.postValue(null)
}
makeInvisible()
makeDisabled()
}
fun insert(vocab: Vocab) : Job = viewModelScope.launch {
repository.insert(vocab)
statusMessage.value = Event("Vocab inserted successfuly")
}
fun update(vocab: Vocab) : Job = viewModelScope.launch {
repository.update(vocab)
}
fun delete(vocab: Vocab) : Job = viewModelScope.launch {
repository.delete(vocab)
}
fun deleteAll() = viewModelScope.launch {
repository.deleteAll()
}
override fun addOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback?) {
//TODO("Not yet implemented")
}
override fun removeOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback?) {
//TODO("Not yet implemented")
}
}
build.gradle
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
/**
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt'
}
**/
android {
compileSdkVersion 29
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "com.jwanhsulaiman.talktag"
minSdkVersion 16
targetSdkVersion 29
versionCode 1
versionName "1.0"
multiDexEnabled true
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures {
dataBinding true
}
}
dependencies {
implementation files('libs/postagger.jar')
implementation files('libs/nlp/opennlp-tools-1.9.3.jar')
def lifecycle_version = "2.2.0"
def room_version = "2.2.3"
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.vectordrawable:vectordrawable:1.1.0'
implementation 'androidx.navigation:navigation-fragment:2.3.3'
implementation 'androidx.navigation:navigation-ui:2.3.3'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0'
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.3'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.3'
implementation 'androidx.room:room-runtime:2.2.6'
testImplementation 'junit:junit:4.13.2'
kapt "com.android.databinding:compiler:3.5.0"
implementation 'androidx.fragment:fragment-ktx:1.3.0'
// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
// LiveData
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
// Annotation processor
//noinspection LifecycleAnnotationProcessorWithJava8
kapt "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
// optional - Kotlin Extensions and Coroutines support for Room
implementation "androidx.room:room-ktx:$room_version"
//coroutines
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.1'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
kapt 'androidx.room:room-compiler:2.2.6'
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0"
// Kotlin Extensions and Coroutines support for Room
implementation "androidx.room:room-ktx:2.2.6"
implementation 'com.android.support:multidex:1.0.3'
}
Ok, it's normal you have this message because in your code, you' ll do this :
homeViewModel.vocabs.observe(viewLifecycleOwner, Observer {
Log.i("MYTAG", it.toString())
binding.vocabRecyclerView.adapter = MyRecyclerViewAdapter(it, { selectedItem: Vocab -> listItemClicked(selectedItem) })
})
The best solution to not have this message is to set an adapter in createView :
myRecyclerViewAdapter = MyRecyclerViewAdapter({ selectedItem: Vocab -> listItemClicked(selectedItem) })
binding.vocabRecyclerView.adapter = myRecyclerViewAdapter
and :
homeViewModel.vocabs.observe(viewLifecycleOwner, Observer {
myRecyclerViewAdapter.addData(it)
}
and for recyclerView :
class MyRecyclerViewAdapter : .... {
private var values = arrayListOf<....>()
fun addData(values : List<>){
values.addAll(values)
}
}
NB: I put addAll but I don't know what you do, may be you have to do something else.
You have this message because there's no adapter. You have an adpater only when there're data with your observe. It's better if you initialize your adapter and after listening for new datas.
}
I'm using a SwitchCompat in a fragment where I keep getting and Unresolved reference that don't let me compile.
I don't know where to look at any more.. I believe I have my gradle files how they are supposed to be but I still suspect is something from there..
Any idea what I might be doing wrong?
Thank you in advance!! Any tip would be very appreciated
My gradle files:
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = "1.4.21"
ext.nav_version = '2.3.0'
repositories {
google()
jcenter()
}
dependencies {
classpath "com.android.tools.build:gradle:4.1.1"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
And:
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'androidx.navigation.safeargs.kotlin'
}
android {
compileSdkVersion 30
buildToolsVersion "30.0.2"
defaultConfig {
applicationId "com.example.onemorepassword"
minSdkVersion 21
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures {
dataBinding true
}
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
//ViewModel
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
}
I have my 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">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.SwitchCompat
android:id="#+id/lowLetters_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="30dp"
android:checked="true"
android:fontFamily="#font/nunito_sans_bold"
android:text="#string/switchLowLetters"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/lengthSize_seekBar" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
My Fragment:
package com.example.onemorepassword
import android.annotation.SuppressLint
import android.content.ClipData
import android.content.ClipboardManager
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.Animation
import android.view.animation.AnimationUtils
import android.widget.Button
import android.widget.SeekBar
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import com.example.onemorepassword.databinding.OneMorePasswordFragmentBinding
class OneMorePasswordFragment : Fragment() {
private lateinit var binding: OneMorePasswordFragmentBinding
private lateinit var viewModel: OneMorePasswordViewModel
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding = DataBindingUtil.inflate<OneMorePasswordFragmentBinding>(inflater, R.layout.one_more_password_fragment, container, false)
viewModel = ViewModelProvider(this).get(OneMorePasswordViewModel::class.java)
//All fun that update UI from viewModel
savedGeneratedPass()
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
**val myLowLettersSwitch = binding.lowLettersSwitch
myLowLettersSwitch.setOnCheckedChangeListener { _, b ->
Toast.makeText(requireActivity(),b.toString(),Toast.LENGTH_SHORT).show(**)
}
......
More code...
....
**private fun switchLowLetters(): Boolean {
val myLowLettersSwitch= binding.lowLettersSwitch
return myLowLettersSwitch.isChecked
}**
....
More code...
And last my mainActivity
package com.example.onemorepassword
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import com.example.onemorepassword.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//setContentView(R.layout.activity_main)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
supportActionBar?.hide()
}
}
You need to add viewBinding true to buildFeatures in build.gradle (app) file in order to be able to use view binding.
Just replace:
buildFeatures {
dataBinding true
}
with:
buildFeatures {
dataBinding true
viewBinding true
}
Found out what was happening. Once you know it..!
I have to XML files for same Fragment, Portrait and Landscape.
Forgot to edit me Landscape XML file.... So in one I had <com.google.android.material.switchmaterial.SwitchMaterial> (or compat, does not matter)
and in other XML I only had
Maybe this will serve for someone to look into this before collapsing
I am developing football statics app but when I run the code I am getting following exception
cannot find symbol
import yodgorbekkomilov.edgar.footballapp.databinding.FootballItemBindingImpl;
^
symbol: class FootballItemBindingImpl
below XML class
<?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">
<data>
<variable
name="viewModel"
type="yodgorbekkomilov.edgar.footballapp.ui.FootballViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:mutableText="#{viewModel.clubName}"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:mutableText="#{viewModel.countryName}"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:mutableText="#{viewModel.clubValue}"/>
<ImageView
app:imageUrl="#{viewModel.image}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:id="#+id/clubImage"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:mutableText="#{viewModel.europeanTitle}"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
below my FootballAdapter.kt where I have used databinding
import android.os.Build
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.annotation.RequiresApi
import androidx.databinding.DataBindingUtil
import androidx.recyclerview.widget.RecyclerView
import yodgorbekkomilov.edgar.footballapp.FootballResponse
import yodgorbekkomilov.edgar.footballapp.R
import yodgorbekkomilov.edgar.footballapp.databinding.FootballItemBinding
import yodgorbekkomilov.edgar.footballapp.ui.FootballViewModel
class FootballAdapter :
RecyclerView.Adapter<FootballAdapter.ViewHolder>() {
private lateinit var footballList: List<FootballResponse>
fun updatePostList(footballList: List<FootballResponse>) {
this.footballList = footballList
notifyDataSetChanged()
}
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): FootballAdapter.ViewHolder {
val binding: FootballItemBinding = DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.football_item,
parent,
false
)
return ViewHolder(binding)
}
override fun getItemCount(): Int {
return footballList.size
}
#RequiresApi(Build.VERSION_CODES.O)
override fun onBindViewHolder(holder: FootballAdapter.ViewHolder, position: Int) {
holder.bind(footballList[position])
}
class ViewHolder(private val binding: FootballItemBinding) : RecyclerView.ViewHolder(binding.root) {
private val viewModel = FootballViewModel()
fun bind(model: FootballResponse) {
viewModel.bind(model)
binding.viewModel = viewModel
}
}
}
below FootballViewModel.kt
import androidx.lifecycle.MutableLiveData
import yodgorbekkomilov.edgar.footballapp.FootballResponse
import yodgorbekkomilov.edgar.footballapp.base.BaseViewModel
class FootballViewModel: BaseViewModel() {
private val clubName = MutableLiveData<String>()
private val countryName = MutableLiveData<String>()
private val clubValue = MutableLiveData<String>()
private val clubImage = MutableLiveData<String>()
private val europeanTitle = MutableLiveData<String>()
fun bind(football: FootballResponse){
clubName.value= football[0].name
countryName.value = football[1].country
clubValue.value = football[2].value.toString()
clubImage.value = football[3].image
europeanTitle.value = football[4].europeanTitles.toString()
}
fun getClubName():MutableLiveData<String>{
return clubName
}
fun getCountryName():MutableLiveData<String>{
return countryName
}
fun getClubValue():MutableLiveData<String>{
return clubValue
}
fun getImage():MutableLiveData<String> {
return clubImage
}
fun getEuropeanTitle():MutableLiveData<String> {
return europeanTitle
}
}
below my app.gradle
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
android {
compileSdkVersion 29
buildToolsVersion "30.0.0"
defaultConfig {
applicationId "yodgorbekkomilov.edgar.footballapp"
minSdkVersion 15
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
buildFeatures{
dataBinding = true
viewBinding = true
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.0'
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'com.google.android.material:material:1.0.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.navigation:navigation-fragment-ktx:2.1.0'
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
def room_version = "2.2.5"
def nav_version = "2.3.0"
//Navigation component
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
//RxJava
implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
implementation 'io.reactivex.rxjava3:rxjava:3.0.0'
implementation "com.squareup.retrofit2:adapter-rxjava2:2.6.1"
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
//Room
implementation "androidx.room:room-rxjava2:$room_version"
implementation 'com.google.android.material:material:1.1.0'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.recyclerview:recyclerview:1.1.0'
//Glide
implementation 'com.github.bumptech.glide:glide:4.11.0'
//Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.6.2'
implementation 'com.squareup.retrofit2:converter-gson:2.6.2'
implementation 'androidx.navigation:navigation-ui-ktx:2.1.0'
//Dagger2
implementation 'com.google.dagger:dagger:2.27'
kapt 'com.google.dagger:dagger-compiler:2.27'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
what I have tried
1.invalidate cache restart
2. I have checked xml carefully it did not solve problem
3. tried following After migration to androidX: Databinding problem (Android Studio 4) and ActivityMainBindingImpl cannot be found in this one as well it did not solve my problem
I want to know where I am making mistake
I am trying to use databinding with MVVM in my android app but it is giving me an error: "Unable to resolve reference BR" at runtime while i can see the class already exist in the project.
build.gradle:
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 29
buildToolsVersion "29.0.2"
defaultConfig {
applicationId "com.abc.xxx"
minSdkVersion 15
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
dataBinding {
enabled = true
}
}
dependencies {
def lifecycle_version = "2.2.0"
def arch_version = "2.1.0"
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.core:core-ktx:1.0.2'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
}
ViewModel class:
class SearchViewModel : BaseObservable() {
private var searchModel = SearchModel("")
fun setSearchText(text: String){
searchModel.text = text
// notifyPropertyChanged(B)
notifyPropertyChanged(BR._all)
}
#Bindable
fun getSearchText(): String{
return searchModel.text
}
fun onSearchClicked(){
if(searchModel.text.isNullOrEmpty())
setSearchText("Error!!!")
}
}
Activity class:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val bind : ActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
bind.viewModel = SearchViewModel()
bind.executePendingBindings()
}
}
xml:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:bind="http://schemas.android.com/tools">
<data>
<variable
name="viewModel"
type="com.abc.xxx.viewmodels.SearchViewModel" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<EditText
android:id="#+id/inImageSearch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="#string/search_hint"
android:maxLines="1"
android:text="#={viewModel.userText}" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="#{()-> viewModel.onSearchClicked()}"
android:text="#string/search"
/>
</LinearLayout>
</layout>
So what are the changes i should do to make it work? I have tried many SO threads already but none of them is working. Please help me out.
It appears you are binding to a property that doesn't exit. You have the userText in your xml which doesn't exist. Also you are binding to a model of the viewModel, so you need to make sure your SearchModel also implements BaseObservable.
Then make sure it is not private in your viewModel or that you write appropriate getters to retrieve it. The get and set Names MUST match the property name that is labeled with bindable.
Also just an architecture comment. I'm not sure why you would create a searchViewModel and a searchModel. Seems sort of redundant, but maybe you have reasons.
Example SEARCH MODEL below
class SearchModel : BaseObservable() {
#Bindable
private var searchText = ""
fun setSearchText(text: String){
searchModel.text = text
notifyPropertyChanged(BR.searchText)
}
fun getSearchText(): String{
return searchModel.text
}
}
Then your XML needs to be bound correctly to the models property
<EditText
android:id="#+id/inImageSearch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="#string/search_hint"
android:maxLines="1"
android:text="#={viewModel.searchModel.searchText}" />