How do i set the images in this layout?
The ListView will contain 3 of the above entries, each image will be downloaded in an AsyncTask (See Below) and the text will be filled in by a String array of preset Strings eg
String[] values = {"string one", "string two", "String three"};
I want to be able to first set the String content values of all 3 entries using the adapter below, then have AsyncTasks running in the background downloading and setting the icons for each entry.
The Strings are more important than the icons so i dont want the user to have to wait for each icon to download before the string is set.
I have a ListView Layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:paddingBottom="#dimen/small_8dp"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/small_8dp" >
<ImageView
android:id="#+id/logo"
android:layout_width="50dp"
android:layout_height="50dp"
android:contentDescription="#string/loading"
android:scaleType="fitXY"
android:src="#drawable/image" >
</ImageView>
<TextView
android:id="#+id/label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="#dimen/small_8dp"
android:text="#string/loading"
android:textSize="#dimen/medium_15dp" >
</TextView>
</LinearLayout>
Which is in a Layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/color_white"
android:orientation="vertical" >
<ListView
android:id="#android:id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:entries="#array/list_headlines">
</ListView>
</LinearLayout>
Ive been working with this custom adapter:
private class ArticleAdapter extends ArrayAdapter<String>{
private final Context context;
private final String[] values;
public ArticleAdapter(Context context, String[] values) {
super(context, R.layout.list_entry, values);
this.context=context;
this.values=values;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rowView = inflater.inflate(R.layout.list_entry,parent,false);
TextView tv = (TextView) rowView.findViewById(R.id.label);
tv.setText(values[position]);
return rowView;
}
}
LoadThumbnail AsyncTask:
protected class LoadThumbnail extends AsyncTask<String, Void, String> {
private String url;
private boolean loaded; //if loaded set the bitmap image to whats downloaded
private Bitmap icon;
private int iconIndex;
public LoadThumbnail(int iconIndex, String url){
loaded = false;
this.url = url; //url of the icon to download
this.iconIndex=iconIndex; //Which icon in the listview were downloading
}
#Override
protected String doInBackground(String... params) {
Download download = new Download(url); //My Download Class
try {
icon = download.downloadImage(); //Returns A Bitmap image
loaded=true; //If no errors caught
} catch (Exceptions e) {
//Various Exception Handling Here
}
return null;
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
}
}
Can you tell me which functions i have to adapt to achieve this?
Thanks!
The easiest way to do this is to download your images to some Collection and then for the list to update when the downloads are complete by calling notifyDatasetChanged on the Adapter.
Before we get started - I don't know where your AsyncTask is located. If its a separate class you need to use an Interface to set up a call back to the place you're starting it from. If its an inner class within your Adaper you can do this localy.
What you're going to do is set up your Adapter to (1) check if the image you want is available (2) if not download the image and add it to a collection (3) when the image is downloaded refresh the list (or when all images are downloaded depening how long this list is).
This all happens when you set the content for a specific list item
if(logo != null){
if(imageCollection.contains(key){
logo.setImageBitmap(imageCollection.get(key));
} else {
thumbnailDownloader.execute;
}
}
If you're AsyncTask is an inner class witin your adapater then within onPostExecute you will (1) add the image to this collection (2) update the ListView using notifyDatasetChanged. If your AsyncTask is it's own class you would add the image to the collction in your callback and update the list from there as well.
For the collection it's easier if you use the LruCache built into android but you could use a HashMap or something else. Just depending on your implementation.
Firstly I'd suggest to give a reference of the ArticleAdapter to the AsyncTask.
Then create a Bitmap array or map in ArticleAdapter, and create an add(Bitmap bmp) method for the ArticleAdapter, which puts a bitmap object into the array/map.
Because you have reference in the AsyncTask, you can call the add() method in the onPostExecute() method, with the downloaded icon Bitmap.
Then you can call the ArticleAdapter's notifyDataSetChanged(), so it can refresh its views.
Of course your Adapter's getView should check its bitmap array/map, if the bitmap for the given key is already downloaded. if it's reference is null, then put a placeholder, if it is downloaded already, place the bitmap which is put in the array/map by the asynctask.
In getView(), you should start a new task to download the image for that list item. Pass a reference of the ImageView you want populated to the AsyncTask, and then set the source for that ImageView to the downloaded bitmap (icon) in onPostExecute().
This does get a little complicated as you will have to deal with Views being recycled by the ListView (by cancelling the tasks to download the images). I like to use this library for all my downloading and caching of images: http://square.github.io/picasso/
This answer is late but may be helpful for others.
In your code, if your AsyncTask<> is located as separate class then you can simply instantiate it with giving it ImageView in you adapter class, like
new LoadThumbnail(imageView).execute(url);
then in your LoadThumbnail class simply set bitmap in imageView.
i am writing this roughly, please make it as you want;
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rowView = inflater.inflate(R.layout.list_entry,parent,false);
TextView tv = (TextView) rowView.findViewById(R.id.label);
ImageView imageView = (ImageView)rowView.findViewById(R.id.logo);
tv.setText(values[position]);
//Instantiate LoadThumbnail giving it imageView to set bitmap in.
new LoadThumbnail(imageView).execute(url);
return rowView;
}
Now in LoadThumbnail simply set bitmap in onPostExecute() method.
protected class LoadThumbnail extends AsyncTask<String, Void, Bitmap> {
private ImageView imageView;
public LoadThumbnail(ImageView imageView){
this.imageView = imageView;
}
#Override
protected String doInBackground(String... url) {
Bitmap icon = null;
Download download = new Download(url[0]); //My Download Class
try {
icon = download.downloadImage(); //Returns A Bitmap image
loaded=true; //If no errors caught
} catch (Exceptions e) {
//Various Exception Handling Here
}
return icon;
}
#Override
protected void onPostExecute(Bitmap icon) {
super.onPostExecute(icon);
//here set bitmap in imageView;
if(icon != null){
imageView.setImageBitmap(icon);
}
}
}
Related
I have a List Adapter which inflates a list. On the method getView() i make an AscyncTask to get an image from internet. However, i figured out that the method getView() is called for numerous reasons, not only to inflate the list.
My question is: how to know when the method is called to inflate the list? I don't want to make a AsyncTask every time the method is called.
Here is my code:
public class ListItem extends ArrayAdapter<Carro> {
private final Activity context;
private final List<Carro> carros;
public ListItem(Activity context, List<Carro> carros) {
super(context, R.layout.list_item, carros);
this.context = context;
this.carros = carros;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = context.getLayoutInflater();
View rowView = inflater.inflate(R.layout.list_item, null, true);
TextView modelo = (TextView) rowView.findViewById(R.id.modelo);
modelo.setText(carros.get(position).getModelo());
GetFoto getFoto = new GetFoto(rowView);
getFoto.execute(position);
return rowView;
}
private class GetFoto extends AsyncTask<Integer, Void, Void> {
Drawable fotoDraw;
View rowView;
int position;
public GetFoto(View view) {
this.rowView = view;
}
#Override
protected Void doInBackground(Integer... params) {
position = params[0];
HTTPHandler handler = new HTTPHandler();
fotoDraw = handler.makeServiceCallImage(carros.get(position).getFoto());
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
ImageView imagem = (ImageView) rowView.findViewById(R.id.imagem);
imagem.setImageDrawable(fotoDraw);
}
}
}
The method getView() will be called every time the view appears on the screen during the scrolling process. The number of getView() method calls can be greater than the number of elements if the ListView height is set to wrap_content.
Use some libraries like Picasso or Glide for downloading images. These libraries will cache the downloaded images that prevents unnecessary requests.
It will look like this:
Glide.with(context)
.load(imageUrl)
.into(imageView);
Or you can read this article https://developer.android.com/topic/performance/graphics/cache-bitmap.html
I recommend using RecyclerView instead of ListView. It recycling the views which prevents multiple inflation
Like Alex Nik said: use picasso or glide for picture loads.
And stop using ListViews, they are kind of deprecated, yet still supported. Use RecyclerView, it is better and easier to use.
So far I've been using a simple ArrayAdapter to display some items in a ListView. Now I also want to display images alongside the text in the ListView. I have an AsyncTask called DownloadImageTask to download images. The downloading is working perfectly, but I don't know how to display an image in the ListView and how to use the DownloadImageTask to download the images in the ListView.
This is the DownloadImageTask I use to download images into an ImageView:
private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
ImageView bmImage;
public DownloadImageTask(ImageView bmImage) {
this.bmImage = bmImage;
}
protected Bitmap doInBackground(String... urls) {
String urldisplay = urls[0];
Bitmap mIcon11 = null;
try {
InputStream in = new java.net.URL(urldisplay).openStream();
mIcon11 = BitmapFactory.decodeStream(in);
} catch (Exception e) {
Log.e("Error", e.getMessage());
e.printStackTrace();
}
return mIcon11;
}
protected void onPostExecute(Bitmap result) {
bmImage.setImageBitmap(result);
}
}
I have also defined an ImageView alongside the ListView to download the images into the layout.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000000">
<ImageView
android:id="#+id/image1"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<ListView
android:id="#android:id/list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</RelativeLayout>
I call the DownloadImageTask like this:
new DownloadImageTask((ImageView) findViewById(R.id.image1)).execute(url);
How can I use the DownloadImageTask to download images and display them in the ListView alongside the text?
To achieve what you want to do you have to create a custom Adapter. To download the images I suggest you use a library like Picasso. Picasso takes care of pretty much everything when downloading the images and it really can't get any easier to use it, you just need to call this to download an image into an ImageView:
Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView);
It already caches images and can also transform images in many ways. Picasso is a very powerful yet easy to use library.
1) Implementing a custom Adapter
First we need to create a layout for each row in the ListView, in your case since you want to display an image and a text it needs to contain a TextView and an ImageView:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/imageView"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_margin="10dp"/>
<TextView
android:id="#+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="#id/imageView"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:textAlignment="gravity"
android:gravity="center"/>
</RelativeLayout>
Now we need to create a container class - called view model - to hold the data which belongs in each row of the ListView. In your case this view model contains the text you want to display and the url to the image:
private class ExampleViewModel {
private String text;
private String imageUrl;
private ExampleViewModel(String text, String imageUrl) {
this.text = text;
this.imageUrl = imageUrl;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public String getImageUrl() {
return imageUrl;
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
}
ListViews use view recycling. We can speed up the performance of the ListView by using a pattern called "view holder". Basically we save a reference to the Views inside each row and attach it to the row itself. That way we need to call the expensive findViewById() only once. This view holder class - I like to call them rows - also contain a method called bind() to bind the data from the view model to the Views in each row. We need a reference to the TextView and ImageView but we also need a Context for Picasso. I also like to define the layout associated with this row as a public constant in the row.
private class ExampleRow {
// This is a reference to the layout we defined above
public static final int LAYOUT = R.layout.list_item;
private final Context context;
private final TextView textView;
private final ImageView imageView;
private ExampleRow(Context context, View convertView) {
this.context = context;
this.imageView = (ImageView) convertView.findViewById(R.id.imageView);
this.textView = (TextView) convertview.findViewById(R.id.textView);
}
public void bind(ExampleViewModel exampleViewModel) {
this.textView.setText(exampleViewModel.getText());
Picasso.with(this.context).load(exampleViewModel.getImageUrl()).into(this.imageView);
}
}
Finally we need a custom Adapter to make this work, it's really nothing special. The only interesting part is in getView(). I will comment important parts if necessary:
public class ExampleAdapter extends BaseAdapter {
private final List<ExampleViewModel> viewModels;
private final Context context;
private final LayoutInflater inflater;
public ExampleAdapter(Context context) {
this.context = context;
this.inflater = LayoutInflater.from(context);
this.viewModels = new ArrayList<ExampleViewModel>();
}
public ExampleAdapter(Context context, List<ExampleViewModel> viewModels) {
this.context = context;
this.inflater = LayoutInflater.from(context);
this.viewModels = viewModels;
}
public List<ExampleViewModel> viewmodels() {
return this.viewModels;
}
#Override
public int getCount() {
return this.viewModels.size();
}
#Override
public ExampleViewModel getItem(int position) {
return this.viewModels.get(position);
}
#Override
public long getItemId(int position) {
// We only need to implement this if we have multiple rows with a different layout. All your rows use the same layout so we can just return 0.
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// We get the view model for this position
final ExampleViewModel viewModel = getItem(position);
ExampleRow row;
// If the convertView is null we need to create it
if(convertView == null) {
convertView = this.inflater.inflate(ExampleRow.LAYOUT, parent, false);
// In that case we also need to create a new row and attach it to the newly created View
row = new ExampleRow(this.context, convertView);
convertView.setTag(row);
}
// After that we get the row associated with this View and bind the view model to it
row = (ExampleRow) convertView.getTag();
row.bind(viewModel);
return convertView;
}
}
And that's everything you need. It's pretty much a best practice implementation of an Adapter. It uses the view holder pattern for extra performance and works perfectly with the view recycling of the ListView. It's fast, concise and easy and leaves little room for errors made by the developer which would otherwise slow the ListView down. You have perfect separation between what data you want to display (that's all in the ExampleViewModel) and how it is displayed (that's in the ExampleRow). The adapter itself doesn't know about either - as it should be!
2) How to use it
To use the code above we first need to create the view models which hold the data we want to display:
ExampleViewModel firstRow = new ExampleViewModel("First Row". "http://http://upload.wikimedia.org/wikipedia/commons/6/6f/Freiburger_Alpen.JPG");
ExampleViewModel secondRow = new ExampleViewModel("Second Row". "http://blog.caranddriver.com/wp-content/uploads/2013/05/lamborghini_egoista_three_quarter_front_view.jpg");
ExampleViewModel thirdRow = new ExampleViewModel("Third Row". "http://4.bp.blogspot.com/-vXnf7GjcXmg/UfJZE9rWc2I/AAAAAAAAGRc/x2CIlHM9IAA/s1600/aphoto49721.jpg");
We need to add all those rows into a List:
List<ExampleViewModel> viewModels = new ArrayList<ExampleViewModel>();
viewModels.add(firstRow);
viewModels.add(secondRow);
viewModels.add(thirdRow);
And after that we need to create an instance of the ExampleAdapter and pass the List of view models in the constructor. Finally we just need to set the Adapter to the ListView:
ExampleAdapter adapter = new ExampleAdapter(context, viewModels);
listView.setAdapter(adapter);
You can modify the items displayed in the ListView later on with the viewmodels() method of the ExampleAdapter! You just need to remember to always call notifyDataSetChanged() on the Adapter after modifying the view models:
adapter.viewmodels().remove(0); // Remove first view model
adapter.viewmodels().add(someNewViewModel); // Add some new view model
// Always remember to call this method after modifying the viewmodels.
// This will apply the changes to the ListView.
// If you forget to call this you will get an exception
adapter.notifyDataSetChanged();
I hope I could help you and if you have any further questions feel free to ask!
You could use an external library like Picasso or Universal Image Loader, they will give you a lot of options. Since they manage cache, you will be able to load images from urls or whatever one time and show them in several places.
You could try:
I'm not sure, but maybe you could use your DownloadClass inside the getView() method of your list's adapter in the next way:
new DownloadImageTask((ImageView) findViewById(R.id.image1)){
#Override
protected void onPostExecute(Bitmap bm) {
//set imageview src with your bitmap }}.execute(url);
}
}.execute(archivo,"investigacion");
I'm trying to add images in a ListView which has an ArrayAdapter. Fyi, the toList() is a conversion from iterator to a list of the given DBObject.
I override the View getView() and set a textview and an image.
private static class EventAdapter extends ArrayAdapter<DBObject> {
public EventAdapter(Context context, int resource, Iterable<DBObject> events) {
super(context, resource, toList(events));
}
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
LayoutInflater vi = LayoutInflater.from(getContext());
v = vi.inflate(R.layout.adapter_event_list, null);
DBObject event = getItem(position);
if (event != null) {
//Get the logo if any
if( ((DBObject)event.get("events")).containsField("logo") ){
String logoURL = ((DBObject)((DBObject)event.get("events")).get("logo")).get("0").toString();
ImageView eventLogo = (ImageView) v.findViewById(R.id.eventLogoList);
new setLogo().execute(logoURL, eventLogo);
}
TextView title= (TextView) v.findViewById(R.id.eventTitleList);
title.setText( ((DBObject)event.get("events")).get("title").toString() );
}
return v;
}
protected static <T> List<T> toList( Iterable<T> objects ) {
final ArrayList<T> list = new ArrayList<T>();
for( T t : objects ) list.add(t);
return list;
}
//setLogo() method here. See below
}
The text in the textview is fine. However the images are getting messed up. They seem to load in wrong places in the list. The route of the code is: 1)Get from the DB (async) 2)populate the ListView 3) while populating load each image(second async).
Here is the setLogo() AsyncTask which is inside the EventAdapter above:
private class setLogo extends AsyncTask<Object,Void,Bitmap>{
ImageView eventLogo = null;
#Override
protected Bitmap doInBackground(Object...params) {
try{
Bitmap eventImage = downloadBitmap((String) params[0]);
eventLogo = (ImageView) params[1];
return eventImage;
}
catch(Exception e){
e.printStackTrace();
return null;
}
}
#Override
protected void onPostExecute(Bitmap eventImage) {
if(eventImage!=null && eventLogo!=null){
eventLogo.setImageBitmap(eventImage);
}
}
}
I did so (using an Async) which I believe is the correct way to load images from urls. I saw this post on multithreading and from which I borrowed the downloadBitmap() method.
As explained above the images are loaded in wrong places of the ListView. What can be a robust way to load them?
Also the idea to pass the v.findViewById(R.id.eventLogoList) inside the AsyncTask is that the program will distinguish each adapter's ImageView but it seems it doesn't.
Update
After following the problem that is causing this mix I found this SO question.
I altered my code in order to check if the if is causing the problem.
//Get the logo if any
if( ((DBObject)event.get("events")).containsField("logo") ){
String logoURL = ((DBObject)((DBObject)event.get("events")).get("logo")).get("0").toString();
ImageView eventLogo = (ImageView) row.findViewById(R.id.eventLogoList);
//new setLogo().execute(logoURL, eventLogo);
TextView title= (TextView) row.findViewById(R.id.eventTitleList);
title.setText( "Shit happens" );
}
Let's say I have 40 items. The Shit happens is set on the fields that a logo field exists. If I scroll down/up the order changes and the text gets messed up. It is because the stack created inside the loop is small than the maximum of the list..I guess... I am still struggling.
PS: I found this easy library to load images asynchronously instead of DYI stuff.
Update 2
I added an else with a static url. Because of the time it take to the image to load they are still misplaced.
I would really go for a good library like Picasso.
It will handle all the hard part for you and it's very well written.
http://square.github.io/picasso/
I've a problem with a listview and shuffle image.
Premise: I know that the cells/rows are re-used (recycled)
My cell is made up of an image and some textview (to simplify we suppose, only one image and only one tetxview)
if the image is present on the net (I know that by a bussines logic...) it's downloaded from the net (correct image); otherwise I put an image from application's bundle (default image)
in this way, i've a shuffle images problem.
Instead, if I load the defualt image always from the net, it's work fine.
My question is: why? why if i load default-image from the net it's ok, but if i load default-image from the app's bundle I've shuffle images problem?
Thanks in advance.
Code of MyAdaper
public class MyArrayAdapter extends ArrayAdapter<MyObject> {
final String URL_DEFAULT_IMG_USER="https://xxxxxxxxxxx/xxxx/default.png";
private LayoutInflater inflater;
private List<MyObject> data;
LinearLayout l1=null;
Context context=null;
public MyArrayAdapter(Context context,List<MyObject> objects) {
super(context, R.layout.item_myobject, objects);
inflater= LayoutInflater.from(context);
this.data=objects;
this.context=context;
BitmapManagerNoCompressionUser.INSTANCE.setPlaceholder(BitmapFactory.decodeResource(
context.getResources(), R.drawable.user));
}
//ViewHolder class that hold over ListView Item
static class ViewHolder{
//ImageView image;
ImageView image;
TextView displayName;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if(convertView == null){
convertView = inflater.inflate(R.layout.item_myobject, null);
holder =new ViewHolder();
holder.image=(ImageView) convertView.findViewById(R.id.imageView);
holder.displayName =(TextView) convertView.findViewById(R.id.textViewDisplayName);
convertView.setTag(R.id.imageViewMyObject, holder.image);
convertView.setTag(R.id.textViewDisplayName, holder.displayName);
//set tag of convertView to the holder
convertView.setTag(holder);
}
else {
holder =(ViewHolder) convertView.getTag();
/* THIS CODE IS IRRELEVANT FOR THE SUFFLE PROBLEM, BUT I DONT'KNOW IF IT IS NECESSARY
holder.image=(ImageView) convertView.getTag(R.id.imageViewMyObject);
holder.displayName =(TextView) convertView.getTag(R.id.textViewDisplayName);
*/
}
holder.image.setTag(position); // This line is important.
if(data.get(position).getPathFotoServer()!=null ) {
BitmapManagerNoCompressionUser.INSTANCE.loadBitmap(data.get(position).getPathFotoServer(), holder.image, 100,100);
} else {
BitmapManagerNoCompressionUser.INSTANCE.loadBitmap(URL_DEFAULT_IMG_USER, holder.image, 100,100); //IT'S OK
//holder.image.setImageResource(R.drawable.default); //WITH THIS LINE, SHUFFLE IMAGE PROBLEM
}
holder.displayName.setText((CharSequence) data.get(position).getDisplayName());
return convertView;
}
}
Code of item_myobject
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/layout1"
android:layout_width="match_parent"
android:layout_height="60dp"
android:orientation="horizontal"
android:layout_gravity="fill_vertical"
android:background="#drawable/state_item_listview"
>
<!-- android:layout_margin="5dp" -->
<ImageView
android:id="#+id/imageView"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_margin="5dp"
android:src="#drawable/user" />
<TextView
android:id="#+id/textViewDisplayName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Medium Text"
android:gravity="left"
android:textStyle="bold"
android:textAppearance="?android:attr/textAppearanceMedium" />
</LinearLayout>
Update: add the code for sort items in the activity:
Collections.sort(data, new Comparator<MyObject>(){
public int compare(MyObject s1, MyObject s2) {
Log.d(TAG, "s1.getDisplayName().compareToIgnoreCase(s2.getDisplayName():"+s1.getDisplayName()+" "+s2.getDisplayName());
return s1.getDisplayName().compareToIgnoreCase(s2.getDisplayName());
}
})
If you want to load images in your listview from server then use android query for performing this, it will manage every thing for you. You don't have to bother for any thing.
check this link for example with code
This is your problem:
else {
BitmapManagerNoCompressionUser.INSTANCE.loadBitmap(URL_DEFAULT_IMG_USER, holder.image, 100,100); //IT'S OK
//holder.image.setImageResource(R.drawable.default); //WITH THIS LINE, SHUFFLE IMAGE PROBLEM
}
Image loading from web it is'nt a quick operation, and while it's running, adapter set image to your adapter from it's own cache.
For this situations i use often a viewSwitcher - when i start loading data i show a progress bar while image is loading. For example - here my code:
Explantations
- newsItem is my adapter item which contains image_url call's "pict" and "image" as my bitmap image. Also in newsItem i create an AsyncTask which will load image from url, and after setting an ArrayList to my adapter i've start an asyncTask and show progressBar in display, if picture was downloaded sucessfuly - i show an image.
if (newsItem.pict.length() != 0 &&
!newsItem.isTaskWasStarted() &&
newsItem.image == null) {
loadImage(newsItem, viewItem);
} else if (newsItem.image != null) {
viewItem.image.setImageBitmap(newsItem.image);
viewItem.flipper.setDisplayedChild(1);
} else if (newsItem.pict.length() == 0) {
viewItem.flipper.setDisplayedChild(0);
} else if (newsItem.image == null && newsItem.isTaskWasStarted()) {
viewItem.flipper.setDisplayedChild(0);
}
I try to develop an Android App which allows the user to fetch data from flickr and show it in a GridView (with some nice 3D-Animation). After some adventures i got it almost running, but now I'm stuck.
Here's the problem:
I got a UI Thread "LoadPhotosTask" which gets the pictures from flickr, just like the open source application photostream. In the method onProgressUpdate(LoadedPhoto... value) of that subclass I call addPhoto(). Until now everythings fine - I got some nice Bitmap and Flickr.photo data with all the information I need.
#Override
public void onProgressUpdate(LoadedPhoto... value) {
addPhoto(value);
}
On the other hand I have got a GridView. Now I want to fill it with the Photos. It has got an adapter called ImageAdapter (which extends BaseAdapter, see this tutorial). If I use an array inside the ImageAdapter class I can populate the GridView with some sample images. But if I want to populate it at runtime, I don't know what to do.
How do I have to set up the getView method in the ImageAdapter? I was trying to fill the array inside the ImageAdapter class with my values in addPhoto, but it doesn't display anything.
So first of all I was setting up the array with the amount of Photos i wanted to display in the grid like that (code is inside the ImageAdapter class):
// class variable
private ImageView[] mThumbIds;
[...]
public void setupArray(int count) {
this.mThumbIds = new ImageView[count];
}
Then I call this method with the lenght of my photolist:
final Flickr.PhotoList list = params[0];
final int count = list.getCount();
int helper = 0;
imagead.setupArray(count);
Afterwards I call the getView method manually inside the addPhoto method:
private void addPhoto(LoadedPhoto... value) {
ImageView image = (ImageView) mInflater.inflate(
R.layout.grid_item_photo, null);
image.setImageBitmap(value[0].mBitmap);
image.setTag(value[0].mPhoto);
imagead.setmThumbIds(image, value[0].mPosition);
imagead.getView(value[0].mPosition, null, mpicturesGrid);
}
That is the getView method inside ImageAdapter:
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) { // if it's not recycled, initialize some
// attributes
imageView = new ImageView(mContext);
imageView.setLayoutParams(new GridView.LayoutParams(EDGE_LENGTH,
EDGE_LENGTH));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setPadding(0, 0, 0, 0);
imageView.setVisibility(View.VISIBLE);
} else {
imageView = (ImageView) convertView;
}
imageView.setImageDrawable(mThumbIds[position].getDrawable());
imageView.setTag(mThumbIds[position].getTag());
return imageView;
}
You are missing a key part.
When you use an Adapter you have a method called notifyDataSetChanged().
The logic you are missing there is the following:
When creating the Adapter for the GridView stay with a reference for the list that the adapter will use. Something like:
private ArrayList<Photo> mPhotos;
private BaseAdapter mAdapter;
private GridView mGridView;
onCreate:
/* other things here */
mAdapter = new MyAdapter(mPhotos);
mGridView.setAdapter(mAdapter);
What you addPhoto should do is the following:
mPhotos.add(photo);
mAdapter.notifyDataSetChanged();
That's it.