0

I'm using Parse in my app, and in order to load my 'profile' images, I need to retrieve a so called Parsefile. When the Parsefile is downloaded it uses a callback to notify when it's done. Now this is generally a nice way to do things but I encountered a problem with this when using a Listview and downloading the images with an Asynctask.

The problem is as follows:

In my ListView adapter in the getView method, I create an AsyncTask and execute it, this AsyncTask starts the retrieveProfileImage(callBack) function. In my callback I simply start a Runnable on the UI thread to update the ImageView in the View with the new (retrieved Image). The problem however as it seems, is the fact that as soon as I start my AsyncTask, the View is returned. So I can't set the other images to the correct row. I hope my code demonstrates my problem more clearly.

The ListAdapter:

public class FriendListAdapter extends ArrayAdapter<Profile> {

private int resource;
private Context context;
private List<Profile> friends;
private Profile fProfile;
private Bitmap profileImageBitmap;
private ProgressBar friendImageProgressBar;


//ui
private ImageView friendImage;

public FriendListAdapter(Context context, int resource,
        List<Profile> objects) {
    super(context, resource, objects);
    this.context = context;
    this.resource = resource;
    this.friends = objects;
}



@Override
public View getView(int position, View convertView, ViewGroup parent) {

    TextView  friendName = null;
    friendImage = null;
    View rowView = convertView;
    if (rowView == null) {
      LayoutInflater inflater = ((Activity) context).getLayoutInflater();
      rowView = inflater.inflate(R.layout.friendslist_row, null);
      friendName = (TextView) rowView.findViewById(R.id.fName);
      friendImage = (ImageView) rowView
          .findViewById(R.id.fImage);
      friendImageProgressBar = (ProgressBar) rowView.findViewById(R.id.friendImageProgressBar);
    } else {
        friendName = (TextView) convertView.findViewById(R.id.fName);
        friendImage = (ImageView) convertView.findViewById(R.id.fImage);
        friendImageProgressBar = (ProgressBar) convertView.findViewById(R.id.friendImageProgressBar);
    }

    fProfile = friends.get(position);

    DownloadProfileImage dImg = new DownloadProfileImage();
    dImg.execute();

    friendName.setText(fProfile.getName());

    return rowView;

}

private class DownloadProfileImage extends AsyncTask<Void, Integer, String> {

    @Override
    protected String doInBackground(Void... arg0) {
        Log.d("logpp", "Starting download image for " + fProfile.getName());
        fProfile.retrieveProfileImage(new ProfileImageCallback());
        return null;
    }

}

private class ProfileImageCallback extends GetDataCallback {

    @Override
    public void done(byte[] bytearray, ParseException e) {
        if (e == null) {
            Log.d("logpp", "Done downloading image for " + fProfile.getName() + ". Setting bitmap to:" +
         " " + friendImage.getId());
            profileImageBitmap = BitmapManager
                    .getBitmapFromByteArray(bytearray);
            ((Activity) context).runOnUiThread(new UpdateUi());
        }
    }
}

private class UpdateUi implements Runnable {

    @Override
    public void run() {
        friendImage.setImageBitmap(profileImageBitmap);
        friendImage.setVisibility(View.VISIBLE);
        friendImageProgressBar.setVisibility(View.INVISIBLE);
    }

}

}

The retrieveProfileImage method:

public void retrieveProfileImage(GetDataCallback callBack) {
    this.image.getDataInBackground(callBack);
}

I hope someone can help me with this one.

Regards,

Tim

Tim Kranen
  • 4,202
  • 4
  • 26
  • 49
  • use lazy loading with libraries like Universal Image Loader and picasso. Using UIL http://stackoverflow.com/questions/16789676/caching-images-and-displaying – Raghunandan Jan 22 '14 at 12:22
  • @Raghunandan The thing is, I can't use URLs. I don't have Image urls, the only way I can retrieve an Image (bitmap) is by using the callback. The parse library is restricting me there. – Tim Kranen Jan 22 '14 at 12:43

2 Answers2

1

i solved this problem by following code

