Custom adapter for ListView (only 1 ImageView being displayed) - java

For some reason my custom adapter is only allowing me to have 1 image visible in the list view at any time, I am not sure why this is happening. There should be a image within each list item but only the last list item has its image set, so I assume I am re-using a value somewhere that I should not be. The rest of the list items are being set properly.
public class CustomAdapter extends ArrayAdapter<Item> {
private ArrayList<Item> itemList;
private ViewHolder holder;
private Context context;
public CustomAdapter(Context context, int textViewResourceId, ArrayList<Item> list) {
super(context, textViewResourceId, list);
this.itemList = new ArrayList<Item>();
this.itemList.addAll(list);
this.context = context;
}
private class ViewHolder {
ImageView img;
TextView name;
CheckBox access;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater vi = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = vi.inflate(R.layout.list_grid, null);
holder = new ViewHolder();
holder.name = (TextView) convertView.findViewById(R.id.name);
holder.access = (CheckBox) convertView.findViewById(R.id.checkBox1);
holder.img = (ImageView) convertView.findViewById(R.id.imageView1);
convertView.setTag(holder);
}
else { holder = (ViewHolder) convertView.getTag(); }
Item it = itemList.get(position);
new UrlLookup().execute(it.getUrl());
holder.name.setText(it.getName());
holder.access.setChecked(it.isSelected());
return convertView;
}
//Create an image from the url passed in from the server and display it on the image view
private class UrlLookup extends AsyncTask<String, Integer, String>{
Bitmap bmp;
#Override
protected String doInBackground(String... params){
try {
URL u = new URL(params[0]);
bmp = BitmapFactory.decodeStream(u.openConnection().getInputStream());
} catch (Exception e) { e.printStackTrace(); }
return "Done!";
}
#Override
protected void onPostExecute(String result){
super.onPostExecute(result);
holder.img.setImageBitmap(bmp);
}
}
}

You're forgetting the fact that your holder changes every time a view is visible. So it's normal only the last item has an image.
AsyncTask will take considerably longer to finish than your getView method.
You should pass your ImageView to your AsyncTask:
new UrlLookup().execute(it.getUrl(), holder.img);
To call the AsyncTask
private class UrlLookup extends AsyncTask<Object, Integer, String>{
Bitmap bmp;
ImageView view;
#Override
protected String doInBackground(Object... params){
view = (ImageView) params[1];
try {
URL u = new URL((String)params[0]);
bmp = BitmapFactory.decodeStream(u.openConnection().getInputStream());
} catch (Exception e) { e.printStackTrace(); }
return "Done!";
}
#Override
protected void onPostExecute(String result){
super.onPostExecute(result);
view.setImageBitmap(bmp);
}
}
Now you will have a reference of the ImageView of every row in every AsyncTask.
I would like to point out to you that Koush has a splendid project for doing just what you want:
UrlImageViewHelper

Related

How to get data from AsyncTask in android?

