How to combine an imageview with some text in listview - android

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");

Related

Android - How to know when getView is called to inflate list

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.

Load Bitmaps/images in ListView Adapter

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/

Android: How to set the image in this ListView?

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);
}
}
}

ListView, and ArrayAdapter issue, How do I proceed?

I have a Product Class, Which has three fields:-
id
name
price
In my code I create a List<Product> productList = Collections.synchronizedList(new ArrayList<Product>());
This product list is used to create a ArrayAdapter<Product> adapter = ArrayAdapter<Product>(), I fill the productList in a separate thread and adapter is notified accordingly. It is working absolutely fine.
Now,
I want to change the color of the some specific products (say for price < 1000).
Each row of ListView should contain 4 elements product image,name, desc and price.
When User clicks the Product, in a context menu options i.e. buy Product, View Product should be displayed.
I have read few blogs and threads related to that. Still I cant decide where to begin, I read about the customization of the ArrayAdapter, overriding getView(), custom list filters etc. Which way will be the best for my requirement... in other words How can custom adapters and list filters benefit me ?
You should extend BaseAdapter and provide your own layout for each item (getView()). Don't forget to manage the view recycling and maybe use the ViewHolder paradigm.
EDIT
I didn't use a lot the ListAdpater, because it binds to a ListView only. Sometimes I need an adapter for a GridView, and the BaseAdapter gives me enough freedom for all use cases.
Example of BaseAdapter:
public class FanAdapter extends BaseAdapter {
private List<Fan> mFans;
private Activity mContext;
public FanAdapter(Activity context, List<Fan> fans) {
mContext = context;
mFans = fans;
}
private class ViewHolder {
public ImageView image;
public TextView firstName;
public TextView lastName;
}
#Override
public View getView(int position, View view, ViewGroup container) {
if (view == null) {
view = LayoutInflater.from(mContext).inflate(R.layout.fan_item, container, false);
}
ViewHolder viewHolder = (ViewHolder) view.getTag();
if(viewHolder == null){
viewHolder = new ViewHolder();
viewHolder.image = (ImageView) view.findViewById(R.id.image);
viewHolder.firstName = (TextView) view.findViewById(R.id.firstname);
viewHolder.lastName = (TextView) view.findViewById(R.id.lastname);
view.setTag(viewHolder);
}
// setting here values to the fields of my items from my fan object
viewHolder.firstName.setText(fan.getFirstName());
(...)
return view;
}
#Override
public int getCount() {
if (mFans != null) {
return mFans.size();
} else {
return 0;
}
}
#Override
public Object getItem(int position) {
return mFans.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
}
You can use it with an Activity containing a ListView or a ListActivity (having in its layout a ListView with a special id):
<ListView
android:id="#id/android:list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:cacheColorHint="#android:color/transparent" />
This way, your ListActivity that will inflate the view will be able to make a findViewById() call and getListView() will return this internal listView. It's a small hack, you can put your own listView with another id and make the findViewById() yourself. For The ListActivity, there's another hack: if the ListActivity finds an empty view with again a special id, it will be shown when the list is empty:
<include
android:id="#+id/empty"
layout="#layout/empty"
android:visibility="gone"
android:layout_gravity="center" />
Then on your listView, whether you used an Activity or ListActivity, you can set your adapter on the ListView:
getListView().setAdapter(new FanAdapter(this, myFanDataArray)));
in getView(...) method you have to check price and set color of row...
see this customized listview..
http://samir-mangroliya.blogspot.in/p/android-customized-listview.html
i set row color as per odd and even row and
you can set checking price...
if(price < 1000){
row.setBackgroundColor(Color.RED);
}else{
row.setBackgroundColor(Color.Yellow);
}

Infinite Scrolling Gallery View with text description

