OnBindViewHolder not called unless I save a file first - java

Update #2
Thanks to AgentP for hinting my problem . I have fixed this issue by making this change inside showImages() :
(Also created a global reference field String path; in the same activity) :
private void showImages(){
// Added following two lines :
ContextWrapper cw = new ContextWrapper(this); // + added
path = cw.getDir("files", Context.MODE_PRIVATE).toString(); // + added
// String path = DrawingActivity.path; // - removed
allFilesPaths = new ArrayList<>();
allFilesPaths = listAllFiles(path);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.galleryRecycleViewId);
recyclerView.setHasFixedSize(true);
RecyclerView.LayoutManager layoutManager = new GridLayoutManager(getApplicationContext(), 3);
recyclerView.setLayoutManager(layoutManager);
ArrayList<Cell> cells = prepareData();
ImageAdapter adapter = new ImageAdapter(getApplicationContext(), cells);
recyclerView.setAdapter(adapter);
}
Update
Apparently I have a static string that is only set inside save image method . It's purpose is to give image folder path to the show images method. I'm trying to provide path to my showImages() method via other means now .
Old
I have a gallery activity that displays images from a folder with recycler view .
When I start my app the gallery is empty despite having images in the folder .
When I go to my drawing activity , save an image and return to gallery activity it shows all images without problems .
I placed a log inside onbindviewholder method and it only executes after I save an image.
What functionality is missing for image adapter to see exiting files and what does image saving do to make it find them when executed ?
(Also I remember when I wrote the save image method that it wanted me to do #SuppressLint("WrongThread") , but now it works without it)
Debug info when opening gallery without saving a file in drawing activity:
ClassLoader referenced unknown path: /data/app/com.example.myapp-2/lib/x86_64
Before Android 4.1, method android.graphics.PorterDuffColorFilter androidx.vectordrawable.graphics.drawable.VectorDrawableCompat.updateTintFilter(android.graphics.PorterDuffColorFilter, android.content.res.ColorStateList, android.graphics.PorterDuff$Mode) would have incorrectly overridden the package-private method in android.graphics.drawable.Drawable
Rejecting re-init on previously-failed class java.lang.Class<androidx.core.view.ViewCompat$2>
D/OpenGLRenderer: Use EGL_SWAP_BEHAVIOR_PRESERVED: true
I/OpenGLRenderer: Initialized EGL, version 1.4
W/OpenGLRenderer: Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without...
Methods involved in displaying images inside Gallery Activity :
List <Cell> allFilesPaths;
// on create calls show images after checking if read storage permission is good
private void showImages(){
// this is the current problem line ,
// I need a way to give path of my image folder to this method
// Check SaveImage() method below to see how it is initially set
// path is a global static field inside drawing activity
String path = DrawingActivity.path;
allFilesPaths = new ArrayList<>();
allFilesPaths = listAllFiles(path);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.galleryRecycleViewId);
recyclerView.setHasFixedSize(true);
RecyclerView.LayoutManager layoutManager = new GridLayoutManager(getApplicationContext(), 3);
recyclerView.setLayoutManager(layoutManager);
ArrayList<Cell> cells = prepareData();
ImageAdapter adapter = new ImageAdapter(getApplicationContext(), cells);
recyclerView.setAdapter(adapter);
}
private ArrayList<Cell> prepareData(){
ArrayList<Cell> allImages = new ArrayList<>();
for (Cell c : allFilesPaths){
Cell cell = new Cell();
cell.setTitle(c.getTitle());
cell.setPath(c.getPath());
allImages.add(cell);
}
return allImages;
}
private List<Cell> listAllFiles(String pathName){
List<Cell> allFiles = new ArrayList<>();
if(pathName != null){
File file = new File(pathName);
File[] files = file.listFiles();
if(files != null){
for (File f : files){
Cell cell = new Cell();
cell.setTitle(f.getName());
cell.setPath(f.getAbsolutePath());
allFiles.add(cell);
}
}
}
return allFiles;
}
Inside my Image Adapter :
private ArrayList<Cell> galleryList;
private Context context;
private static final String TAG = "ImageAdapter";
public ImageAdapter(Context context, ArrayList<Cell> galleryList) {
this.context = context;
this.galleryList = galleryList;
}
#NonNull
#Override
public ImageAdapter.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.cell, parent, false);
return new ImageAdapter.ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull ImageAdapter.ViewHolder holder, final int position) {
holder.img.setScaleType(ImageView.ScaleType.CENTER_CROP);
Log.d(TAG, "onBindViewHolder: " + galleryList.size());
setImageFromPath(galleryList.get(position).getPath(), holder.img);
holder.img.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String path = galleryList.get(position).getPath();
Intent intent = new Intent(context ,ImagePreview.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("imagePath",path);
context.startActivity(intent);
}
});
}
#Override
public int getItemCount() {
return galleryList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
private ImageView img;
public ViewHolder(#NonNull View itemView) {
super(itemView);
img = (ImageView) itemView.findViewById(R.id.img);
}
}
private void setImageFromPath(String path, ImageView image){
File imgFile = new File(path);
try {
Bitmap myBitmap = BitmapFactory.decodeStream(new FileInputStream(imgFile));
image.setImageBitmap(myBitmap);
// ImageView imageView = (ImageView)findViewById(R.id.imageViewSelect);
// imageView.setImageBitmap(bitmap);
}catch (FileNotFoundException e){
e.printStackTrace();
}
}
method inside drawing activity that saves images :
public void saveImage() {
ContextWrapper cw = new ContextWrapper(getContext());
String filename = "img" + System.currentTimeMillis();
File directory = cw.getDir("files", Context.MODE_PRIVATE);
path = cw.getDir("files", Context.MODE_PRIVATE).toString();
File myPath = new File(directory, filename + ".jpg");
FileOutputStream fileOutputStream = null;
try {
fileOutputStream = new FileOutputStream(myPath);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream);
}catch(Exception e){
e.printStackTrace();
}finally {
try {
fileOutputStream.flush();
fileOutputStream.close();
}catch (IOException e){
e.printStackTrace();
}
}
}