Iv tried some on the solutions in stack overflow, but was not able to work it out for my case.
I want to get the Bitmap data from onPostExecute or bmp from doInBackground and set it to ImageView imageurl using imageurl.setImageBitmap() in the getView() method
public class BooksAdapter extends ArrayAdapter<Books> {
public BooksAdapter(Activity context, ArrayList<Books> word) {
super(context, 0, word);
}
#NonNull
#Override
public View getView(int position, #Nullable View convertView, #NonNull ViewGroup parent) {
// Check if the existing view is being reused, otherwise inflate the view
View listItemView = convertView;
if (listItemView == null) {
listItemView = LayoutInflater.from(getContext()).inflate(
R.layout.list_item, parent, false);
}
// Get the {#link AndroidFlavor} object located at this position in the list
Books currentbook = getItem(position);
TextView bookView = (TextView) listItemView.findViewById(R.id.bookTittle);
String booktitle = currentbook.getBookName();
bookView.setText(booktitle);
TextView authorView = (TextView) listItemView.findViewById(R.id.authorname);
String authorname = currentbook.getAuthorName();
authorView.setText(authorname);
ImageView imageurl = (ImageView) listItemView.findViewById(R.id.imageView);
String imagelink = currentbook.getImageLink();
ImageAsyncTask task = new ImageAsyncTask();
task.execute(imagelink);
// imageurl.setImageBitmap();
return listItemView;
}
private class ImageAsyncTask extends AsyncTask<String, Void, Bitmap> {
#Override
protected Bitmap doInBackground(String... urls) {
URL url = null;
Bitmap bmp = null;
try {
url = new URL(urls[0]);
} catch (MalformedURLException e) {
Log.e(LOG_TAG, "Error with creating URL ", e);
}
try {
bmp = BitmapFactory.decodeStream(url.openConnection().getInputStream());
} catch (IOException e) {
Log.e(LOG_TAG, "Problem retrieving the earthquake JSON results.", e);
}
return bmp;
}
#Override
protected void onPostExecute(Bitmap data) {
}
}
}
Just pass your ImageView to AsyncTask via constructor and then set image in onPostExecute like this:
ImageAsyncTask task = new ImageAsyncTask(myImageView);
private class ImageAsyncTask extends AsyncTask<String, Void, Bitmap> {
private ImageView img;
ImageAsyncTask(Imageview img){
this.img = img;
}
...
protected void onPostExecute(Bitmap data) {
this.img.setImageBitmap(data);
}

The ListView skips the bitmap images in the 2nd and 3rd rows. How to populate all the rows?

The problem is that the getView() method is skipping to view the other images, in short, getView() only displays the 1st and last row of the ListView.
How to populate all the rows in the ListView?
This is my getView() code in my CustomListAdapter, there is no URL included.
I just directly download the images from the API.
public View getView(int position, View convertView, ViewGroup parent) {
RowItemLoyalty rowItem = getItem(position);
if (inflater == null) {
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
if (convertView == null) {
convertView = inflater.inflate(R.layout.list_item, null);
holder = new Holder();
holder.title = (TextView) convertView.findViewById(R.id.description);
holder.exp = (TextView) convertView.findViewById(R.id.promotion_expiration);
holder.tokensFor = (TextView) convertView.findViewById(R.id.tokensForRedeemOffer);
holder.promotionImages = (ImageView) convertView.findViewById(R.id.promotion_image);
convertView.setTag(holder);
} else {
holder = (Holder) convertView.getTag();
}
holder.title.setText(rowItem.getDescription());
holder.exp.setText(rowItem.getDateEnd());
holder.tokensFor.setText(rowItem.getTokensFor());
//holder.promotionImages.setImageBitmap(rowItem.getImage1());
try {
image_id.put(ConstantKeys.ID, rowItem.getImageId());
} catch (JSONException e) {
e.printStackTrace();
}
ImageDownloadRequest2 request = new ImageDownloadRequest2();
SharedPreferences sharedPreferences = context.getSharedPreferences("USERPASS", 0);
String email = sharedPreferences.getString("username", null);
String password = sharedPreferences.getString("password", null);
request.getToken(email, password, new ApiRequestListener() {
#Override
public void onSuccess(Object object) {
(new GetImageTask() {
#Override
protected void onPostExecute(Bitmap data) {
super.onPostExecute(data);
if (data != null) {
Bitmap[] holderImage = new Bitmap[] {data};
holder.promotionImages.setImageBitmap(holderImage[0]);
Log.d("BITMAPVALUE", String.valueOf(data));
}else
{
holder.promotionImages.setImageResource(R.drawable.app_icon);
}
}
}).execute();
}
#Override
public void onError(String error) {
Log.e("Get Logo", error);
}
});
Log.d("IMAGESTASHLOYALTY", String.valueOf(rowItem.getImageId()));
return convertView;
}
And this is my AsyncTask
private static class GetImageTask extends AsyncTask<Void, Void, Bitmap>
{
#Override
protected Bitmap doInBackground(Void... params)
{
ImageDownload2 manager = ImageDownload2.getInstance();
Bitmap result = null;
try
{
result = manager.apiCall("File/DownloadFiles", image_id.toString(), "C");
}
catch (Exception e)
{
e.printStackTrace();
}
return result;
}
}
Standard misconcept of recycling the views. You give the AsyncTask the holder as parameter. But holder will be a different holder by the time the asynctask is ready with downloading the image.
You better give int position as parameter to the AsyncTask as then you can place the image at the rigth position.

Output JSON image in listView

I tried uploading an image using JSON. I create a link to image in JSON. Many times I tried to fix my problem, but with no success.
Is there a mistake in my use of DownloadImageTask in My adapter Adapter ?
MainActivity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
list = (ListView) findViewById(R.id.list);
newsList = new ArrayList<News>();
//this is i take json image
new NewsAsynkTask().execute();
}
public class NewsAsynkTask extends AsyncTask<String , Void, String> {
protected String doInBackground(String... params) {
//code
}
protected void onPostExecute(String file_url) {
pDialog.dismiss();
//this i run adapter
NewsAdapter adapter = new NewsAdapter(getApplicationContext(), R.layout.list_row, newsList);
list.setAdapter(adapter);
}
}
My adapter Adapter
public class NewsAdapter extends ArrayAdapter<News> {
ArrayList<News> ArrayListNews;
int Resourse;
Context context;
LayoutInflater vi;
public NewsAdapter(Context context, int resource, ArrayList<News> objects) {
super(context, resource, objects);
ArrayListNews = objects;
Resourse = resource;
this.context = context;
vi = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = vi.inflate(Resourse, null);
holder = new ViewHolder();
holder.imageview = (ImageView) convertView.findViewById(R.id.imagenews);
holder.nameNews = (TextView) convertView.findViewById(R.id.namenews);
holder.dayNews = (TextView) convertView.findViewById(R.id.daynews);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
new DownloadImageTask(holder.imageview).execute(ArrayListNews.get(position).getImageNews());
holder.nameNews.setText(ArrayListNews.get(position).getNameNews());
holder.dayNews.setText(ArrayListNews.get(position).getDayNews());
return convertView;
}
static class ViewHolder {
public ImageView imageview;
public TextView nameNews;
public TextView dayNews;
}
//this is i try load image. I using wrong somthing, please help me
private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
ImageView bmImage;
ImageLoader imgLoader;
public DownloadImageTask(ImageView bmImage) {
this.bmImage = bmImage;
}
#Override
protected Bitmap doInBackground(String... urls) {
final String urldisplay = urls[0];
Bitmap mIcon11 = null;
try {
Handler handler = new Handler(context.getMainLooper());
handler.post(new Runnable() {
#Override
public void run() {
//this is class on this [link][1]. I try use simple, it work. But here dont work
imgLoader = new ImageLoader(context);
imgLoader.DisplayImage(urldisplay, bmImage);
}
});
} catch (Exception e) {
e.printStackTrace();
}
return mIcon11;
}
protected void onPostExecute(Bitmap result) {
bmImage.setImageBitmap(result);
}
}
}

