Android, Xamarin: Load Pictures Into Recycle Viewer While Scrolling - android

Have you ever been going through apps like 9gag, Instagram or Facebook? You notice, that while scrolling the pictures load into the recviewer. They sort of come in one by one while you can still use the app.
I have implemented my own custom recviewer and am stuck right here:
pictures = KumulosHelper.Pictures.getNewestXPhotosFromUserInRange
(strUsername, "6", startNumberOfTask.ToString(), "1");
foreach (var picture in pictures)
{
startNumberOfTask++;
var metrics = Resources.DisplayMetrics;
lstData.Add(new DataForProfile()
{
imageId = startNumberOfTask,
img = (Bitmap.CreateScaledBitmap
(KumulosGeneral.DecodePhotoFromBase64(picture.photo),
metrics.WidthPixels, metrics.WidthPixels, true)),
description = picture.taskId.ToString()
});
}
Where I type in "6" is where I get 6 pictures to load into my recycle viewer form the server. I can type in 10 or 20 and it continues loading those pictures. So, I could probably set up a "reachedbottomevent" to continue loading new pictures when the viewer reached the end. But that would mean that the user has to wait everytime he or she reached the bottom of the matrix. This is pretty annoying. Also, the whole activity wont start untill all pictures are fully loaded.
How do I get my recview to behave like the afforementioned apps above? Some sort of "smart" loading the pictures?
Thank you for the input!

Try using the Picasso library which is very reliable and easy to use. It also loads pictures in a background thread.
https://www.nuget.org/packages/Square.Picasso/
Also, the whole activity won't start until all pictures are fully loaded.
In order not to block your UI thread try using a BackgroundWorker or a Task.
At last, if you want to load a new batch of images before the user reaches the bottom of your list, then change your logic to trigger the call for a new batch for example when the user scrolls half the table.

Related

Expansion Android Apk working very slow whenever downloading first time

Currently, I am using this plugin
https://github.com/erobertson42/cordova-plugin-xapkreader/tree/cordova-9
I am using this path to show the image -
'content://com.x.x/main_expansion/assets/dummy.png';
and to make image path safe I am using domSanitizer.bypassSecurityTrustUrl method
getImg(img) {
img = this.domSanitizer.bypassSecurityTrustUrl(img);
return img;
}
Whenever I am using my app, it is working very slow but I am going one by one on the screen throughout the app first time it stucks on every screen approx 40 to 60seconds but after taking sometime it will work normally. If anybody has any suggestion or concern, please share

How to best load images from URL in Android ListView