Make sure the variable DrawingActivity.path in showImages() have some value inside of it. It might not have initialized properly.

Try putting this in the ViewHolder Class constructor instead
setImageFromPath(galleryList.get(getLayoutPosition()).getPath(), holder.img);

Related

Android: Get integer value of images in SD storage?

I'm looking to add images from my SD card storage into an Interger array list.
At the moment I can display images from my drawable folder because they are (somehow) in int format, for example: Log.d("MyTag", R.drawable.ic_launcher_background)); currently returns me 2131230825.
I can access my SD card images in a way to return me: /storage/emulated/0/Android/data/com.hangr.hangr/files/Pictures/Hangr_20181119__153130.jpg (String format). And I can also make bitmaps out of them with "Bitmap myBitmap = BitmapFactory.decodeFile(f.get(i));" (Bitmap format).
Any idea on how I can pass these string/bitmap forms into an int object?
package com.myapp.myapp;
imports ...
public class test extends Activity {
public ArrayList<Integer> mThumbIds = new ArrayList<>();
File[] listFile;
ArrayList<String> f = new ArrayList<String>();// list of file paths
ArrayList<Bitmap> myBitmapArrayList = new ArrayList<Bitmap>();// list of bitmaps
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test);
getFromSdcard();
GridView gridview = (GridView) findViewById(R.id.gridview);
gridview.setAdapter(new ImageAdapter(this));
// Test: adding drawable items to my interger list
mThumbIds.add(R.drawable.logo);
}
//************** important function that calls from my SD card
public void getFromSdcard(){
File file = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
if (file.isDirectory()) {
listFile = file.listFiles();
for (int i = 0; i < listFile.length; i++)
{
f.add(listFile[i].getAbsolutePath());
Bitmap myBitmap = BitmapFactory.decodeFile(f.get(i));
fml.add(myBitmap);
}
}
//************** TESTING THE VALUE OF MY VARIABLES
Log.d("MyTag", "File file " + file); ///storage/emulated/0/Android/data/com.myapp.myapp/files/Pictures
Log.d("MyTag", "File length " + file.isDirectory()); //true
Log.d("MyTag", "First element in f: " + f.get(0)); ///storage/emulated/0/Android/data/com.myapp.myapp/files/Pictures/20181119__153130.jpg
Log.d("MyTag", "All the Bitmaps? " + myBitmapArrayList); //returns huge list of bitmaps, i.e. android.graphics.Bitmap#39bd71a
Log.d("MyTag", "Drawables? " + (R.drawable.ic_launcher_background)); //returns me 2131230825
}
public class ImageAdapter extends BaseAdapter {
//************** code to zoom in on images, from https://www.androidbegin.com/tutorial/android-gridview-zoom-images-animation-tutorial/
public View getView(final int position, View convertView, ViewGroup parent) {
final ImageView imageView;
if (convertView == null) {
imageView = new ImageView(mContext);
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
} else {
imageView = (ImageView) convertView;
}
imageView.setImageResource(mThumbIds.get(position));
// imageView.setImageListener(bottoms_Listener);
imageView.setTag(mThumbIds.get(position));
imageView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
int id = (Integer) arg0.getTag();
zoomImageFromThumb(arg0, id);
}
});
return imageView;
}
private void zoomImageFromThumb...
}
Before you get mad at me for not "trying", I have. The hardest part was pulling from the device storage and accessing the photos in there. I just dont get why drawable images are ints and why there's no seemingly straightforward way I can turn my images into ints for similar use.
You can't get their integer references, because they don't have any.
R.whatever (R.drawable, R.string, R.xml, etc) are classes generated by Android Studio that hold integer fields whose names correspond to your resources. When you use something like getDrawable(), Android uses the integer you passed to find the corresponding resource and load it as an image. This is all done because the resources are compiled and stored in the APK itself, and aren't accessible with paths.
However, images on internal storage or your SD card aren't resources. They have directly accessible paths, and aren't inside any APKs. Android doesn't give them integers IDs because they don't need them, and it's just not how it works.
To get images from storage, you need to use paths.

