How to access a button in included layout using data binding - android

I am using databinding in my application but button is not working. where am i making mistake in this code. i tried many solution but no luck. but if make button in activity_login.xml then button click works. i think i am not able to pass the view model to the included view.
Here is my code
activity_login.xml
<?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:bind="http://schemas.android.com/apk/res-auto">
<data>
<variable
name ="loginViewModel"
type="com.innowi.checoutrestaurantdashboard.view.main.MainViewModel"/>
</data>
<android.support.constraint.ConstraintLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/login_constraint_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/login_background_burgerstack"
android:isScrollContainer="false"
android:paddingEnd="32dp"
android:paddingStart="32dp"
tools:context=".view.main.LoginActivity">
<include
android:id="#+id/login_layout"
layout="#layout/layout_login"
android:layout_width="0dp"
android:layout_height="0dp"
bind:loginViewModel="#{loginViewModel}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</android.support.constraint.ConstraintLayout>
</layout>
layout_login.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 ="loginViewModel"
type="com.innowi.checoutrestaurantdashboard.view.main.MainViewModel"/>
</data>
<android.support.constraint.ConstraintLayout
style="#style/Login"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:isScrollContainer="false"
android:padding="32dp">
<Button
android:id="#+id/login_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:background="#drawable/button_login_drawable"
android:enabled="false"
android:gravity="center"
android:paddingBottom="16dp"
android:paddingTop="16dp"
android:text="#string/button_sign_in"
android:textColor="#color/colorWhite"
android:textSize="24sp"
android:onClick="#{loginViewModel::onLoginButtonClick}"
app:layout_constraintLeft_toLeftOf="#+id/login_username"
app:layout_constraintRight_toRightOf="#+id/login_username"
app:layout_constraintTop_toBottomOf="#+id/login_password" />
.
.
.
.
</android.support.constraint.ConstraintLayout>
</layout>
MainViewModel.kt
class MainViewModel #Inject constructor(
private val loginRepository: LoginRepository
) : BaseViewModel() {
fun onLoginButtonClick(view : View){
performLogin()
}
.
.
.
}
LoginActivity.kt
class LoginActivity : AppBaseActivity() {
private lateinit var loginViewBinding: ActivityLoginBinding
private lateinit var viewModel: MainViewModel
private val TAG = LoginActivity::class.simpleName
override fun initViewModel(viewModelProvider: ViewModelProvider): BaseViewModel? {
viewModel = viewModelProvider.get(MainViewModel::class.java)
return viewModel
}
override fun render(state: ViewState) {
when (state) {
is LoadingState -> {
// loading
val loading = state.loading
Log.d(TAG,"Loading State")
}
is DefaultState -> {
// render Data
val data = state.data
Log.d(TAG,"Data State")
}
is ErrorState -> {
// show error
val error = state.error
Log.d(TAG,"Loading State")
}
}
}
override fun setContentView() {
loginViewBinding = DataBindingUtil.setContentView(this, R.layout.activity_login)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
loginViewBinding.loginViewModel = viewModel
loginViewBinding.executePendingBindings()
}
}

Related

Click Handling on Views in Clean Architecture

I have to implement click listener using binding and ViewModel as per clean architecture.
I have two button to select language like English and Chinese.
LanguageActivity.kt
#AndroidEntryPoint
class LanguageActivity : PBActivity(R.layout.activity_language) {
private val mViewModel:LanguageViewModel by viewModels()
private val mBinding:ActivityLanguageBinding by viewbind()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mBinding.apply {
lifecycleOwner = this#LanguageActivity
viewModel = mViewModel
}
collectFlow(mViewModel.uiState){
Toast.makeText(this, it.name, Toast.LENGTH_SHORT).show()
when(it){
LanguageSelected.ENGLISH -> {
}
LanguageSelected.CHINESE -> {
}
LanguageSelected.NONE -> {
}
}
}
}
}
enum class LanguageSelected{
ENGLISH,CHINESE,NONE
}
LanguageViewModel.kt
#HiltViewModel
class LanguageViewModel #Inject constructor(
private val pbPrefs: PBPrefs
): ViewModel(){
// Backing property to avoid state updates from other classes
private val _uiState = MutableStateFlow(LanguageSelected.NONE)
// The UI collects from this StateFlow to get its state updates
val uiState: StateFlow<LanguageSelected> = _uiState
fun englishSelected()= viewModelScope.launch {
_uiState.value = LanguageSelected.ENGLISH
pbPrefs.setLanguageSelect()
}
fun urduSelected() = viewModelScope.launch {
_uiState.value = LanguageSelected.URDU
pbPrefs.setLanguageSelect()
}
}
activity_language.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">
<data>
<variable
name="viewModel"
type="uk.co.planetbeyond.telenorbluecollar.ui.language.LanguageViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.language.LanguageActivity">
<androidx.constraintlayout.widget.Guideline
android:id="#+id/center_vertical_gl"
android:layout_width="match_parent"
android:layout_height="1dp"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />
<Button
android:id="#+id/englishTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="English "
android:onClick="#{() -> viewModel.englishSelected()}"
android:layout_marginEnd="8dp"
app:layout_constraintEnd_toStartOf="#id/center_vertical_gl"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="#+id/urduTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Urdu"
android:onClick="#{() -> viewModel.urduSelected()}"
android:layout_marginStart="8dp"
app:layout_constraintStart_toEndOf="#id/center_vertical_gl"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
I have to select language on click of a button. What's the best approach as per clean architecture to implement
I have created an interface LanguageSelected but for that I have to created multiple methods in viewModel, As languages increase I have to create more methods in viewmodel.
How could I make it short or I mean extensible?

