Loading an image through data binding is easy. I am using Glide in my project. I have to set placeholder image which will change as per some selection by user. Can we use some expression which accepts imageurl and placeHolder image reference.
<androidx.appcompat.widget.AppCompatImageView
android:id="#+id/vehicle_1_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/margin_twenty"
app:layout_constraintEnd_toEndOf="#id/centerGuideline"
app:layout_constraintStart_toStartOf="#id/centerGuideline"
app:layout_constraintTop_toBottomOf="#id/txt_enter_vehicle_name"
app:loadImage="#{viewModel.imgUrl}" />
#BindingAdapter({"loadImage"})
public static void loadUrlImage(ImageView view, String url, int placeHolderImage){
ImageLoaderUtil.getInstance().loadImageWithCache(view, url, placeHolderImage);
}
public void loadImageWithCache(ImageView imageView, String url, int placeholderImage) {
Glide.with(imageView.getContext())
.load(url)
.apply(getDefaultGlideOptions())
.diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
.placeholder(placeholderImage)
.into(imageView);
}
Found this nice article: https://ayusch.com/databinding-with-glide-android/
We can also accept multiple arguments in our bindingadapter. For example, one may need to load an error image, or a placeholder while our image loads.
So I think listeners is the answer. Posting also the code in case the link is dead.
companion object {
#JvmStatic
#BindingAdapter(value = ["profileImage", "error"], requireAll = false)
fun loadImage(view: ImageView, profileImage: String, error: Int) {
Glide.with(view.context)
.load(profileImage)
.listener(object : RequestListener<Drawable> {
override fun onLoadFailed(
e: GlideException?,
model: Any?,
target: Target<Drawable>?,
isFirstResource: Boolean
): Boolean {
view.setImageResource(error)
return true
}
override fun onResourceReady(
resource: Drawable?,
model: Any?,
target: Target<Drawable>?,
dataSource: DataSource?,
isFirstResource: Boolean
): Boolean {
view.setImageDrawable(resource)
return true
}
})
.into(view)
}
}
and in your layout:
app:error="#{user.errorImage}"
You can add multiple parameter in BindingAdapter just like this.
#BindingAdapter("url","placeHolderImage")
public static void loadUrlImage(ImageView view, String url, int placeHolderImage)
{
ImageLoaderUtil.getInstance().loadImageWithCache(view, url, placeHolderImage);
}
And you have to add field in Imageview xml just like this.
<androidx.appcompat.widget.AppCompatImageView
android:id="#+id/vehicle_1_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/margin_twenty"
app:layout_constraintEnd_toEndOf="#id/centerGuideline"
app:layout_constraintStart_toStartOf="#id/centerGuideline"
app:layout_constraintTop_toBottomOf="#id/txt_enter_vehicle_name"
app:url="#{viewModel.imgUrl}"
app:placeHolderImage="#{viewModel.}"
/>
You have pass two thing xml Url and Placeholder.
Related
I'm trying to solve a problem with getting the dominant color in an image, but I can't convert it to a bitmap, because I get null all the time. What could the problem be?
View itself in XML:
<androidx.appcompat.widget.AppCompatImageView
android:id="#+id/ivIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintCircleRadius="8dp"
app:srcCompat="#drawable/icon_default"
tools:srcCompat="#drawable/icon_default" />
Getting the image goes through Glide by loading the image by url (fun loadInto (...)) and after that I try to get the drawable:
viewLifecycleOwner.lifecycleScope.launchWhenStarted {
viewOutput.iconFlow.collect { photoUrl ->
if (url != null) {
loadInto(url, binding.Icon)
getDominantColor(binding.ivIcon) <- null here
}
}
}
Update:
private fun getDominantColor(image: AppCompatImageView) {
val bitmap: Bitmap = (image.drawable as BitmapDrawable).bitmap
Palette.from(bitmap).generate { palette ->
val dom: Int = palette!!.getDominantColor(0x000000)
setGradientColor(dom)
}
}
fun loadInto(
url: String,
imageView: ImageView,
placeHolder: Int = 0,
errorHolder: Int = 0,
) {
Glide
.with(imageView)
.load(url)
.run { if (placeHolder != 0) placeholder(placeHolder) else this }
.run { if (errorHolder != 0) error(errorHolder) else this }
.into(imageView)
}
As mentioned by #Lino, Glide will make an asynchronous call to get the image. This will take some time and it may return null if you get the Bitmap from the ImageView right after it.
Your attempt to create a listener callback is correct. And if it is properly implemented, onResourceReady() method should be called once ready. You may refer the sample below:
Glide
.with(this#MainActivity)
.load(url)
.run { this }
.run { this }
.listener(object : RequestListener<Drawable> {
override fun onLoadFailed(
e: GlideException?,
model: Any?,
target: Target<Drawable>?,
isFirstResource: Boolean
): Boolean {
return false
}
override fun onResourceReady(
resource: Drawable?,
model: Any?,
target: Target<Drawable>?,
dataSource: DataSource?,
isFirstResource: Boolean
): Boolean {
// Process with your bitmap in callback here
Palette.from((resource!! as BitmapDrawable).bitmap).generate { palette ->
val dom: Int = palette!!.getDominantColor(0x000000)
setGradientColor(dom)
}
return false
}
})
.into(ivIcon)
I need to download an image and keep it as variable to put it into a notification:
.setLargeIcon(bitmap)
This is the code, I hope, it's clear by the comments what I try:
var bitmap = BitmapFactory.decodeResource(this#MainActivity.resources, R.drawable.notif_smiley) // create placeholder bitmap
val requestOptions = RequestOptions()
.skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.NONE)
bitmap = Glide.with(this#MainActivity)
.asBitmap()
.load(imgurl)
.listener(object : RequestListener<Bitmap> {
override fun onLoadFailed(
e: GlideException?,
model: Any?,
target: Target<Drawable>?,
isFirstResource: Boolean
): Boolean {
// just dont do anything, keep the placeholder bitmap
return false
}
})
.apply(requestOptions)
.submit()
.get()
binding.contentMain.testingGlide.setImageBitmap(bitmap) // this is just for easy testing
I get all kind of error, I tried this based on an answer but there the bitmap goes directly into a view, please help :D
Inside your load failed method you're not setting any image so try changing that also try adding the onerror() method below code implementation should fix it
try{
lateinit var bitmap:Bitmap
bitmap = Glide.with(context)
.asBitmap()
.placeholder(R.mipmap.avengerswallp)
.load(imgUri)
.error(getnothumb())
.listener(object : RequestListener<Bitmap>{
override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<Bitmap>?, isFirstResource: Boolean): Boolean {
bitmap = myPlaceHolderBitmap
return true
}
override fun onResourceReady(resource: Bitmap?, model: Any?, target: Target<Bitmap>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
return false
}
}).submit().get()
}
catch(ex:Exception)
{
}
I'm using the following code:
Glide
.with(activity)
.load(hdImage)
.placeholder(R.drawable.loading)
.transition(DrawableTransitionOptions.withCrossFade(250))
.error(R.drawable.error)
.into(imageView);
HdImage is an URL string and could be invalid or empty, so I've set an error drawable.
But before showing it, i would like to try to load a second URL called, for example, sdImage.
Is there a way to achieve it?
Kishan put me on the right path to find the solution. To try a second image if the first fails:
Glide
.with(activity).load(hdImage)
.placeholder(R.drawable.loading)
.transition(DrawableTransitionOptions.withCrossFade(250))
.error(
Glide
.with(activity)
.load(sdImage)
.placeholder(R.drawable.loading)
.transition(DrawableTransitionOptions.withCrossFade(250))
.error(R.drawable.error)
)
.into(imageView);
You can try the below code. This is in Kotlin. In onLoadFailed, you can try for sdImage URL.
Glide.with(activity).load(hdImage)
.placeholder(R.drawable.loading)
.transition(DrawableTransitionOptions.withCrossFade(250))
.error(R.drawable.error)
.listener(object : RequestListener<Drawable> {
override fun onResourceReady(resource: Drawable?, model: Any?, target: Target<Drawable>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
return false
}
override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<Drawable>?, isFirstResource: Boolean): Boolean {
return false
}
})
.into(imageView)
I use glide version 4.7.1
I wanna use default image when onLoadFailed.
this is my code
Glide.with(context).load(imageUrl).listener(new RequestListener<Drawable>() {
#Override
public boolean onLoadFailed(#Nullable GlideException e, Object model, com.bumptech.glide.request.target.Target<Drawable> target, boolean isFirstResource) {
// fail
// How can i use default image in imgvAssetPicture?
}
#Override
public boolean onResourceReady(Drawable resource, Object model, com.bumptech.glide.request.target.Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
// success
imgvLoadingProgressbar.setVisibility(View.INVISIBLE);
imgvAssetPicture.setVisibility(View.VISIBLE);
return false;
}
}).into(imgvAssetPicture);
Glide.with(passContext)
.applyDefaultRequestOptions(new RequestOptions()
.placeholder(R.drawable.ic_user_default)
.error(R.drawable.ic_user_default))
.load(url)
.into(image);
With version 4.7.1 (you are using), you can easily set these options.
placeHolder which shows when there is not image.
error when some URL fails to load.
Bonus
Are you using some ProgressBar with setting visibility, that's very old way to do.
See CircularProgressDrawable, which is very easy to use. Just pass this CircularProgressDrawable in your placeHolder.
Just Copy and Past these line of code wherever you want to use
Easy and simple
val circularProgressDrawable = CircularProgressDrawable(mContext)
circularProgressDrawable.strokeWidth = 5f
circularProgressDrawable.centerRadius = 30f
circularProgressDrawable.start()
Glide.with(mContext)
.load(specificCategoryObject.imgUrl)
.placeholder(R.drawable.circularProgressDrawable)
.transition(DrawableTransitionOptions.withCrossFade())
.error(R.drawable.img_place_holder)
.listener(object : RequestListener<Drawable> {
override fun onLoadFailed(
e: GlideException?,
model: Any?,
target: Target<Drawable>?,
isFirstResource: Boolean
): Boolean {
circularProgressDrawable.alpha = 0
return false
}
override fun onResourceReady(
resource: Drawable?,
model: Any?,
target: Target<Drawable>?,
dataSource: DataSource?,
isFirstResource: Boolean,
): Boolean {
circularProgressDrawable.alpha = 0
return false
}
})
.into(holder.ivProductImage)
private SimpleTarget target = new SimpleTarget<Bitmap>() {
#Override
public void onResourceReady(Bitmap bitmap, GlideAnimation glideAnimation) {
// do something with the bitmap
// for demonstration purposes, let's just set it to an ImageView
imageView1.setImageBitmap( bitmap );
}
};
private void loadImageSimpleTarget() {
Glide.with(context)
.load(uri)
.override(600, 600)
.fitCenter()
.into(target);
}
I tried to convert it into Kotlin like as follow.
val finish_target = object : SimpleTarget<Bitmap>() {
override fun onResourceReady(bitmap: Bitmap?, glideAnimation: GlideAnimation<in Bitmap>?) {
preview_image.setImageBitmap(bitmap)
}
}
Glide.with(context)
.load(uri)
.override(600, 600)
.fitCenter()
.into(finish_target)
But compilation error shows that
public open fun <Y : Target<GlideDrawable!>!> into(target: (???..???)): (???..???) defined in com.bumptech.glide.DrawableRequestBuilder
public open fun into(view: ImageView!): Target<GlideDrawable!>! defined in com.bumptech.glide.DrawableRequestBuilder
Please kindly help me how to solve this problem.
Glide.with(context)
.load(url)
.listener(object : RequestListener<Drawable> {
override fun onLoadFailed(p0: GlideException?, p1: Any?, p2: Target<Drawable>?, p3: Boolean): Boolean {
Log.e(TAG, "onLoadFailed")
//do something if error loading
return false
}
override fun onResourceReady(p0: Drawable?, p1: Any?, p2: Target<Drawable>?, p3: DataSource?, p4: Boolean): Boolean {
Log.d(TAG, "OnResourceReady")
//do something when picture already loaded
return false
}
})
.into(imgView)
With Glide you can add Listener to your chain, which monitor state of your image loading. You have to override two methods, in onResourceReady method you have callback that your image is already loaded and you can do something , for example hide loader or let finish animation from another view.
In onLoadFailed you get information about some error while loading and also you can react somehow. This way you can avoid those errors.
Android Studio 3.5 - Kotlin 1.3.41 - Glide 4.9.0
add this dependency to your build.gradle under dependencies:
implementation 'com.github.bumptech.glide:glide:4.9.0'
Go to the top of your class and add these imports (pay attention expecially to target class which is different from the kotlin target class annotation):
import com.bumptech.glide.Glide
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.load.resource.gif.GifDrawable
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.target.Target
import android.graphics.drawable.Drawable
import android.support.graphics.drawable.Animatable2Compat
I've put some extra parameter as override(600, 600), if you don't need remove it..
// Start animation
Glide
.with(this)
.load(R.drawable.tossina_pose1)
.centerCrop()
.override(600, 600)
.placeholder(R.drawable.tossina_idle_0)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.listener(object : RequestListener<Drawable> {
override fun onLoadFailed(p0: GlideException?, p1: Any?, p2: Target<Drawable>?, p3: Boolean): Boolean {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun onResourceReady(p0: Drawable?, p1: Any?, p2: Target<Drawable>?, p3: DataSource?, p4: Boolean): Boolean {
(p0 as GifDrawable).setLoopCount(1)
p0.registerAnimationCallback(object : Animatable2Compat.AnimationCallback() {
override fun onAnimationEnd(drawable: Drawable) {
println("animation ends")
}
})
return false
}
})
.into(img)
Some indications:
R.drawable.tossina_pose1 is my GIF, you can put also gif local image like this example. The final line .into(img) have img that is my imageView
The problem is that in the Java code, you used the type SimpleTarget as the type of target. This is a raw type (missing generic parameters), and is one of the big legacy problems in Java generics. Kotlin doesn't allow raw types, and this is why you got problems while converting.
To fix this, you should do the following in Java:
private SimpleTarget<Bitmap> target = new SimpleTarget<Bitmap>() { ... }
Which will force you to add asBitmap() to your Glide call:
Glide.with(context)
.load(uri)
.asBitmap()
.override(600, 600)
.fitCenter()
.into(target);
Now that your code is using generics safely, it can be translated to Kotlin without a problem:
Glide.with(context)
.load(uri)
.asBitmap()
.override(600, 600)
.fitCenter()
.into<SimpleTarget<Bitmap>>(target)
For those who are using Glide 3.8:
Glide.with(this)
.load(imgUrl)
.listener(object : RequestListener<String, GlideDrawable> {
override fun onException(e: Exception?, model: String?, target: Target<GlideDrawable>?, isFirstResource: Boolean): Boolean {
return false
}
override fun onResourceReady(resource: GlideDrawable?, model: String?, target: Target<GlideDrawable>?, isFromMemoryCache: Boolean, isFirstResource: Boolean): Boolean {
return false
}
})
.into(image)
If you only want to set the Bitmap into the ImageView using Glide then you can try out for a Extension Function in Kotlin with which you will only need to pass the parameters like uri/url or size.
For Example:
class KotlinActivity : AppCompatActivity() {
var sampleImageView : ImageView? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_kotlin)
sampleImageView = findViewById(R.id.imageView) as ImageView?
sampleImageView?.setImage("https://kbob.github.io/images/sample-3.jpg")
}
//Extension function for ImageView
fun ImageView.setImage(url:String, context:Context){
Glide.with(context).load(url).into(this)
}
}