Accessing viewbinding object in a non view class? - android

In my fragment I am using a bottom sheet to show some UI components
This is my fragment xml
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
>
<!-- other views -->
<include
android:id="#+id/feedbackIncluded"
layout="#layout/chat_feedback" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
I currently handle view actions or data of bottomsheet from my fragment.
I am planning to seperate the code for bottomsheet handling to another class
This is my bottom sheet 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"
app:behavior_hideable="false"
app:behavior_peekHeight="16dp"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
<data>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/parentSheetLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/transparent">
<!-- some views -->
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
This is the class which I have created where I want to do the handling
class ChatFeedbackBottomSheet(context: Context) {
var binding: ChatFeedbackBinding? = null
init {
binding = ChatFeedbackBinding.inflate(LayoutInflater.from(context), null, false)
println(binding?.option1.text)
}
fun openSheet() {
println("------>" + binding?.option1?.text)
}
}
I can't seem to figure how do I get the correct viewbinding object in this class?

You don't need to have another class for refactoring of code, you can just take out the function and keep it inside another file, You can pass view binding variables in the function parameters and execute your code there.

Related

Cannot access class 'android.widget.fragment'. Check your module classpath for missing or conflicting dependencies

I have fragment and inside it I have child fragment. I can't access from parent fragment using binding cos it gives compilation error. I did like this:
From parent fragment, I did:
val fragment = binding.calculatorSheetHome // calculatorSheetHome is fragment id
But compile error says,
Cannot access class 'android.widget.fragment'. Check your module classpath for missing or conflicting dependencies
Here is parent fragment layout code, and I am trying to access calculator_sheet_home
<variable
name="mLanguage"
type="com.hamidjonhamidov.calculator.model.room.LanguageM" />
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="#+id/coordinator_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutFullscreen="#{true}">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingTop="#dimen/grid_0_25"
app:paddingBottomSystemWindowInsets="#{true}"
app:paddingTopSystemWindowInsets="#{true}"
android:clipToPadding="false">
// ...
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<fragment
android:id="#+id/calculator_sheet_home"
android:name="com.hamidjonhamidov.calculator.ui.main.home.calculator_sheet_home.CalculatorSheetFragmentHome"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Fragment is not "View" so you cannot access it like traditional view. But, you can access it via fragment tag doing like this:
<fragment
android:id="#+id/calculator_sheet_home"
android:name="com.hamidjonhamidov.calculator.ui.main.home.calculator_sheet_home.CalculatorSheetFragmentHome"
android:tag="fragment_sheet_home" // add this line
android:layout_width="match_parent"
android:layout_height="match_parent" />
Now from parent fragment you can do this:
val childFragment = childFragmentManager.findFragmentByTag("fragment_sheet_home") as? CalculatorSheetFragmentHome ?: return
Now, you can use your fragment for whatever reason)
This solved it for me,note i was using Bottom Nav and View Binding
val childFragment = supportFragmentManager.findFragmentByTag("fragment_sheet_home") as? NavHostFragment

Passing LiveData as <layout> tag argument