public View getView(int position, View convertView, ViewGroup parent) {
    try {
        if (inflater == null)
            inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        if (convertView == null)
            convertView = inflater.inflate(R.layout.answer_item, null);

        TextView name = (TextView) convertView.findViewById(R.id.textView_ans_user_name);
        TextView body = (TextView) convertView.findViewById(R.id.textView_ans_user_body);
        TextView timestamp = (TextView) convertView.findViewById(R.id.textView_ans_user_timestamp);
        final CircularImageView thumbnail = (CircularImageView) convertView.findViewById(R.id.imageView_ans_user);
        Parse_answer_model ans = answers.get(position);
        name.setText(ans.getAns_by());
        body.setText(ans.getAns_body());
        SimpleDateFormat sdfAmerica = new SimpleDateFormat("dd-M-yyyy hh:mm:ss a");
        sdfAmerica.setTimeZone(TimeZone.getDefault());
        String sDateInAmerica = sdfAmerica.format(ans.getCreatedAt());
        timestamp.setText(sDateInAmerica);
        ParseQuery<User> query = ParseQuery.getQuery("_User");
        query.whereEqualTo("username", ans.getAns_by());
        query.getFirstInBackground(new GetCallback<User>() {

            public void done(User user, ParseException e) {
                // TODO Auto-generated method stub
                if (e == null) {

                    img.DisplayImage(user.getprofile_pic_url(), thumbnail, false);
                } else {
                }
            }
        });
    } catch (Exception e) {
        e.printStackTrace();

    }

put your imageview as final dont make it global and you get image url from geturl() method, it is as defined by parse you can use below example

ParseFile fileObject = (ParseFile) object.get("image_file");
                User user = new User();
                user = (User) ParseUser.getCurrentUser();
                user.setProfile_pic_url(fileObject.getUrl().toString());
                user.saveInBackground();

update

last day i found new solution you can get user's data which related to parse object by following code and made some changes in model class,too.

 void getchats() {
        pd.show();
        ParseQuery<Parse_chat_dialogs> query =    ParseQuery.getQuery("chat_dialogs");
        query.addDescendingOrder("updatedAt");
        query.whereContains("users",  ParseUser.getCurrentUser().getUsername());
        query.findInBackground(new FindCallback<Parse_chat_dialogs>() {
        public void done(List<Parse_chat_dialogs> dilogs, ParseException e) {
            if (e == null) {
                pd.hide();
                dialoglist = (ArrayList<Parse_chat_dialogs>) dilogs;
                adp = new ChatDialogAdapter(Chat_list.this, dialoglist);
                list.setAdapter(adp);
                for (int i = 0; i < dialoglist.size(); i++) {
                    ParseQuery<User> query = ParseQuery.getQuery("_User");
                    query.whereEqualTo("username", dialoglist.get(i).getUsers().trim()
                            .replace(ParseUser.getCurrentUser().getUsername(), "").replace(",", ""));
                    User user = new User();
                    try {
                        user = query.getFirst();
                        dialoglist.get(i).setFirstname(user.getFirstname());
                        dialoglist.get(i).setLastname(user.getLastname());
                        dialoglist.get(i).setProfileurl(user.getprofile_pic_url());
                        adp.notifyDataSetChanged();
                    } catch (ParseException e1) {
                        // TODO Auto-generated catch block
                        e1.printStackTrace();
                    }
                }

            } else {
                Toast.makeText(Chat_list.this, e.getMessage(), Toast.LENGTH_SHORT).show();
            }
        }
    });
}

as in above example i added three new param in parseobejct model class for storing values of user's firstname ,lastname and profile url. i am also sharing model class for getting more idea

@ParseClassName("chat_dialogs")
public class Parse_chat_dialogs extends ParseObject {

private String firstname;
private String lastname;
private String profileurl;

public String getFirstname() {
    return firstname;
}

public void setFirstname(String firstname) {
    this.firstname = firstname;
}

public String getLastname() {
    return lastname;
}

public void setLastname(String lastname) {
    this.lastname = lastname;
}

public String getProfileurl() {
    return profileurl;
}

public void setProfileurl(String profileurl) {
    this.profileurl = profileurl;
}

/////////////////////////////////////////////////////////////////////////////
public String getLast_message() {
    return getString("last_message");
}

public void setLast_message(String value) {
    put("last_message", value);
}

public String getUsers() {
    return getString("users");
}

public void setUsers(String value) {
    put("users", value);
}
}
Viraj Patel
  • 211
  • 2
  • 5
0

How about this!

Instead of using AsyncTask in the adapter class, use it in the MainActivity where you set the adapter for the listview. And in your done method in the Callback or the postExecute update the object/objects and call notifyDataSetChanged().

So, essentially you could have an update method in your adapter class, say, like this,

public void updateObject(int pos, byte[] byteArray){
     //Assuming your Profile Object has some member to store this image data
     friends.get(pos).setImageData(byteArray); //friends is the list in adapter class and setImageData may be the setter in your Profile object class
     notifyDataSetChanged();
    }

and in the getView(), you could do something like this

profileImageBitmap = BitmapManager
                    .getBitmapFromByteArray(friends.get(pos).getImageData);
friendImage.setImageBitmap(profileImageBitmap);
droidx
  • 2,172
  • 19
  • 26