I see this error while updating a textview once the image has been successfully rendered by Glide.
Fatal Exception: java.lang.IllegalStateException: Required DataBindingComponent is null in class CustomBinding. A BindingAdapter in CustomViewModel is not static and requires an object to use, retrieved from the DataBindingComponent. If you don't use an inflation method taking a DataBindingComponent, use DataBindingUtil.setDefaultComponent or make all BindingAdapter methods static.
@BindingAdapter(value = { "android:src", "placeHolder" }, requireAll = false)
public void setUrl(ImageView imageView, String url, Drawable placeHolder) {
Glide.with(imageView.getContext())
.load(url)
.placeholder(placeHolder)
.centerCrop()
.listener(new Listener<String, Drawable>() {
@Override
public boolean onException() {
viewmodel.setTextVisible(true);// ERROR!
return false;
}
@Override public boolean onResourceReady() {
viewmodel.setTextVisible(false); // ERROR!
return false;
}
})
.into(imageView);
}
public void setTextVisible(boolean visibility) {
textVisibility = visibility;
notifyPropertyChanged(BR.textVisibility);
}
@Bindable
public boolean getTextVisible() {
return textVisibility;
}
This is how i initialise the viewmodel and bind the data inside the fragment:
CustomBinding binding =
DataBindingUtil.inflate(inflater, R.layout.custom, container,
false);
CustomViewModel viewModel = new CustomViewModel(data, context);
binding.setHandlers(new CustomHandlers(context));
binding.setData(viewModel);
I cant find a way to actually implement this within a viewmodel. Thanks in advance for the help.
Was facing the same issue, got resolved by adding @JvmStatic
annotation to the method and it works.
it should be in the form
@BindingAdapter("app:someAttribute")
@JvmStatic
example
class TestBindingHelper {
companion object{
@BindingAdapter("app:serviceOptions")
@JvmStatic
fun setServiceOptions(recyclerView: RecyclerView, listOfData: List<String>?) {
//do something funny
}
}
}
Your error tells you that the adapter you created is not static, but it has to be.
I am not quite sure that it is a good attempt to change the view model directly from the binding adapter.
Consider each binding adapter as a stand-alone static method, which is located in a separate class (say BindingUtills) and it can do staff with the parameters you pass to it.
In your example, It's not clear how do you pass the URL itself (I guess not through the android:src).
In this example adapter deals with the ImageView, image url and error drawable:
/**
* Loads image from url and sets it into the Image View
* with error drawable (set into imageview if failed to load the image)
*
* @param imageView {@link ImageView}
* @param url image url
* @param errorDrawable Drawable to set in case of load error.
*/
@BindingAdapter({"bind:imageUrlRound", "bind:imageError"})
public static void loadRoundImage(ImageView imageView, String url, Drawable errorDrawable) {
Glide.with(imageView.getContext())
.load(url)
.asBitmap()
.error(errorDrawable)
.centerCrop()
.animate(R.anim.fade_in)
.into(new BitmapImageViewTarget(imageView) {
@Override
protected void setResource(Bitmap resource) {
RoundedBitmapDrawable circularBitmapDrawable =
RoundedBitmapDrawableFactory.create(imageView.getContext().getResources(), resource);
circularBitmapDrawable.setCircular(true);
imageView.setImageDrawable(circularBitmapDrawable);
}
});
}
What in layout looks like:
<ImageView
...
app:imageUrlRound="@{yourViewModel.image}"
app:imageError="@{@drawable/ic_error}" />
I am not sure that this is a good practice, but you may try to add the view model as another binding attribute as well as a parameter of the adapter and change it's param in the glide's callback.
P.S. Also simplify your view model (create a mapper to make in handy) to contain only the parameters you want to show in the layout (i.e. Strings ints etc) IMHO it's strange to pass context there...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With