So I have been struggling with the best way to load images in a ListView in Android for a while.
The images come from a server, so can take some time to load. From what I understand there are only 2 ways to implement this:
1 - Load the images on the main thread - This leads to have the correct images display immediately when the view displays and scrolls, but gives poor scrolling performance and can lead to the app hanging and crashing.
2 - Load the images in an AsyncTask. This means the images will be blank when the list display or scroll, but eventually display. Because of caching done by the list, you can also get the wrong image for an item. But this gives good performance and scrolling, and does not hang/crash.
Seems like neither solution works correctly. There must be a solution that works?? I have seen other posts like this, but the answer seems to always be 1 or 2, but neither is a good solution...
My code for the list adapter is below.
The HttpGetImageAction.fetchImage() method either executes an async task, or fetches the image on the main thread depending on a flag I set. I also cache the images locally and on disk once they have been loaded, so the issue mainly occur the first time (but the cache is only for 100 images, and the list has 1000s)
public class ImageListAdapter extends ArrayAdapter<WebMediumConfig> {
Activity activity;
public ImageListAdapter(Activity activity, int resourceId, List<WebMediumConfig> items) {
super(activity, resourceId, items);
this.activity = activity;
}
class ImageListViewHolder {
ImageView imageView;
TextView nameView;
TextView descriptionView;
TextView statView;
}
public View getView(int position, View convertView, ViewGroup parent) {
ImageListViewHolder holder = null;
WebMediumConfig config = getItem(position);
LayoutInflater mInflater = (LayoutInflater)this.activity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
if (convertView == null) {
convertView = mInflater.inflate(R.layout.image_list, null);
holder = new ImageListViewHolder();
holder.imageView = (ImageView)convertView.findViewById(R.id.imageView);
holder.nameView = (TextView) convertView.findViewById(R.id.nameView);
holder.descriptionView = (TextView)convertView.findViewById(R.id.descriptionView);
holder.statView = (TextView)convertView.findViewById(R.id.statView);
convertView.setTag(holder);
} else {
holder = (ImageListViewHolder) convertView.getTag();
}
holder.nameView.setText(Utils.stripTags(config.name));
holder.descriptionView.setText(Utils.stripTags(config.description));
holder.statView.setText(config.stats());
if (MainActivity.showImages) {
HttpGetImageAction.fetchImage(this.activity, config.avatar, holder.imageView);
} else {
holder.imageView.setVisibility(View.GONE);
}
return convertView;
}
}
You should never load images on the main thread. Any network calls should be done asynchronously and should not be done on the main thread. Your images might load quickly (on the main thread) because you may have a good internet connection, have small images and/or have few images. But imagine what would happen if someone using your app has a slower internet and then one day your list grows to hundreds!
The approach below is a well accepted practice for loading scrollview content
a) load your text content as you load the scrollview. Again the loading should be done async but you can show a loading view until the download completes
b) Show image
placeholders while the image loads on each cell
c) Load the images
asynchronously
Using a library such as Picasso would help immensely because there is a lot of boilerplate code which you'd need to handle otherwise such as
cancelling image download when a cell is reused and
starting a new download for a reused cell
caching
retrying a failed download
Hope this helps.
Use Glide [https://github.com/bumptech/glide] or Picasso [http://square.github.io/picasso/] - both libraries are very similar, and easy to use, have caching, work in background, allow placeholders ets.
Usage is as simple as:
Picasso.get().load("http://i.imgur.com/DvpvklR.png").into(imageView);
Simple Solution: Glide.
Glide is a fast and efficient open source media management and image
loading framework for Android that wraps media decoding, memory and
disk caching, and resource pooling into a simple and easy to use
interface. Glide supports fetching, decoding, and displaying video
stills, images, and animated GIFs.
You can load the image like:
GlideApp.with(context)
.load("http://via.placeholder.com/300.png")
.into(imageView);
Refer for more info:
https://guides.codepath.com/android/Displaying-Images-with-the-Glide-Library
https://github.com/bumptech/glide
Use Glide library.. Its recommended by Google.
Step 1:
Use latest Glide Version 4 dependency (https://github.com/bumptech/glide)
implementation 'com.github.bumptech.glide:glide:4.8.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.8.0'
Step 2:
Glide.with(MainActivity.this)
.load(url)
.apply(new RequestOptions().placeholder(R.drawable.placeholder).error(R.drawable.error_image))//this line optional - you can skip this line
.into(imageview);
use picasso or Glide Library
What these library do , is that they image url from u and download them and save it in cache so that whenever you open the same image another time it will load faster and it prevents the usage of the network
Picasso
http://square.github.io/picasso/
Glide
https://github.com/bumptech/glide
You can try UnivarsalImageLoader which is quite Fast and Optimised.
It supports Multithread image loading (async or sync) and high level of customization.
https://github.com/nostra13/Android-Universal-Image-Loader
Here's how I do it
First I always create the RecyclerView.ViewHolder to which I pass a Func<int, Bitmap> image_provider (or you can pass some sort of lamda?) which when called will retrieve the image for a specific Id, if it can't it will show the default loading image
Everything is stored in a fragment (which I call a DataFragment) which has retainstate set to true.
First load a list of json objects that tell me which images I must show, this happens inside the data fragment so it will go on if there is a configuration change
I send that info to the adapter using a method like SetItems(MyImages)
Then I start a new thread that will load the images (while allowing the user to work)
I do this with the TPL library, the closest approximation for Android is Anko Async but you can also do it with AsyncTask, the problem is that I send a lot of messages to the main thread, so to do it with AsyncTask you have to give it a handler to the main thread which will send messages.
In the thread I loop through all the images I have to download, and download them and send a message to the DataFragment which sends it to the currently attached Activity which triggers the NotifyItemChanged method in the adapter
The adapter on creation receives a Func<int, Bitmap> image_provider which it invokes and retrieves the image from memory
I know it sounds a bit messy, so if you want code examples I can give them , but they are in C#
In your app level gradle implement below repository:
implementation 'com.squareup.picasso:picasso:2.5.2'
Put below line where you want to load image
Picasso.with(getApplicationContext()).load(config.avatar).into(imageview);
just use Picasso.get().load("http://i.imgur.com/DvpvklR.png").into(imageView); or
Glide.with(getApplicationContext()).load("image_url").asBitmap().centerCrop().into(new BitmapImageViewTarget(imageView)
in adapter like any view and done no extra efforts required

Why Android is storing dynamic views in RAM?

Question may seem weird but I ran in an issue with Android app. I am creating views for my Android app dynamically using layout inflater and the view contains images. Most images are less than 800kb. So when I open the app, it starts using tons of RAM and when I switch to another activity, it adds more ram which contains images too. When I exit the activity and come back to the first activity it is not cleaning the RAM. I cannot add my actual code here because of the company policy but here is the snippet of what I am doing,
I have two activities HomeActivity and DisplayActivity. In home activity I have empty scrollview. I am making db query an pulling information. Using layout inflater,
for (DataModel d : dm) {
JSONObject dataJson = new JSONObject(d.getData());
View headerCard = LayoutInflater.from(HomeActivity.this).inflate(R.layout.head_card, null);
ImageView cardHeadImage = (ImageView) headerCard.findViewById(R.id.head_card_header_image);
TextView cardNote = (TextView) headerCard.findViewById(R.id.head_card_note);
String imageLink = dataJson.getJSONObject("data").getString("article_image");
String note = dataJson.getJSONObject("data").getString("article_title");
imageLoader.displayImage(h.getMediaURL() + imageLink, cardHeadImage);
cardNote.setText(note);
scrollViewChild.addView(headerCard);
}
So when I don't add images, it is using less RAM. I have cached images. So my question is that is there anything possible to clear RAM after view is generated and placed in display? I can provide more information if needed. But basically this is how my both activities are running.
The imageLoader works in three steps. 1) Check for cached image. 2) If image is not cached than download image and cache it. 3) Display image in imageview. 2nd step will be skipped most time as images are mostly available from cache.
Thanks. :D