Android binding adapter doesn't change after property changed

I write a code for a simple dice roll app but there is a problem in updating the image of an image view that bound with a binding adapter
I can't extend BaseObservable class because I already extended the ViewModel class. I need that for keeping data after the status changed. Also, I can't observe the live data inside my binding adapter because the function is static
do you know any other way?
This is my xml code
<?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">
<data>
<variable
name="viewModel"
type="com.example.diceroller.MainActivityViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ImageView
android:id="#+id/imgDice"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:imageRes="#{viewModel.drawableResource}" />
<Button
android:id="#+id/btnRoll"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp"
android:text="Roll the dice"
android:onClick="#{() -> viewModel.onRollButtonClicked()}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
and this is my kotlin code
class MainActivityViewModel : ViewModel() , Observable {
#Bindable
var drawableResource : MutableLiveData<Int> = MutableLiveData<Int>()
init {
drawableResource.value = R.drawable.dice_1
}
fun onRollButtonClicked(){
drawableResource.value = when(Random.nextInt(1,7)){
1 -> R.drawable.dice_1
2 -> R.drawable.dice_2
3 -> R.drawable.dice_3
4 -> R.drawable.dice_4
5 -> R.drawable.dice_5
else -> R.drawable.dice_6
}
}
override fun addOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback?) {
}
override fun removeOnPropertyChangedCallback(callback:
Observable.OnPropertyChangedCallback?) {
}
}
#BindingAdapter("android:imageRes")
fun loadImage(view : View , imageRes : Int){
(view as ImageView).setImageResource(imageRes)
}

How to bind recycler view with activity using data binding?

I am struck at the data binding part. I have a simple app where I am using mvvm pattern to show a list. But the problem is how to bind recycler view with the id recycler_view with activity
// data class
data class MovieItem(
val imageUrl: String,
val name: String
)
// repository
class MovieRepository(private val api: Api) {
suspend fun getAllMovies() = api.getMovieList()
}
// view Model
class MovieViewModel(private val repository: MovieRepository):ViewModel() {
var listMovie = MutableLiveData<List<MovieItem>>()
var job: Job? = null
fun makeApiCall(){
job= CoroutineScope(Dispatchers.IO).launch {
var response = repository.getAllMovies()
withContext(Dispatchers.Main){
if (response != null){
listMovie.postValue(response.body())
}
}
}
}
}
// adapter
class MovieItemAdapter:RecyclerView.Adapter<MovieItemAdapter.MovieViewHolder>() {
var listMovie = mutableListOf<MovieItem>()
private val context:Context ? = null
fun setMovieItem(data : List<MovieItem>){
this.listMovie = data.toMutableList()
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MovieViewHolder {
val binding = DataBindingUtil.inflate<MovieItemBinding>(LayoutInflater.from(parent.context),
R.layout.movie_item,parent,
false)
return MovieViewHolder(binding)
}
override fun onBindViewHolder(holder: MovieViewHolder, position: Int) =
override fun getItemCount(): Int {
return listMovie.size
}
class MovieViewHolder(val binding: MovieItemBinding):RecyclerView.ViewHolder(binding.root){
fun bind(item : MovieItem){
binding.model = item
}
}
}
// activity
class MainActivity : AppCompatActivity() {
lateinit var movieadapter: MovieItemAdapter
lateinit var api: Api
lateinit var repository: MovieRepository
lateinit var movieViewModel: MovieViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var recyclerView = findViewById<RecyclerView>(R.id.recycler_view)
recyclerView.layoutManager = LinearLayoutManager(this)
movieadapter = MovieItemAdapter()
recyclerView.adapter = movieadapter
api = Api.getInstance()
repository = MovieRepository(api)
movieViewModel = ViewModelProvider(this, MovieFactory(repository))
.get(MovieViewModel::class.java)
movieViewModel.makeApiCall()
movieViewModel.listMovie.observe(this, Observer {
movieadapter.setMovieItem(it)
})
}
}
// adapter layout
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="model"
type="com.example.databindingpractice.MovieItem" />
</data>
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:padding="3dp"
android:orientation="horizontal">
<ImageView
android:layout_width="70dp"
android:layout_height="70dp"
android:id="#+id/img_view" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#color/black"
android:textSize="18sp"
android:text="#{model.name}"
android:layout_marginLeft="5dp"
android:id="#+id/tv_view"
android:layout_gravity="center" />
</LinearLayout>
</androidx.cardview.widget.CardView>
<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="activity"
type="com.example.databindingpractice.MainActivity" />
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/recycler_view" />
</LinearLayout>
In onCreate method of Activity, replace
setContentView(R.layout.activity_main)
With
val binding : MainActivityBinding =
DataBindingUtil.setContentView(this, R.layout.activity_main)
Then you can use
binding.recyclerView.layoutManager = LinearLayoutManager(this)
movieadapter = MovieItemAdapter()
binding.recyclerView.adapter = movieadapter
Also remove this from activity_main.xml
<data>
<variable
name="activity"
type="com.example.databindingpractice.MainActivity" />
</data>
Use this for reference
Update:
Binding adapter can be defined as follows:
#BindingAdapter("moviesList")
fun bindRecyclerView(recyclerView: RecyclerView, list: List<MovieItem>?){
val adapter: MoviesAdapter = recyclerView.adapter as MoviesAdapter
adapter.submitList(list)
}
In activity_main.xml
<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="viewModel"
type="com.example.databindingpractice.MovieViewModel" />
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/recycler_view"
app:movieList="#{viewModel.listMovie}"/>
</LinearLayout>