I have quick question about my code. I'm writing an app using Android MVVM with LiveData. I want to create loading layout which is will be included in many views. Main goal is to have ability of passing live data representing if layout should be visible and what text info should be displayed with progress bar.
So far I created loading indicator layout, and definded two variables "indicatorVisibility" and "progressText". In attached code one of values is commented out. I created also BindingAdapters to set visibility and text on controls.
This is my layout with progress bar
<data>
<variable
name="indicatorVisibility"
type="android.arch.lifecycle.LiveData"/>
<!--<variable-->
<!--name="progressText"-->
<!--type="android.arch.lifecycle.LiveData"/>-->
</data>
<android.support.constraint.ConstraintLayout
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/layout_loading_background"
>
<ProgressBar
android:id="#+id/progressBar2"
style="?android:attr/progressBarStyle"
android:layout_width="#dimen/layout_loading_progress_bar_size"
android:layout_height="#dimen/layout_loading_progress_bar_size"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="#+id/textView7"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<!--android:text="#{progressText}"-->
<TextView
android:id="#+id/textView7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="TextView"
android:textColor="#color/layout_loading_text"
android:textSize="#dimen/layout_loading_text_size"
app:layout_constraintBottom_toBottomOf="#+id/progressBar2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="#+id/progressBar2"
app:layout_constraintTop_toTopOf="#+id/progressBar2" />
</android.support.constraint.ConstraintLayout>
This is how i include it in fragment layout
<include layout="#layout/layout_loading_info"
app:indicatorVisibility="#{viewModel.isBusy}"
/>
And those are my bind adapters:
#BindingAdapter("android:visibility")
fun getVisibility(view: View, liveData: LiveData<Boolean>){
liveData.observe(view.getLifecycleOwner(), Observer {
view.visibility = if(it == true) View.VISIBLE else View.INVISIBLE
})
}
#BindingAdapter("app:text")
fun getText(view: TextView, liveData : LiveData<Int>)
{
liveData.observe(view.getLifecycleOwner(), Observer {
it?.let{
view.text = view.context.resources.getString(it)
}
})
}
So far I tried passing simple types like Integer and it works. The problem lays in LiveData. Even when I don't use variables inside included layout I get error (error message tells nothing).
I saw similar stack task [here] : Applying databinding adapter to include tag but they passed the whole viewModel, which is not a flexible enough solution for me.
I think you use the wrong name-space; for data-binding that should be bind:
<include
layout="#layout/layout_loading_info"
bind:indicatorVisibility="#{viewModel.isBusy}"/>
The data-type is LiveData<Boolean>; therefore you'd need to import LiveData and Boolean, in order to use them in a variable definition. The data-binding should look about like this:
<data class=".databinding.LiveDataBinding">
<import type="android.arch.lifecycle.LiveData"/>
<import type="java.lang.Boolean"/>
<variable name="indicatorVisibility" type="LiveData<Boolean>"/>
</data>
bind:viewModel="#{viewModel}" might in general be better than binding single values.

Databinding and included layouts: Cannot find setter attribute for onClick