Images randomly break in PhoneGap/WebView on Android 4.4

I am experiencing a strange bug in PhoneGap on Android 4.4, for which I couldn't find any solution online. In my app, I am loading a lot of different images from a remote server, and as the user navigates back and forth, new images are loaded on each page (4 at a time, to be specific, through jQuery-generated html). After having navigated back and forth for a little while, some images will randomly not show up and instead show the typical "broken image" icon.
Now, here comes the strange part: I have been following the instructions at jQuery/JavaScript to replace broken images and done a few tests of my own. In conclusion, the naturalWidth and naturalHeight parameters report the right sizes of the images, and complete reports true for all images. Therefore, the solutions mentioned in the above SO thread don't work at all. Changing the image src doesn't help, either with or without a setTimeout (I tried adding the current timestamp as a parameter to the image path as well).
Did anyone else encounter this issue at all, or am I going crazy here? :)
EDIT: By the way, no error is ever reported. Therefore, no error handler is called when loading the image, making it useless to solve the problem with the already suggested methods (see the link above).
This is how i handle error images,
<img src="images/imageName.jpg" onError="onErrorFunc(this);" alt=" " />
function onErrorFunc(elem){
var imgUrl = "https://alternative-image";
elem.onerror = function (){
elem.src='images/noimage.jpg';
}
elem.src=imgUrl;
}
Hope it helps!

Way to "preload" Canvas animation before showing the page?

I have started experimenting with the WebView in order to display locally stored HTML files that play some fairly basic Canvas animations (using the CreateJS library).
They seem to play really well but some times, the page is loading and playing the animation before images are fully loaded (e.g. one animation has a fairly large background image ~1mb)
Is there a way to either "speed" things up, "preload/prefetch" the images (and other assets if need be) or simply wait till the app has loaded the images into its memory (if it does that) before displaying and starting the animation?
To add to that last question, is there a way to "pause" the animation and only play it once the images have fully loaded?
Thanks
Try with onload event which runs after all images or external resources are loaded:
$(window).load(function(){
//code
});
or you can try with load event for individual images and run your code when they have loaded:
$('img.imgClass').load(function(){
// The image loaded,,, code
});
The CreateJS suite includes PreloadJS, which is a pretty decent way of preloading content that is used by EaselJS, or any other content for that matter.
http://preloadjs.com
You can pre-fetch images, javascript, audio, JSON/text/XML, SVG, CSS, etc.
var queue = new createjs.PreloadJS();
queue.addEventListener("complete", handleComplete);
queue.loadManifest([
{id: "image", src:"image.png"},
{id: "image2", src: "image2.jpg"},
{id:"script", src:"script.js"}
]);
function handleComplete(event) {
var image = queue.getResult("image"); // Returns an image tag
}
There are some workarounds to preload audio (with multiple formats), or use tag loading to get around cross-domain issues. Check out the docs for more:
http://www.createjs.com/Docs/PreloadJS/modules/PreloadJS.html
You can do it like this:
create a function that should do this:
loadData();
loadData = function()
{
preloader = new createjs.LoadQueue(true);
preloader.addEventListener("complete",this.handleComplete.bind(this));
var manifest = [
{src:"./images/yourIMAGE.PNG"},
];
preloader.loadManifest(manifest);
};
handleComplete = function()
{
console.log("Load complete!");
//other function
};
}
This is a way to do it. If you have questions just let me know.

Categories

Resources