How to set images from assets folder to list view?(using SimpleAdapter)

I've got folder in assets called images1 with 114 images in it. I need to set them in listView with 2 textViews and 1 imageView. I haven't problems with textViews, but i don't know how to set images from assets to listView.
I tried:
int ids[] = new int[114];
for (int i = 0; i <ids.length; i++) {//<-------- taking ids of all pictures from images1 in assets-folder
try {
String[] images =getAssets().list("images1");
ArrayList<String> listImages = new ArrayList<String>(Arrays.asList(images));
int imgId = getResourceId(this, listImages.get(i),"images1", getPackageName());
ids[i] = imgId;
}
catch(IOException ex) {}
}
ArrayList<Map<String, Object>> data = new ArrayList<Map<String, Object>>(
questionTexts.length);//<--------filling listView's textViews and imageView
Map<String, Object> m;
for (int i = 0; i < questionTexts.length; i++) {
m = new HashMap<String, Object>();
m.put(ATTRIBUTE_QUESTION_TEXT, questionTexts[i]);//<-------- textView
m.put(ATTRIBUTE_ANSWER_TEXT, answerTexts[i]);//<-------- textView
m.put(ATTRIBUTE_NAME_IMAGE, ids[i]);//<-------- imageView
data.add(m);
String[] from = { ATTRIBUTE_QUESTION_TEXT, ATTRIBUTE_ANSWER_TEXT,
ATTRIBUTE_NAME_IMAGE };
int[] to = { R.id.listView_item_title, R.id.listView_item_short_description, R.id.listView_image };
SimpleAdapter sAdapter = new SimpleAdapter(this, data, R.layout.item,
from, to);
lvSimple = (ListView) findViewById(R.id.lvSimple);
lvSimple.setAdapter(sAdapter);
}
public static int getResourceId(Context context,String variableName, String resourceName,
String packageName) throws RuntimeException{//<----- this method helps me to get IDs of images from assets/images1
try{
return context.getResources().getIdentifier(variableName,resourceName,packageName);
}catch (Exception e){
throw new RuntimeException("Error getting resource id");
}
}
But finally i've got white fields instead my pictures.
I know how to solve this problem when your pictures are in R.drawable, but how to do it when they are in assets subfolder?
You can use this.
try
{
// get input stream
InputStream ims = getAssets().open("avatar.jpg");
// load image as Drawable
Drawable d = Drawable.createFromStream(ims, null);
// set image to ImageView
mImage.setImageDrawable(d);
ims .close();
}
catch(IOException ex)
{
return;
}
Are you sure that this images should be inside assets/ folder? Why not in res/drawables/?
Of course you can load it from assets ;)
To get the list of all files inside asset folder use below code:
list = getAssets().list("images1")
To load the image from asset you can use below code:
fun setImageFromAsset(ImageView view, String filename) {
try {
InputStream is = getAssets().open(filename);
Drawable drawable = Drawable.createFromStream(is, null);
view.setImageDrawable(drawable);
}
catch(IOException ex) {
return;
}
}
You can get a bitmap by using below code
private Bitmap getBitmapFromAssets(String fileName){
AssetManager am = getAssets();
InputStream is = null;
try{
is = am.open(fileName);
}catch(IOException e){
e.printStackTrace();
}
Bitmap bitmap = BitmapFactory.decodeStream(is);
return bitmap;
}
You can show bitmap in imageview by using "Glide" Here is sample code
Glide.with(context)
.load(Uri.parse("file:///android_asset/fileName"))
.into(imageView);
Simple Adapter can work only with IDs in drawable-folder or with Uri. But you can use ViewBinder to set image using setImageBitmap method.