I have infinite gallery based on this example :
http://blog.blundell-apps.com/infinite-scrolling-gallery/ ,
It runs nicely, now I want to have the Gallery still scroll through the images and under each image there should be a text caption.
I searched net with no result, would you please help me in coding that, just beginner in development.
==================================================================================
NEW Update :
upload photo explane what i need exactly which i get it with normal gallery (applied text to each image and able to customize text too as shown down image ,and each image has diffrenet text than others , but still not succeed to do it with infinite gallery :
PLEASE ANY HELP AND ADVICE .
THANKS ALOT.
I went through Blundell's tutorial and thanks to him I know how to make an Infinitelyscrolling gallery :)
To answer the question, about how to add a text caption below each of the images , I made same small changes to Blundell's nice tut and used some of his suggestions in the above answer and I think I got a nice way of doing the task.
The code below doesnt inflate or use gallery_item.xml at all, so it will increase the performance significantly compared to the way when you are inflating it every time.
Trimmed down code of classes from Blundell's tutorial ( because in the question, you are using only resources and not sdcard).
public class InfiniteScrollingGalleryActivity extends Activity {
public class GalleryItem{
int imageId;
String caption;
public int getImageId() {
return imageId; }
public String getCaption() {
return caption;
}
public GalleryItem(int i,String s) {
imageId=i;
caption=s; }
}
int[] resourceImages = {R.drawable.ic_launcher,R.drawable.ic_launcher,R.drawable.ic_launcher,
R.drawable.ic_launcher,R.drawable.ic_launcher,R.drawable.ic_launcher};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GalleryItem[] item = new GalleryItem[6];
//initialising all items, change member variables according to needs
for(int i=0;i<6;i++){
item[i] = new GalleryItem(resourceImages[i], "pic no" +(i+1)); }
setContentView(R.layout.activity_main);
InfiniteGallery galleryOne = (InfiniteGallery) findViewById(R.id.galleryOne);
galleryOne.setResourceGallery(item);
} }
Here I have added the GalleryItem class array and passed it.
Also added the below code in InfiniteGalley class.
public void setResourceGallery(GalleryItem[] item) {
setAdapter(new InfiniteGalleryResourceAdapter(getContext(), item));
setSelection((getCount() / 2));
}
below code's getView() is where the good things happen :
public class InfiniteGalleryResourceAdapter extends BaseAdapter {
/** The context your gallery is running in (usually the activity) */
private Context mContext;
GalleryItem[] myItems;
public InfiniteGalleryResourceAdapter(Context context, GalleryItem[] item) {
this.mContext = context;
myItems=item;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// convertView is always null in android.widget.Gallery
TextView t = new TextView(mContext);
try {
int itemPos = (position % myItems.length);
t.setText(myItems[itemPos].getCaption());
Drawable d = mContext.getResources().getDrawable(myItems[itemPos].getImageId());
((BitmapDrawable) d).setAntiAlias(true); // Make sure we set anti-aliasing otherwise we get jaggies (non-smooth lines)
//d.setBounds(0,0,60,60); //use this to change dimens of drawable,if needed
t.setCompoundDrawablesWithIntrinsicBounds(null, d, null, null);
} catch (OutOfMemoryError e) {
// a 'just in case' scenario
Log.e("InfiniteGalleryResourceAdapter", "Out of memory creating imageview. Using empty view.", e);
}
return t;
}
#Override
public int getCount() {
return Integer.MAX_VALUE;
}
#Override
public Object getItem(int position) {
return position;
}
#Override
public long getItemId(int position) {
return position;
}
/** The width of each child image */
private static final int G_ITEM_WIDTH = 120;
/** The height of each child image */
private static final int G_ITEM_HEIGHT = 80;
private int imageWidth;
private int imageHeight;
}
In getView(), I am just creating a textView and assigning the drawable to it using the handy t.setCompoundDrawablesWithIntrinsicBounds(null, d, null, null); . So it excludes the need of inflating layouts which is a heavy operation.
Below is the output image:
In the adapter you can see the method: getView, you can see this method returns an ImageView, so now you want the getView method to return an imageview and textview...
U can do this in a few ways, here how you can do it with a LayoutInflater
View v = getLayoutInflater().inflate(R.layout.gallery_item, null);
((ImageView) v.findViewById(R.id.img)).setImageResource(imageIds[position]);
((TextView) v.findViewById(R.id.caption)).setText(captions[position]);
So in your res/layout folder you should have an 'gallery_item' layout that contains an ImageView (img) and a TextView (caption)
i.e.
gallery_item.xml
<LinearLayout>
<ImageView ... />
<TextView ... />
</LinearLayout>
Hope this was helpfull!
EDIT
so as the above example shows you would need two arrays, one of imageIds and one of textCaptions. To keep your Adapter nice and clean it's screaming for you to make an object.
public class GalleryItem {
int imageId;
String caption
// Constructor
// getters and setters
}
You could then pass an Array or List of your GalleryItems to the Adapter (replacing the setAdapter method). i.e:
GalleryItem[] items = new GalleryItem[];
Then in your getView method as outlined above you would extract each object:
GalleryItem item = items[position];
View v = getLayoutInflater().inflate(R.layout.gallery_item, null);
((ImageView) v.findViewById(R.id.img)).setImageResource(item.getImageId());
((TextView) v.findViewById(R.id.caption)).setText(item.getCaption());
Hope thats clear

Categories

Resources