ListView filled from AsyncTask doesn't render

I have a Task which extends Androids AsyncTask in order to get data from my Database in the cloud. I am doing this as to avoid NetworkingOnMainThread exception (I know you can set up StrictMode but in my opionion it's more of a hack than a solution).
I get no errors whatsoever. I have checked values in debugging and everything gets fetched correctly from my database. I have a Venue where I set my name, description, image etc... and the ArrayList<Venue> which I use for my adapter gets instantiated properly.
The problem is - the ListView doesn't render.
Here is my custom adapter code
public class EventListArrayAdapter extends ArrayAdapter<String> {
Context context;
ArrayList<Venue> values;
public EventListArrayAdapter(Context context, ArrayList<Venue> values) {
super(context, R.layout.single_event_list_item);
this.context = context;
this.values = values;
}
public View getView(int position, View convertView, ViewGroup parent){
View v = convertView;
if(v==null){
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = inflater.inflate(R.layout.single_event_list_item, null);
}
Venue currEvent = values.get(position);
if(currEvent!=null){
TextView singleItemTitle = (TextView) v.findViewById(R.id.singleItemTitle);
TextView singleItemLocation = (TextView) v.findViewById(R.id.singleItemLocation);
TextView singleItemDate = (TextView) v.findViewById(R.id.singleItemDate);
ImageView singleItemImage = (ImageView) v.findViewById(R.id.singleItemImage);
singleItemTitle.setText(currEvent.getName());
singleItemDate.setText(currEvent.getDate());
singleItemLocation.setText(currEvent.getLocation());
singleItemImage.setImageBitmap(currEvent.getVenueImage());
}
return v;
}
}
And here is my AsyncTask code
private class FillContentTask extends AsyncTask<String, Void, Void> {
ArrayList<Venue> venues;
Context activity;
ProgressDialog dialog;
public FillContentTask(Context context) {
this.activity = context;
}
#Override
protected void onPreExecute() {
dialog = new ProgressDialog(activity);
dialog.setTitle("Loading...");
dialog.show();
}
#Override
protected Void doInBackground(String... params) {
this.venues = Venue.getAll();
for (Venue currentVenue : this.venues) {
try {
InputStream in = new java.net.URL(currentVenue.getImageURL()).openStream();
currentVenue.setVenueImage(BitmapFactory.decodeStream(in));
} catch (Exception e) {
Log.e("Error", e.getMessage());
e.printStackTrace();
}
}
return null;
}
#Override
protected void onPostExecute(Void result) {
ListView list = (ListView) findViewById(R.id.eventList);
EventListArrayAdapter adapter = new EventListArrayAdapter(activity, this.venues);
list.setAdapter(adapter);
dialog.hide();
}
}
And as I already said. To the best of my knowledge, the AsnycTask does it's job properly since all ArrayList<Venue> values get filled properly
Override the getCount method in your EventListArrayAdapter
#Override
public int getCount()
{
return values.size();
}
OR
change the super call in EventListArrayAdapter constructor from
super(context, R.layout.single_event_list_item);
to
super(context, R.layout.single_event_list_item,values);
and also you will need to change extends ArrayAdapter<String> to extends ArrayAdapter<Venue>

Multi-line ListView w/ ImageView on left side

I am required to create a project similar to that of the ff. pic. (My apologies for the use a siily pic, I have to make do with what is readily-available).
I wouldn't say what I did was the best approach in making a 'dynamic' ListView but I'd be glad just to make what I currently have work. Anyway my implementation is as follows:
public class MultiLineListViewActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ArrayList<SearchResults> searchResults = getSearchResults();
ListView listView = (ListView) findViewById(R.id.listView);
listView.setAdapter(new MyCustomBaseAdapter(this, searchResults));
}
private ArrayList<SearchResults> getSearchResults() {
ArrayList<SearchResults> results = new ArrayList<SearchResults>();
SearchResults sr1 = new SearchResults();
sr1.setName("Name 1");
sr1.setPhone("12345");
SearchResults.setIcon(R.drawable.pic_one);
results.add(sr1);
sr1 = new SearchResults();
sr1.setName("Name 2");
sr1.setPhone("123456");
SearchResults.setIcon(R.drawable.pic_two);
results.add(sr1);
...
return results;
}
}
public class SearchResults {
private String name;
private String phone;
public static ArrayList<Integer> iconsList = new ArrayList<Integer>();
public void setName(String name) {
this.name = name;
}
public void setPhone(String phone) {
this.phone = phone;
}
public static void setIcon(int i) {
iconsList.add(i);
}
public String getName() {
return this.name;
}
public String getPhone() {
return this.phone;
}
}
public class MyCustomBaseAdapter extends BaseAdapter {
private ArrayList<SearchResults> searchArrayList;
private LayoutInflater layoutInflater;
private Context context;
public MyCustomBaseAdapter(Context context, ArrayList<SearchResults> results) {
this.searchArrayList = results;
//this.context = context;
layoutInflater = LayoutInflater.from(context);
}
#Override
public int getCount() {
return searchArrayList.size();
}
#Override
public Object getItem(int position) {
return searchArrayList.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
ImageView imageView;
if(convertView == null) {
convertView = layoutInflater.inflate(R.layout.custom_row_view, null);
holder = new ViewHolder();
holder.txtName = (TextView) convertView.findViewById(R.id.name);
holder.txtPhone = (TextView) convertView.findViewById(R.id.phone);
imageView = new ImageView(context);
convertView.setTag(holder);
}
else {
//imageView = (ImageView) convertView;
holder = (ViewHolder) convertView.getTag();
}
imageView = new ImageView(context);
imageView.setImageResource(SearchResults.iconsList.get(position));
holder.txtName.setText(searchArrayList.get(position).getName());
holder.txtPhone.setText(searchArrayList.get(position).getPhone());
return convertView;
}
class ViewHolder {
TextView txtName;
TextView txtPhone;
}
The above code was successful in displaying the Name and Phone lines in the ListView, it's the addition of the image that's giving me the 'Force Close'.
Take note of the commented line imageView = (ImageView) convertView;
I have reduced it down to that particular line in that removing the comments causes the app to crash.
Note: Whenever you get a force close that you're asking about in a StackOverflow question, please post the full stack trace.
You're always creating a new ImageView when in fact you probably shouldn't need to create one at all. It should be defined in your custom item layout (which you should probably post as well in this case) just like the TextViews. You can use findViewById the same way and get it.
kabuko is right on both counts. (UPDATE: also his comment below should not be ignored.) I'm guessing that your layout/custom_row_view is a LinearLayout, which would mean that the commented line attempts to coerce a LinearLayout to an ImageView, which yes would raise an exception. Also, your ViewHolder class is superfluous, just define those TextViews as variables in your getView() like so...
// Assuming that layout/custom_row_view is a LinearLayout...
getView(yadda) {
LinearLayout row;
ImageView imageView;
TextView txtName, txtPhone;
if (convertView == null) {
row = // inflate a new one
} else {
row = // get the old one
}
// fetch the views
imageView = row.findViewById(...);
txtName = row.findViewById(...);
txtPhone = row.findViewById(...);
// fill the views
imageView.setImageResource(...);
txtName.setContent(...);
txtPhone.setContent(...);
return row;
}
Ciao.

Categories

Resources