use images from assets folder instead of drawable folder in a ListFragment

im having a hard time to figure out how to use images from assets folders in the same way as in a drawable folder. i have searched and found that i can use getAssets() function, but i cant figure out how to use it, i have costume listview inside fragments.
i dont want to use drawable folder because my assets contains some subfolders that i need.
this is what i have done so far, but the images are from drawable folder and i want to repace them with those in assets:
public class AfricaFragment extends ListFragment{
private AssetManager assets;
/** An array of items to display in ArrayList */
String android_versions[] = new String[]{
"flag1",
"flag2",
"flag3",
"flag4"
};
Integer[] imgid={
R.drawable.a,
R.drawable.b,
R.drawable.c,
R.drawable.d
};
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
assets = getActivity().getBaseContext().getAssets();
InputStream stream;
CustomListAdapter adapter=new CustomListAdapter(getActivity(), android_versions, imgid);
this.setListAdapter(adapter);
return super.onCreateView(inflater, container, savedInstanceState);
}
#Override
public void onStart() {
super.onStart();
/** Setting the multiselect choice mode for the listview */
getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
}
}
Im gratful for any help.
To get image from assets :
try {
// Use image name with extension
InputStream ims = getActivity().getAssets().open("flag1.jpg");
Drawable d = Drawable.createFromStream(ims, null);
iv.setImageDrawable(d);
ims .close();
}
catch(IOException ex) {
return;
}
Try this code
public Bitmap getBitmapFromAssets(String fileName) {
AssetManager assetManager = getAssets();
InputStream is = null;
try{
is = assetManager.open(fileName);
}catch(IOException e){
e.printStackTrace();
}
Bitmap bitmap = BitmapFactory.decodeStream(is);
return bitmap;
}

Android custom adapter and asyncTask not updating listView

