I have an array of image urls and I want to download the images using Picasso and show them in a grid view. My current implementation works but it is putting the last image into every image view in the grid view.
public class GridViewAdapter extends ArrayAdapter {
Context context;
public GridViewAdapter(Context context) {
super(context, 0);
this.context = context;
}
public int getCount() {
return thumbnailURLS.size();
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
if(row == null) {
LayoutInflater inflater = ((Activity)context).getLayoutInflater();
row = inflater.inflate(R.layout.grid_row, parent, false);
ImageView gridImageView = (ImageView)row.findViewById(R.id.gridImageView);
for(String s : thumbnailURLS) {
Picasso.with(context)
.load(s)
.placeholder(R.drawable.placeholder)
.error(R.drawable.placeholder)
.into(gridImageView);
}
}
return row;
}
}
getView is called once for each item (ie the number of times it is called is equal to 'getCount'). Easiest thing to do would be to ditch the for loop and lookup the thumbnailUrl using the position argument.
ie
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
if(row == null) {
LayoutInflater inflater = ((Activity)context).getLayoutInflater();
row = inflater.inflate(R.layout.grid_row, parent, false);
}
ImageView gridImageView = (ImageView) row.findViewById(R.id.gridImageView);
Picasso.with(context)
.load(thumbnailURLs.get(position))
.placeholder(R.drawable.placeholder)
.error(R.drawable.placeholder)
.into(gridImageView);
return row;
}
Your getView is actually loading every single image into the list item, and so only the last one becomes the visible one!
The correct solution would be as follows:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
if (row == null) {
LayoutInflater inflater = ((Activity) context).getLayoutInflater();
row = inflater.inflate(R.layout.grid_row, parent, false);
}
ImageView gridImageView = (ImageView) row.findViewById(R.id.gridImageView);
Picasso.with(context).load(thumbnailURLS.get(position)).placeholder(R.drawable.placeholder)
.error(R.drawable.placeholder).into(gridImageView);
return row;
}
You should also look into using the ViewHolder pattern (http://developer.android.com/training/improving-layouts/smooth-scrolling.html#ViewHolder) such that you are not doing a findViewById every time getView is being called!
try this
// Get the image URL for the current position.
String url = getItem(position);
// Trigger the download of the URL asynchronously into the image view.
Picasso.with(context) //
.load(url) //
.placeholder(R.drawable.placeholder) //
.error(R.drawable.error) //
.fit() //
.into(view);
Related
I am getting following warning in eclipse:
Unconditional layout inflation from view adapter: Should use View Holder pattern (use recycled view passed into this method as the second parameter) for smoother scrolling.
on:
convertView = vi.inflate(R.layout.activity_friend_list_row, parent, false);
I have a base adapter with a CheckBox implemented and I have added a tag to make the CheckBox work.
Here is the code:
public View getView(final int position, View convertView, ViewGroup parent)
{
ViewHolder mViewHolder;
mViewHolder = new ViewHolder();
LayoutInflater vi = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = vi.inflate(R.layout.activity_friend_list_row, parent, false);
mViewHolder.cb = (CheckBox) convertView.findViewById(R.id.checkBox);
convertView.setTag(mViewHolder);
if (InviteFriends.isChecked[position] == true)
{
mViewHolder.cb.setChecked(true);
}
else
{
mViewHolder.cb.setChecked(false);
}
mViewHolder.cb.setOnCheckedChangeListener(new OnCheckedChangeListener()
{
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean ischecked)
{
if (buttonView.isChecked())
{
InviteFriends.isChecked[position] = true;
}
else
{
InviteFriends.isChecked[position] = false;
}
}
});
TextView friendsname = (TextView) convertView.findViewById(R.id.friendsName); // title
ImageView thumb_image = (ImageView) convertView.findViewById(R.id.list_image); // thumb image
HashMap<String, String> song = new HashMap<String, String>();
song = data.get(position);
// Setting all values in listview
friendsname.setText(song.get(InviteFriends.KEY_DISPLAY_NAME));
imageLoader.DisplayImage(song.get(InviteFriends.KEY_IMAGEPROFILE_URL), thumb_image);
return convertView;
}
The results are coming up properly. How do I fix this warning? I am not able to get a solution for this yet?
Thanks!
Try this
static class ViewHolder {
private TextView friendsname;
private ImageView thumb_image;
private CheckBox cb;
}
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder mViewHolder = null;
HashMap<String, String> song = null;
if (convertView == null) {
song = new HashMap <String, String>();
mViewHolder = new ViewHolder();
LayoutInflater vi = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = vi.inflate(R.layout.activity_friend_list_row, parent, false);
mViewHolder.friendsname = (TextView) convertView.findViewById(R.id.friendsName); // title
mViewHolder.thumb_image = (ImageView) convertView.findViewById(R.id.list_image); // thumb image
mViewHolder.cb = (CheckBox) convertView.findViewById(R.id.checkBox);
convertView.setTag(mViewHolder);
mViewHolder.cb.setTag(data.get(position));
mViewHolder.cb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean ischecked) {
InviteFriends.isChecked[position] = buttonView.isChecked();
}
});
} else {
mViewHolder = (ViewHolder) convertView.getTag();
}
song = mViewHolder.cb.getTag();
mViewHolder.friendsname.setText(song.get(InviteFriends.KEY_DISPLAY_NAME));
mViewHolder.imageLoader.DisplayImage(song.get(InviteFriends.KEY_IMAGEPROFILE_URL), thumb_image);
mViewHolder.cb.setChecked(InviteFriends.isChecked[position]);
return convertView;
}
you should init the convert view only if it is null
these lines
LayoutInflater vi = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = vi.inflate(R.layout.activity_friend_list_row, parent, false);
// [...] the rest of initialization part
// [...] some changes that must be done at refresh
return convertView;
should look like this:
if (convertView == null) {
LayoutInflater vi = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = vi.inflate(R.layout.activity_friend_list_row, parent, false);
// [...] the rest of initialization part
}
// [...] some changes that must be done at refresh
return convertView;
the goal is to recycle the already existing view in that list, not to init it each time you display it when scrolling the list for example.
Unconditional layout inflation from view adapter: Should use View Holder pattern (use recycled view passed into this method as the second parameter) for smoother scrolling.
It means that you need to use View Holder pattern in your Adapter. The point of using View Holder is to reusing the views because inflating and using findViewById are slow.
When you're using the following code:
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder mViewHolder;
mViewHolder = new ViewHolder();
LayoutInflater vi = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = vi.inflate(R.layout.activity_friend_list_row, parent, false);
mViewHolder.cb = (CheckBox) convertView.findViewById(R.id.checkBox);
convertView.setTag(mViewHolder);
...
return convertView;
}
you're not reusing the views but instead you always create new views.
You need to change your code to something like this (please check the comment):
// class for holding the cached view
static class ViewHolder {
TextView tvFriendsName;
ImageView imvThumbImage;
CheckBox cbInviteFriend;
}
public View getView(final int position, View convertView, ViewGroup parent) {
// holder of the views to be reused.
ViewHolder viewHolder;
// get data based on the position
HashMap<String, String> song = data.get(position);
// if no previous views found
if (convertView == null) {
// create the container ViewHolder
viewHolder = new ViewHolder();
// inflate the views from layout for the new row
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
convertView = inflater.inflate(R.layout.rowlayout, parent, false);
// set the view to the ViewHolder.
viewHolder.cbInviteFriend = convertView.findViewById(R.id.checkBox);
viewHolder.tvFriendsName = convertView.findViewById(R.id.friendsName);
viewHolder.imvThumbImage = convertView.findViewById(R.id.list_image);
// save the viewHolder to be reused later.
convertView.setTag(viewHolder);
} else {
// there is already ViewHolder, reuse it.
viewHolder = (ViewHolder) convertView.getTag();
}
// now we can set populate the data via the ViewHolder into views
viewHolder.tvFriendsName.setText(song.get(InviteFriends.KEY_DISPLAY_NAME));
imageLoader.DisplayImage(song.get(InviteFriends.KEY_IMAGEPROFILE_URL), viewHolder.imvThumbImage);
viewHolder.cbInviteFriend.isChecked(InviteFriends.isChecked[position]);
return convertView;
}
My suggestion is try to use convertView = vi.inflate(R.layout.activity_friend_list_row, null); insted of convertView = vi.inflate(R.layout.activity_friend_list_row, parent, false); this may help you.
:-
okey.. insted of accessing like this TextView friendsname = (TextView) convertView.findViewById(R.id.friendsName); // title
ImageView thumb_image = (ImageView) convertView.findViewById(R.id.list_image); // thumb image
you have to use viewholder class in your adapter
for example
static class ViewHolder {
public TextView text;
public ImageView image;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View rowView = convertView;
// reuse views
if (rowView == null) {
LayoutInflater inflater = context.getLayoutInflater();
rowView = inflater.inflate(R.layout.rowlayout, null);
// configure view holder
ViewHolder viewHolder = new ViewHolder();
viewHolder.text = (TextView) rowView.findViewById(R.id.TextView01);
viewHolder.image = (ImageView) rowView
.findViewById(R.id.ImageView01);
rowView.setTag(viewHolder);
}
// fill data
ViewHolder holder = (ViewHolder) rowView.getTag();
String s = names[position];
holder.text.setText(s);
if (s.startsWith("Windows7") || s.startsWith("iPhone")
|| s.startsWith("Solaris")) {
holder.image.setImageResource(R.drawable.no);
} else {
holder.image.setImageResource(R.drawable.ok);
}
return rowView;
}
What i have done is created a BaseSpinnerAdapter class and reusing it if needed
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.BaseAdapter
import androidx.annotation.LayoutRes
/**
* #param T Model class
* #param VH ViewHolder class, which holds the view to reuse it
* */
abstract class BaseSpinnerAdapter<T, VH>(context: Context?, private val items: ArrayList<T>) :
BaseAdapter() {
private var inflater: LayoutInflater = LayoutInflater.from(context)
/** use viewHolder pattern to reusing the views
* #param p0 current view position
* #param p1
* #param viewGroup
* */
override fun getView(p0: Int, p1: View?, viewGroup: ViewGroup?): View {
var pClone = p1
val viewHolder: VH
if (pClone == null) {
pClone = inflater.inflate(setSpinnerItemLayout(), viewGroup, false)
viewHolder = createViewHolder(pClone)
pClone?.tag = viewHolder
} else {
viewHolder = pClone.tag as VH
}
getView(viewHolder, p0)
return pClone!!
}
override fun getItem(p0: Int): T {
return items[p0]
}
override fun getItemId(p0: Int): Long {
return p0.toLong()
}
override fun getCount(): Int {
return items.size
}
#LayoutRes
abstract fun setSpinnerItemLayout(): Int
abstract fun getView(viewHolder: VH, position: Int)
abstract fun createViewHolder(view: View?): VH
}
Here is an example How you can use BaseSpinnerAdapter in your implementation. I believe that there is no need to detail out the code description.
class SpinnerImplAdapter(context: Context?, private val items: ArrayList<AnyModelClass>) : BaseSpinnerAdapter<AnyModelClass, SpinnerImplAdapter.ViewHolderImp>(context, items) {
override fun setSpinnerItemLayout(): Int {
return R.layout.spinner_item
}
override fun createViewHolder(view: View?): ViewHolderImp {
return ViewHolderImp(view)
}
override fun getView(viewHolder: ViewHolderImp, position: Int) {
val model = items[position]
viewHolder.textView?.text = "" // model.etc get anything you want
}
/**
* I have used kotlin extension to get the viewId,
* if you are not using then simply call the view.findViewById(R.id.yourView)
* */
class ViewHolderImp(view: View?) {
val textView: TextView? = view?.textView
}
}
im new to android and java and i created a my own adapter and list view from a tutorial I was following online. I want to know how do i the image for each individual row because my adapter (from the tutorial) only has 1 image for all the rows. here is the code for my own adapter.
class HangarAdapter extends ArrayAdapter<String> {
public HangarAdapter(Context context, String[] values) {
super(context, R.layout.hangar_layout, values);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater theInflater = LayoutInflater.from(getContext());
View theView = theInflater.inflate(R.layout.hangar_layout, parent, false);
String ship = getItem(position);
TextView theTextView = (TextView) theView.findViewById(R.id.textView1);
theTextView.setText(ship);
ImageView theImageView = (ImageView) theView.findViewById(R.id.imageView);
theImageView.setImageResource(R.drawable.dot);
return theView;
}
}
theImageView.setImageResource(R.drawable.dot);
This line above is what you have to change in order to use a different images;
you could potentially do something like this;
if (position == 0){
theImageView.setImageResource(R.drawable.dot);
} else if (position == 1){
theImageView.setImageResource(R.drawable.circle);
}
It also depends on the backing data.
My Code: starts with after onCreate Override then the the bottom portion is my ArrayAdapter. Im trying to add individual strings from the array to a TextView. When I run the app it puts all the strings in the array on every TextView it makes. Thanks for taking a look.
MyListAdapter adapter = new MyListAdapter(mMyStringArrayArray());
setListAdapter(adapter);
...
private class MyListAdapter extends ArrayAdapter<String> {
public MyListAdapter(ArrayList<String> strings) {
super(getActivity(), 0, strings);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// If we weren't given a view, inflate one
if (convertView == null) {
convertView = getActivity().getLayoutInflater().inflate(R.layout.my_list, null);
}
LinearLayout mLinearLayout = (LinearLayout)convertView.findViewById(R.id.my_list_linearLayout);
TextView[] strings = new TextView[mMyStringArray.size()];
for (int i = 0; i < mMyStringArray().size(); i++) {
// Create TextView
strings[i] = new TextView(getActivity());
strings[i].setText(mMyStringsArray(i));
strings[i].setId(i + 5);
mLinearLayout.addView(strings[i]);
}
return convertView;
}
}
getView() is called once for each item in your Adapter. So, with your for loop, that is running through each item in mMyStringArray for every item in your Adapter. Try instead to use position to get the element you need. Something like
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// If we weren't given a view, inflate one
if (convertView == null) {
convertView = getActivity().getLayoutInflater().inflate(R.layout.my_list, null);
}
LinearLayout mLinearLayout = (LinearLayout)convertView.findViewById(R.id.my_list_linearLayout);
TextView[] strings = new TextView[mMyStringArray.size()];
// Create TextView
strings[position] = new TextView(getActivity());
strings[position].setText(mMyStringsArray(i));
strings[position].setId(position + 5);
mLinearLayout.addView(strings[position]);
}
return convertView;
}
Something like that should work. However, since you have the layout it would be much easier to just create a TextView inside your LinearLayout of my_list.xml and use that.
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// If we weren't given a view, inflate one
if (convertView == null) {
convertView = getActivity().getLayoutInflater().inflate(R.layout.my_list, null);
}
LinearLayout mLinearLayout = (LinearLayout)convertView.findViewById(R.id.my_list_linearLayout);
TextView tv = (TextView) convertView.findViewById(R.id.someID) //where someID is the id you give the TextView in your layout file
tv.setText(mMyStringsArray(position));
tv.setId(position + 5); // Shouldn't need this now
}
return convertView;
}
I also suggest watching The world of ListView
I am trying to implement a listview that displays a list of directories. and under each directory is a gridview with associated adapter (shown below) showing a list of image thumbnails (see below image). I have it working great except whenever the list item is off the screen then brought back on screen, the images are reloaded. I am using an asynctask to download the thumbnails and replace the placeholder image for each imageview so it is not acceptable that everytime an item is offscreen, all of its thumbnails are downloaded again. Does anyone have an example of this type of implementation (gridview adapter within a listview adapter) where the imageview (or images) are stored? What is the proper way to do this?
Thanks in advance for your help.
Gallery Adapter
public class GalleryAdapter extends BaseAdapter {
private Context mContext;
ArrayList<GalleryItem> GalleryList;
//MediaAdapter adapter;
public GalleryAdapter(Context c,ArrayList<GalleryItem> l) {
mContext = c;
GalleryList = l;
}
public int getCount() {
return GalleryList.size();
}
public Object getItem(int position) {
return GalleryList.get(position);
}
public long getItemId(int position) {
return 0;
}
public View getView(int position, View convertView, ViewGroup parent) {
GalleryViewHolder viewHolder = null;
if(convertView==null){
// inflate the layout
LayoutInflater inflater = ((Activity) mContext).getLayoutInflater();
convertView = inflater.inflate(R.layout.gallery_item, parent, false);
viewHolder = new GalleryViewHolder();
viewHolder.title = (TextView) convertView.findViewById(R.id.title);
viewHolder.folder_settings = (ImageView) convertView.findViewById(R.id.folder_settings);
viewHolder.mediaGrid = (GridView) convertView.findViewById(R.id.imagegrid);
viewHolder.gridHolder = (LinearLayout) convertView.findViewById(R.id.gridholder);
convertView.setTag(viewHolder);
}
else{
viewHolder = (GalleryViewHolder) convertView.getTag();
}
viewHolder.title.setText(GalleryList.get(position).getTitle());
//Formatting the gridView to fit the screen dim.
ImageTools mWidth = new ImageTools(mContext);
viewHolder.mediaGrid.setColumnWidth(mWidth.imageSize());
int rows = (int) Math.ceil((GalleryList.get(position).getMedia().size() / mWidth.columnNumber)+1);
LinearLayout.LayoutParams labelLayoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, mWidth.imageSize()*rows);
viewHolder.gridHolder.setLayoutParams(labelLayoutParams);
viewHolder.mediaGrid.setLayoutParams(labelLayoutParams);
viewHolder.mediaGrid.setMinimumHeight(mWidth.imageSize()*rows);
//Set Adapter for image views
viewHolder.mediaGrid.setAdapter(new MediaAdapter(convertView.getContext(),GalleryList.get(position).getMedia()));
viewHolder.folder_settings.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Dialogs.createListDialog(mContext,"Folder Actions", R.array.gallery_action_array).show();
}
});
viewHolder.mediaGrid.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view,int position, long id) {
}
});
return convertView;
}
private class GalleryViewHolder {
private TextView title;
private ArrayList<ImageView> imageList;
private GridView mediaGrid;
private ImageView folder_settings;
private LinearLayout gridHolder;
private int position;
}
}
Media Adapter
public class MediaAdapter extends BaseAdapter {
private Context mContext;
ArrayList<MediaItem> mediaitems;
public MediaAdapter(Context c,ArrayList<MediaItem> l) {
mContext = c;
mediaitems = l;
}
public int getCount() {
return mediaitems.size();
}
public Object getItem(int position) {
return mediaitems.get(position);
}
public long getItemId(int position) {
return 0;
}
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView;
if (convertView == null) { // if it's not recycled, initialize some attributes
imageView = new ImageView(mContext);
imageView.setImageResource(R.drawable.loading);
imageView.setTag(R.integer.path,mediaitems.get(position).getPath().toString());
imageView.setTag(R.integer.fullsize,"false");
imageView.setTag(R.integer.parentpath,mediaitems.get(position).getParentPath().toString());
imageView.setTag(R.integer.index , String.valueOf(position));
try {
new thumbDownload(mContext).execute(imageView);
} catch (DbxException e) {
e.printStackTrace();
}
ImageTools mWidth = new ImageTools(mContext);
imageView.setLayoutParams(new GridView.LayoutParams(mWidth.imageSize(), mWidth.imageSize()));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
} else {
imageView = (ImageView) convertView;
}
//imageView.setImageBitmap(mediaitems.get(position).getBitmap());
return imageView;
}
}
Try with this..may be its silly way, but its worked for me. Just add a line of code inside your method like this for gallery adapter:
public View getView(int position, View convertView, ViewGroup parent) {
GalleryViewHolder viewHolder = null;
// Add this line.
convertView = null;
if(convertView==null){
// inflate the layout
LayoutInflater inflater = ((Activity) mContext).getLayoutInflater();
convertView = inflater.inflate(R.layout.gallery_item, parent, false);
viewHolder = new GalleryViewHolder();
viewHolder.title = (TextView) convertView.findViewById(R.id.title);
viewHolder.folder_settings = (ImageView) convertView.findViewById(R.id.folder_settings);
viewHolder.mediaGrid = (GridView) convertView.findViewById(R.id.imagegrid);
viewHolder.gridHolder = (LinearLayout) convertView.findViewById(R.id.gridholder);
convertView.setTag(viewHolder);
}
else{
viewHolder = (GalleryViewHolder) convertView.getTag();
}
// rest of your code
}
You can use StickyGridHeaders to implement your UI and Android-Universal-Image-Loader for flexible asynchronous image loading.
StickyGridHeaders is an Android library that provides a GridView that shows items in sections with headers. By default the section headers stick to the top like the People app in Android 4.x but this can be turned off.
Android-Universal-Image-Loader aims to provide a reusable instrument for asynchronous image loading, caching and displaying.
I ended up Using a HashMap<String,Bitmap> to store the images once they were downloaded. I made the hashMap static in my mediaAdapter so I could add the bitmap from my asynctask when it was downloaded. Then in my media Adapter getView(), I added a if statement to check if the image had already been downloaded. If it had, I used setImageBitmap(myHash.get(key)).
I have a custom ListView. This ListView contain of 1 Image and 3 TextView. How can I click on ImageView and get current position. (Do not click to other elements, only ImageView)
In your custom adapter class, you are havinggetView(.. .. ..) method :
#Override
public View getView(final int position, final View convertView, final ViewGroup parent) {
// >>> >>> ^^^^^^ This is your posotion = index
View row = convertView;
Your_Holder holder = null;
if(row == null)
{
LayoutInflater inflater = ((Activity)context).getLayoutInflater();
row = inflater.inflate(resId, parent, false);
holder = new Your_Holder();
....
....
holder.yourImageView = (ImageView) row.findViewById(R.id.yourImageID);
....
....
row.setTag(holder);
}
else{
holder = (Your_Holder)row.getTag();
}
// Set here your images click event
holder.yourImageView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
System.out.println("Position = " + position)
}
});
}