How to bind MutableLiveData<Boolean> to checkBox

The problem is that when I click the button the Toast message always says checked is true, even if I uncheck the checkbox
class MainActivity : AppCompatActivity() {
private lateinit var binding: MainActivityLayoutBinding
var checked = MutableLiveData<Boolean>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
checked.value = true
binding = DataBindingUtil.setContentView(this, R.layout.main_activity_layout)
binding.checked = checked.value
binding.lifecycleOwner = this
binding.showValue.setOnClickListener{
Toast.makeText(this, checked.value.toString(), Toast.LENGTH_SHORT).show()
}
}
}
Xml code
<?xml version="1.0" encoding="utf-8"?>
<layout>
<data>
<variable
name="checked"
type="Boolean" />
</data>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Check"
android:id="#+id/checkBox"
android:checked="#={checked}"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Salva"
android:id="#+id/showValue"/>
</LinearLayout>
</layout>
It is because you set checked.value = true
Try to remove this line
remove this line
binding.checked = checked.value
and observe to live-data change , then update binding view
checked .observe(this , Observer {
binding.checked = it
})

Button onCLick does not work in MVVM Android

I have a MVVM setup for my project and I have a simple layout with an EditText and a button and when I click the button I want to show Text which is in the EditText.
For the button when i add. this code android:onClick="#={() -> addProductViewModel.addProduct()}" it gives me an error A failure occurred while executing org.jetbrains.kotlin.gradle.internal.KaptExecution
and when I remove that like app builds okay.
Error is not so clear and I am not sure how to fix it.
here is my code
add_product.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android" >
<data class=".AddProductBinding">
<variable
name="addProductViewModel"
type="com.rao.iremind.AddProductViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:id="#+id/etProductName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:text="#={addProductViewModel.inputProductName}"
android:hint="Product name"
android:inputType="textPersonName"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="#+id/btn_add_product"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="160dp"
android:text="Add product"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/etProductName" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
AddProductViewModel
class AddProductViewModel (
private val repository: ProductRepository,
private val context: Context
): ViewModel(), Observable {
#Bindable
val inputProductName = MutableLiveData<String>()
fun addProduct() {
//inputProductName.value
Toast.makeText(context, " add product ", Toast.LENGTH_LONG).show()
}
override fun removeOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback?) {
}
override fun addOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback?) {
}
}
AddProductViewModelFactory
class AddProductViewModelFactory (
private val repository: ProductRepository,
private val context: Context
) : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(AddProductViewModel::class.java)) {
return AddProductViewModel(repository, context) as T
}
throw IllegalArgumentException("Unknown View Model class")
}
}
Your help and suggestions are much appreciated
Thanks
R

Categories

Resources