I've searched all the posts I can find, and none seem to help with my situation. I have an android project that uses web services to pull down hourly weather data and populate a listView with the results.
The weird problem I'm having is that when I debug the project on my android phone, the main activity is blank and the listView isn't populated. If I run the project from android studio with my phone locked, and then unlock my phone the app opens on my phone with all of the listView properly formatted and populated.
I feel like it's a race condition issue between the asynctask and the adapter, but I can't seem to resolve it. I tried making my asyncTask an inner private class and calling notifyDataSetChanged on the adapter inside the onPostExecute method, but to no avail. I feel it must be something simple, but I'm relatively new to Android dev, so I'm stuck.
I have three classes that I'll post the pertinent code from
MainActivity.java (onCreate)
public class MainActivity extends ActionBarActivity {
ArrayList<Weather> w = new ArrayList<Weather>();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DownloadWeatherTask myTask = new DownloadWeatherTask(w);
WeatherAdapter myAdapter = new WeatherAdapter(this,w);
ListView l = (ListView) findViewById(R.id.weatherList);
l.setAdapter(myAdapter);
myTask.execute();
}
}
WeatherAdapter.java
public class WeatherAdapter extends ArrayAdapter<Weather>{
public WeatherAdapter(Context context, ArrayList<Weather> weather) {
super(context, R.layout.item_weather, weather);
}
public View getView(int position, View convertView, ViewGroup parent) {
// Get the data item for this position
Weather forecast = getItem(position);
// Check if an existing view is being reused, otherwise inflate the view
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.item_weather, parent, false);
}
// Lookup view for data population
TextView tvTime = (TextView) convertView.findViewById(R.id.listTime);
TextView tvDescr = (TextView) convertView.findViewById(R.id.listDescr);
TextView tvTemp = (TextView) convertView.findViewById(R.id.listTemp);
TextView tvHumid = (TextView) convertView.findViewById(R.id.listHumid);
ImageView ivWeather = (ImageView) convertView.findViewById(R.id.weatherImg);
// Populate the data into the template view using the data object
tvTime.setText(forecast.time);
tvDescr.setText(forecast.description);
tvTemp.setText(forecast.temperature+"°(F)");
tvHumid.setText(forecast.humidity+"% humidity");
ivWeather.setImageBitmap(forecast.weatherImg);
// Return the completed view to render on screen
return convertView;
}
}
DownloadWeatherTask.java
public class DownloadWeatherTask extends AsyncTask<Void,Void,Void>{
ArrayList<Weather> data;
public DownloadWeatherTask(ArrayList<Weather> a){
data = a;
}
public ArrayList<Weather> getData() {
return data;
}
protected Void doInBackground(Void...params) {
try {
String website = "http://api.wunderground.com/api/1111111111111/geolookup/q/autoip.json";
URL site = new URL(website);
HttpURLConnection weatherUnderground = (HttpURLConnection) site.openConnection();
weatherUnderground.connect();
JsonParser weatherParser = new com.google.gson.JsonParser();
JsonElement weatherJson = weatherParser.parse(new InputStreamReader((InputStream) weatherUnderground.getContent()));
JsonObject weatherObj = weatherJson.getAsJsonObject();
String zip = weatherObj.get("location").getAsJsonObject().get("zip").getAsString();
String city = weatherObj.get("location").getAsJsonObject().get("city").getAsString();
String state = weatherObj.get("location").getAsJsonObject().get("state").getAsString();
String hourly = "http://api.wunderground.com/api/111111111111/hourly/q/" + state + "/" + city + ".json";
URL hourlySite = new URL(hourly);
HttpURLConnection hourlyConnection = (HttpURLConnection) hourlySite.openConnection();
hourlyConnection.connect();
com.google.gson.JsonParser hourlyParser = new com.google.gson.JsonParser();
JsonElement hourlyWeatherJson = weatherParser.parse(new InputStreamReader((InputStream) hourlyConnection.getContent()));
JsonArray weatherArr = hourlyWeatherJson.getAsJsonObject().get("hourly_forecast").getAsJsonArray();
int l = weatherArr.size();
for (int i = 0; i < l; i++) {
String date = weatherArr.get(i).getAsJsonObject().get("FCTTIME").getAsJsonObject().get("pretty").getAsString();
String temp = weatherArr.get(i).getAsJsonObject().get("temp").getAsJsonObject().get("english").getAsString();
String condition = weatherArr.get(i).getAsJsonObject().get("condition").getAsString();
String humidity = weatherArr.get(i).getAsJsonObject().get("humidity").getAsString();
String iconUrl = weatherArr.get(i).getAsJsonObject().get("icon_url").getAsString();
Bitmap icon = getBitmapFromURL(iconUrl);
data.add(new Weather(date, condition, temp, humidity, icon));
}
} catch (IOException e) {
Log.e("Error: ",e.toString());
}
return null;
}
protected void onPostExecute(Void...params){
}
}
Below are links to my screenshots showing the app not populating the listView, and the app working properly when the program is run while the phone is initially locked.
Any help would be greatly appreciated!!
Thanks
In postExecute(), you need to update the adapter's List and then invoke its notifyDataSetChanged method. I suspect that you were forgetting to update the adapter's data.
The other option is to create a new adapter with the new data, and set the new adapter on the ListView.
I figured out what the issue was! I hadn't added #Override to my onPostExecute() method so it was never being called.
I added the notifyDataSetChanged to my onPostExecute as suggested, which worked once I added the #override to my method.

