I have a grid. Each item has 2 ImageViews. Loaded image is the same for both ImageViews, but in one ImageView I want to apply a transformation to bitmap.
Can I load original bitmap only one time and cache both original and transformed bitmap (for both Imageviews) ?
private DrawableRequestBuilder<String> mFullRequest;
private DrawableRequestBuilder<String> mBlurRequest;
...
//in my adapter constructor
mFullRequest = Glide.with(context)
.fromString()
.diskCacheStrategy(DiskCacheStrategy.ALL)
.priority(Priority.HIGH)
.crossFade(R.anim.fade_in, 150);
mBlurRequest = Glide.with(context)
.fromString()
.diskCacheStrategy(DiskCacheStrategy.RESULT)
.transform(new BlurredImageTransformation(context))
.crossFade();
.....
//in getview
final String imgUrl =.....
mFullRequest
.load(imgUrl)
.into(holder.campaignImage);
mBlurRequest
.load(imgUrl)
.override(Constants.MAX_BLURRED_IMAGE_WIDTH, (int) (Constants.MAX_BLURRED_IMAGE_WIDTH * ratio))
.into(holder.blurredImage);
Related
I would like to set background in LinearLayout from file, path is correct, but not works
Bitmap bitmap5 = BitmapFactory.decodeFile(data.getData().getPath());
BitmapDrawable bitmapDrawable = new BitmapDrawable(bitmap5);
mDrawingPad.setBackground(bitmapDrawable);
I'm trying to create a RecyclerView that is populated by ImageViews in each cell and each image corresponds to an image in Firebase Storage.
I have a list of Strings that is passed into my RecyclerView adapter and each one represents a URL to an image in Firebase Storage. I load each image inside the onBindViewHolder().
What i get in return is a very VERY slow loading of a few images (around 5-see picture) and then it takes around 4 minutes to load another 5 and it never seems to load any other images after these.
I've read multiple posts on StackOverflow but most of them just tell you to use fitCenter() or centerCrop() but that doesn't change anything in my case. I also read in Glide's documentation that Glide will automatically downsample your images so i shouldn't need to do it manually, right? Any ideas what i could be doing wrong here? The Url Strings are successfully retrieved from Firebase and the queries are resolved almost instantly so i don't think there is any issue there.
UPDATE:
I've made some modifications in the onBindViewHolder() method in order to explicitly request caching of the images from Glide and i also used the thumbnail API to download lower resolutions of the images. Now more images are loading but each one still takes around 7 seconds to load which obviously is too long. If you have any suggestions let me know please.
Here's how the RecyclerView is set up in my main activity:
iconsRCV = findViewById(R.id.cardIconsRCV)
iconsRCV.layoutManager = GridLayoutManager(this,5) // set the layout manager for the rcv
val iconUrls : ArrayList<String> = ArrayList() // initialize the data with an empty array list
val adapter = CardIconAdapter(this,iconUrls) // initialize the adapter for the recyclerview
iconsRCV.adapter = adapter // set the adapter
Note that i get new data when certain queries are done and then i call adapter.notifyDataSetChanged() to pass new data to the RecyclerView.
CardIconAdapter.java:
public class CardIconAdapter extends RecyclerView.Adapter<CardIconAdapter.ViewHolder> {
private RequestOptions requestOptions = RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.ALL).centerCrop().error(R.drawable.applogotmp);
private List<String> urlsList;
private Context context;
class ViewHolder extends RecyclerView.ViewHolder {
ImageView iconImg;
ViewHolder(#NonNull View view) {
super(view);
iconImg = view.findViewById(R.id.cardIcon);
}
}
public CardIconAdapter(Context cntxt, List<String> data) {
context = cntxt;
urlsList = data;
}
#NonNull
#Override
public CardIconAdapter.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_icons_rcv_item,parent,false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull CardIconAdapter.ViewHolder holder, int position) {
GlideApp.with(context).load(urlsList.get(position)).apply(requestOptions).into(holder.iconImg);
}
#Override
public int getItemCount() {
return urlsList.size();
}
}
P.S. The image sizes in Firebase are mostly udner 200KB but with a small few reaching 4MB. Also, the ImageView in the R.layout.card_icons_rcv_item layout is 75x75 in size.
Hope you have used latest version of glide.
There are few ways for better image loading and caching,
credit goes to this nice article .
1. Enable Disk Cache
val requestOptions = RequestOptions().diskCacheStrategy(DiskCacheStrategy.ALL)
Glide.with(context).load(url).apply(requestOptions).into(imageView)
2. List item
val requestOptions = RequestOptions()
.diskCacheStrategy(DiskCacheStrategy.ALL)
.signature(ObjectKey(signature))
Glide.with(context).load(url).apply(requestOptions).into(imageView)
3. Override Image Size (Optional)
val requestOptions = RequestOptions()
.diskCacheStrategy(DiskCacheStrategy.ALL)
.signature(ObjectKey(signature))
.override(100, 100) // resize does not respect aspect ratio
Glide.with(context).load(url).apply(requestOptions).into(imageView)
4. Add Thumbnail Url
// With thumbnail url
Glide.with(context).load(url)
.thumbnail(Glide.with(context).load(thumbUrl))
.apply(requestOptions).into(imageView)
// Without thumbnail url
// If you know thumbnail size
Glide.with(context).load(url)
.thumbnail(Glide.with(context).load(url).apply(RequestOptions().override(thumbSize)))
.apply(requestOptions).into(imageView)
// With size multiplier
Glide.with(context).load(url)
.thumbnail(0.25f)
.apply(requestOptions).into(imageView)
5. Setup Monthly Schedule for Cleaning
// This method must be called on the main thread.
Glide.get(context).clearMemory()
Thread(Runnable {
// This method must be called on a background thread.
Glide.get(context).clearDiskCache()
}).start()
6. To Transform bitmap
// TODO remove after transformation is done
.diskCacheStrategy(SOURCE) // override default RESULT cache and apply transform always
.skipMemoryCache(true) // do not reuse the transformed result while running
.diskCacheStrategy(DiskCacheStrategy.ALL) // It will cache your image after loaded for first time
.format(DecodeFormat.PREFER_ARGB_8888) //for better image quality
.dontTransform() // to load image faster just skip transform
.placeholder(R.drawable.placeholder) // use place holder while image is being load
When adding markers in mapbox, there is a provision to add custom icon to the marker. I wonder whether we can inflate a view(R.layout file) instead of assigning a drawable icon.
Here is the code:-
public void onMapReady(MapboxMap mapboxMap) {
IconFactory iconFactory=IconFactory.getInstance(context);
for(int i=0;i<coordinates.size();i++){
mapboxMap.addMarker(new MarkerOptions()
.position(new LatLng(lat,longt))
.icon(iconFactory.fromResource(R.drawable.ic_location_green))
//can we inflate a view here instead of assigning a drawable image?
}
}
I don't think that it is possible
What you can do is draw custom icon at runtime:
Draw it on Canvas
Generate Drawable at runtime (instead of creating xml, you can create an object. So if you were to type <shape>, you can replace it with new Shape(); in Java)
Generate a view and copy its bitmap (How to convert Views to bitmaps?) this option would provive only looks of it - things like click listeners will not work, thus I don't see a reason for choosing this option
This possible using the following utility class:
/**
* Utility class to generate Bitmaps for Symbol.
* <p>
* Bitmaps can be added to the map with {#link com.mapbox.mapboxsdk.maps.MapboxMap#addImage(String, Bitmap)}
* </p>
*/
private static class SymbolGenerator {
/**
* Generate a Bitmap from an Android SDK View.
*
* #param view the View to be drawn to a Bitmap
* #return the generated bitmap
*/
public static Bitmap generate(#NonNull View view) {
int measureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
view.measure(measureSpec, measureSpec);
int measuredWidth = view.getMeasuredWidth();
int measuredHeight = view.getMeasuredHeight();
view.layout(0, 0, measuredWidth, measuredHeight);
Bitmap bitmap = Bitmap.createBitmap(measuredWidth, measuredHeight, Bitmap.Config.ARGB_8888);
bitmap.eraseColor(Color.TRANSPARENT);
Canvas canvas = new Canvas(bitmap);
view.draw(canvas);
return bitmap;
}
}
A full example integrating this code can be found here. Note that this code can be executed on a background thread, so you don't need to block the main thread for it. While we don't expose a binary atm we are looking into creating a little plugin around this code. The feature request for it can be found here.
I want to do an image slideshow on Android with only full sceen Images. No thumbnail (like in gallery), no zoom, no pinch. Only left/right slide to move from one picture to the other.
Pcitures are saved on the data folder and there are ~2000 pictures.
I use a full screen ViewPager for displaying the picures. Of course I can't load all the 2000 at once so I only load the needed picture when the user swipes to the corresponding view.
Each time the user swipes to a new viewPager position I call this code
public void onNewViewToDisplay(Integer newPosition, Integer previousPosition) {
File file = functionToGetTheCorrespondingFile(newPosition);
Bitmap bp;
bp = BitmapFactory.decodeFile(file.getPath());
((ImageView)imageAdapter.views.get(newPosition)).setImageBitmap(bp);
if (previousPosition != null && previousPosition != newPosition) {
BitmapDrawable bitmapDrawable =
((BitmapDrawable)((ImageView)imageAdapter.views.get(previousPosition)).getDrawable());
// Set the previous imageView to empty view
((ImageView) imageAdapter.views.get(previousPosition)).setImageResource(0);
// Then free previous bitmap memory
if (bitmapDrawable != null && bitmapDrawable.getBitmap() != null) {
bitmapDrawable.getBitmap().recycle();
}
}
}
So I expect my code to always free the memory used by the previous displayed imageview.But when I look at my memory occupation
Leading to a memory leakage. What am I doing wrong?
Your approach is not efficient, because you create each bitmap every time the user swipes.
You should use LruCache to store the bitmaps so you don't have to re-create one that was already decoded.
OK I found the reason why I add these memory leaks. This was because I had a reference to the views in my PagerAdapter.
You need to have no reference to the views and implement the destroyItem like this
public void destroyItem (ViewGroup container, int position, Object object)
{
((ImageView)object).setImageResource(0);
BitmapDrawable bmd = (BitmapDrawable) ((ImageView) object).getDrawable();
if (bmd != null) bmd.getBitmap().recycle();
View view = (View)object;
((ViewPager) container).removeView(view);
view = null;
}
I'm trying to load a png image as a drawable from my device sd card.
I use the following function but it doesn't work:
public Drawable getDrawable()
{
return new BitmapDrawable(imagePath);
}
The image path is: mnt/sdcard/MyFolder/image.png
The app crashes when I try calling that method, how should I load my png image located in my sdcard and cast it into a Drawable object?
There is actually a BitmapDrawable constructor straight from file path. The method you are using is depricated. Try:
Drawable myDrawable = new BitmapDrawable(getResources(), pathName);
If this doesnt work, Try getting a bitmap and creating a drawable from it:
The bitmap can be created with decodeFile
You can use it like this:
Bitmap myBitmap = BitmapFactory.decodeFile(pathName);
Then you can use the bitmap for drawing etc.
to convert Bitmap to drawable use
Drawable myDrawable = new BitmapDrawable(getResources(), myBitmap);
Take a look Here (Bitmaps) and Here (Bitmap Drawables) for more info.
I am simple do like that
public Drawable getDrawableFromPath(String filePath) {
Bitmap bitmap = BitmapFactory.decodeFile(filePath);
//Here you can make logic for decode bitmap for ignore oom error.
return new BitmapDrawable(bitmap);
}