I'm trying to set an OnClickListener for an <include>d layout, but receive a data binding error at compile time stating that data binding "Cannot find the setter for attribute 'android:onClick' with parameter type android.view.View.OnClickListener".
Context here is that I'm using data binding to inflate the included layout, so that I can pass values into it from a viewModel that I've bound to the including layout.
I've tried various syntax for the data binding expression:
#{viewModel::onClickFunction}
#{viewModel.onClickFunction}
#{() -> viewModel.onClickFunction()}
#{(view) -> viewModel.onClickFunction()}
#{_ -> viewModel.onClickFunction()}
I've tried all of the above with onClickFunction as a function, and also as an OnClickListener object.
Other related questions on Stack Overflow seem to solve this issue by cleaning the project to regenerate the databinding files, but that hasn't worked for me.
Relevant code snippets below:
viewModel
class MyViewModel() {
val onClickFunctionAsAListener: OnClickListener = object:OnClickListener{
override fun onClick(v: View?) {
//Do something
}
}
fun onClickFunction() {
//Do something
}
}
Including layout
<layout>
<data>
<variable name="viewModel" type="full.package.name.MyViewModel"/>
</data>
<LinearLayout>
<include
layout="#layout/included_layout"
android:onClick="#{viewModel.onClickListener}"
app:customAttribute="#{`someText`}/>
</LinearLayout>
</layout>
Included layout
<layout>
<data>
<variable name="customAttribute" type="String"/>
</data>
<TextView
layout="#layout/included_layout"
android:text="#{customAttribute}"/>
</layout>
It seems that you can't actually assign an OnClick handler to an <include> tag directly. I managed to get it to work by adding another variable to IncludedLayouts data binding class, and then assigning the OnClickListener to IncudedLayouts root view in XML.
After the changes, my files looked like this:
viewModel
class MyViewModel() {
val onClickFunction: OnClickListener = object:OnClickListener{
override fun onClick(v: View?) {
//Do something
}
}
}
Including layout
<layout>
<data>
<variable name="viewModel" type="full.package.name.MyViewModel"/>
</data>
<LinearLayout>
<include
layout="#layout/included_layout"
app:onClick="#{viewModel.onClickListener}"
app:customAttribute="#{`someText`}/>
</LinearLayout>
</layout>
Included layout
<layout>
<data>
<variable name="customAttribute" type="String"/>
<variable name="onClick" type="android.view.View.OnClickListener"/>
</data>
<TextView
layout="#layout/included_layout"
android:text="#{customAttribute}"
android:onClick="#{onClick}"/>
</layout>
include tag does not support onClick method directly. While the selected answer is correct, instead of passing onClickLister to include layout (or having custom #BindingAdapter, which is also another solution), I would just wrap my include inside a ViewGroup and onClick on ViewGroup.
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="#{()-> viewModel.yourFunction()}">
<include layout="#layout/custom_layout"/>
It's a workaround, but works as charm.

Databind LiveData to CustomView

I have a ViewModel with a List auf MutableLiveData<Data> in my Fragment Layout I set the data variable of my CustomView with one of the data elements from the List.
This works fine when it first loads but it doesn't update when I change a value in my data object.
Not really sure how to do this, until now I just used two-way data binding with EditText and MutableLiveData for example.
CustomView Layout:
<data>
<variable
name="data"
type="androidx.lifecycle.LiveData<Data>"/>
</data>
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:cardBackgroundColor="#{data.color}"
app:cardCornerRadius="16dp">
Class:
var data: MutableLiveData<Data>? = null
set(value) {
binding.data = value
}
Fragment Layout:
<data>
<variable
name="viewModel"
type=".ViewModel" />
</data>
<CustomView
.
.
.
app:data="#{viewModel.data[1]}" />
The reason for the update only happening the first time the screen is loaded is that the XML is used to inflate the View and then the initial item is used and set to the CustomView.
Then when the item in the list is updated, it does not trigger an update in the CustomView.
What you might be looking for is #BindingAdapter
#BindingAdapter("enableButton")
internal fun Button.enableButton(enabled: Boolean?) = enabled?.let { isEnabled = it } ?: also { isEnabled = false }
And then using it in the following way:
<Button
android:id="#+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button Text"
app:enableButton="#{viewModel.observeStatus()}" /> // <- Observes Boolean
A good walk-through might be at the following link: BindingAdapter
Note: The example is only for a Boolean observation, but it can simply be changed to match whatever object is observed.

How to use kotlin.Int in databinding?

I wrote a layout xml like below.
But the Kotlin compiler says Cannot resolve symbol 'Int'
main_activity.xml
<?xml version="1.0" encoding="utf-8"?>
<layout ...>
<data>
<import type="androidx.databinding.ObservableArrayMap" />
<variable
name="myList"
type="ObservableArrayMap<Int,String>" />
</data>
<!-- ...... -->
</layout>
Is it possible to use kotlin builtins in android databinding xml?
Use java Integer instead of kotlin Int.
You can not use characters <,> etc in XML. So use HTML entities.
like
ObservableArrayMap<Integer,String>
You can add data binding simple way.
Configure your app to use the data binding:
Open the app/build.gradle, Then you have to add these line of code inside the android tags in gradle and sync project
dataBinding {
enabled = true
}
Layout and Binding Expression in Data Binding:
Open the layout (XML) file activity_main and replace the root with layout tag and place all tags inside to layout tags.
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<RelativeLayout>
<layout>
Replace the traditional setContentView() to DataBindingUtil.setContentView() in Activity:
public class MainActivity extends AppCompatActivity {
ActivityMainBinding binding;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
}
}
Let’s us see how to data bind with Views and Widget using data binding
binding.tvName.setText("Monika Sharma");
binding.tvAddress.setText("251 mansarovar Jaipur | India ");
binding.tvFollowers.setText("240K");
binding.tvfollowing.setText("324K");

Categories

Resources