Using Cursor with AsyncTask

I'm trying to download images for each artist that has music on my phone, then show these images in a GridView. I'm using the lastfm-java library that Last.fm recommends using. The method you call to fetch an artists image is getImageURL(ImageSize size), but before you do this, you need to tell it which artist you want to reference with a String parameter. So, in full it would be something like this:
#Override
protected String doInBackground(Object... arg0) {
Artist artist = Artist.getInfo(artistOrMbid, LASTFM_API_KEY);
return artist.getImageURL(ImageSize.EXTRALARGE);
}
Getting all the artists that are on my phone isn't a problem, you just reference MediaStore. You would do something like this:
private void getArtists() {
String[] projection = new String[] {
MediaStore.Audio.Artists._ID, MediaStore.Audio.Artists.ARTIST,
};
String sortOrder = MediaStore.Audio.Artists.DEFAULT_SORT_ORDER;
Cursor c = getActivity().getContentResolver().query(
MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI, projection, null, null, sortOrder);
if (c != null) {
int count = c.getCount();
if (count > 0) {
final int ARTIST_IDX = c.getColumnIndex(MediaStore.Audio.Artists.ARTIST);
for (int i = 0; i < count; i++) {
c.moveToPosition(i);
}
}
c.close();
c = null;
}
}
The Adapter for my GridView isn't anything special, it simply extends BaseAdapter.
Note AQuery is a library I'm using that helps cache and load a Bitmap from a URL.
public class GridViewAdapter extends BaseAdapter {
private final String[] imageURLs;
private final LayoutInflater mInflater;
private final Activity mActivity;
public GridViewAdapter(String[] urls, Activity activity) {
imageURLs = urls;
mActivity = activity;
mInflater = (LayoutInflater)mActivity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
return imageURLs.length;
}
#Override
public Object getItem(int position) {
return position;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewholder = null;
// Inflate GridView items
if (convertView == null) {
convertView = mInflater.inflate(R.layout.gridview_items, null);
viewholder = new ViewHolder();
viewholder.mImage = (ImageView)convertView.findViewById(R.id.gridview_image);
convertView.setTag(viewholder);
} else {
viewholder = (ViewHolder)convertView.getTag();
}
AQuery aq = new AQuery(convertView);
aq.id(viewholder.mImage).image(imageURLs[position], false, false, 0, 0, null, 0, 0.75f);
return convertView;
}
}
class ViewHolder {
public ImageView mImage;
}
So in full, my AsyncTask is as follows:
public class LastfmArtistGetImageURL extends AsyncTask<Object, Integer, String[]> implements
Constants {
private static final String tag = LastfmArtistGetImageURL.class.getSimpleName();
private GridViewAdapter mGridAdapter;
// Test
private final String[] imageIds = {
"http://userserve-ak.last.fm/serve/252/71875544.png",
"http://userserve-ak.last.fm/serve/252/6258507.jpg",
"http://userserve-ak.last.fm/serve/252/51274303.png",
"http://userserve-ak.last.fm/serve/252/58672183.png",
"http://userserve-ak.last.fm/serve/252/72029714.png",
"http://userserve-ak.last.fm/serve/252/17666215.jpg",
"http://userserve-ak.last.fm/serve/252/63247381.png",
"http://userserve-ak.last.fm/serve/252/33665463.jpg"
};
private final String artistOrMbid;
private final GridView mGridView;
private final Activity mActivity;
public LastfmArtistGetImageURL(String name, GridView gv, Activity activity) {
artistOrMbid = name;
mGridView = gv;
mActivity = activity;
}
#Override
protected String[] doInBackground(Object... arg0) {
Artist artist = Artist.getInfo(artistOrMbid, LASTFM_API_KEY);
Collection<String> col = new ArrayList<String>();
col.add(artist.getImageURL(ImageSize.EXTRALARGE));
return col.toArray(new String[0]);
}
#Override
protected void onPostExecute(String[] result) {
if (result != null)
mGridAdapter = new GridViewAdapter(imageIds, mActivity);
mGridView.setAdapter(mGridAdapter);
super.onPostExecute(result);
}
}
When I call my AsyncTask, I call it in my getArtists() method like this:
new LastfmArtistGetImageURL(c.getString(ARTIST_IDX), mGridView, getActivity())
.execute();
Problem
When I call this, all of the artists images download, but they download one after the other at position 0 of my GridViewAdapter. In other words, one image loads, then next, and so on all in the first position when I need them to be placed into each available position in the GridView. When I return my test String[] in my AsyncTask everything works like it should. All of the images are placed in order in each available space in the GridView.
Question
My question is, how do I return each artist image I download into my GridView correctly and why are the images currently only being loaded at the first position in my GridViewAdapter?
Edit - Shubhayu's answer
I moved setting my GridViewAdapter into my getArtists() method like so. This results in all the images being downloaded (As says LogCat), but only the last one being set in my GridView.
String[] test = new LastfmArtistGetImageURL(c.getString(ARTIST_IDX),
mGridView, getActivity()).execute().get();
mGridAdapter = new GridViewAdapter(test, getActivity());
mGridView.setAdapter(mGridAdapter);
smoak's answer
This results in only the last artist image (by the default order) being downloaded and applied in my GridView.
String[] test = {c.getString(ARTIST_IDX)};
new LastfmArtistGetImageURL(test, mGridView, getActivity()).execute();
Your AsyncTask looks like you are executing it each time for each Artist. Thus, your AsyncTask returns only one Artist's image and your GridView gets that Artists image, then you run the AsyncTask for the next Artist, GridView gets updated with new image and so on. What you need to do is modify your AsyncTask to take a String array of Artist names and loop over them in the doInBackground to get their image's.
// ... SNIPPED
public LastfmArtistGetImageURL(String[] names, GridView gv, Activity activity) {
artistsOrMbids = names;
mGridView = gv;
mActivity = activity;
}
#Override
protected String[] doInBackground(Object... arg0) {
Collection<String> col = new ArrayList<String>();
for (String nameOrMbid : this.artistsOrMbids) {
Artist artist = Artist.getInfo(artistOrMbid, LASTFM_API_KEY);
col.add(artist.getImageURL(ImageSize.EXTRALARGE));
}
return col.toArray(new String[0]);
}
// .... SNIPPED
And pass in all the artist names:
String[] artists = { "The Black Keys", "Rush", "The Allman Brothers" };
new LastfmArtistGetImageURL(artists, mGridView, getActivity()).execute();
here's what is happening, when you pass the test string it has a list of images and hence the gridview shows them properly. but when you use it to download an image for each artist, things go wrong.
Every time you call
new LastfmArtistGetImageURL(c.getString(ARTIST_IDX), mGridView, getActivity()).execute();
it runs the doInBackground(), completes it and then immediately calls the onPostExecute() where it creates a new adapter and passes your result which basically contains a single image of the single call.
So what u need to do is in your asynctask download all the images and then create a single adapter and pass all the images to it. That is not happening currently.
EDIT
If you see the AsyncTask, you will realize that everytime you call it, the string array returns only one image. So instead of returning a string array, return a string.
Next, I would suggest you use an ArrayList in your Adapter instead of a String array.
In your getArtists(), create an ArrayList and everytime you call
new LastfmArtistGetImageURL(test, mGridView, getActivity()).execute();
add the result to your ArrayList. Once you have looped through all the artists, your ArrayList will contain all the images.
Now set it to the Adapter. (You would have t change the adapter a bit if you change it from string to arraylist.)

Categories

Resources