Android: "Cannot convert from object to Album" (Album is a class..) - android

New to android here, (and programming in general)
I'm following this example trying to create a lazy image loading adapter: http://thinkandroid.wordpress.com/2012/06/13/lazy-loading-images-from-urls-to-listviews/
I'm using "Album" instead of Student, but the rest is pretty much the same.
When I call the following from my adapter:
Album getItem(int position) {
return items.get(position);
}
I get "Type mismatch: cannot convert from Object to Album". I tried copying the code from the example verbatim, and I got the same error..
Could somebody please explain what I am doing wrong, and how to fix it? Thank you.
Here is the code:
Album class:
package another.music.player;
import android.graphics.Bitmap;
import android.os.AsyncTask;
import android.util.Log;
public class Album {
private String album;
private String imageUri;
private Bitmap image;
private AlbumAdapter albumAdapter;
public Album(String album, String imageUri) {
this.album = album;
this.imageUri = imageUri;
// Default image
this.album = null;
}
public String getAlbum() {
return album;
}
public void setAlbum(String album) {
this.album = album;
}
public String getImageUri() {
return imageUri;
}
public void setImageUri(String imageUri) {
this.imageUri = imageUri;
}
public Bitmap getImage() {
return image;
}
public AlbumAdapter getAdapter() {
return albumAdapter;
}
public void setAdapter(AlbumAdapter albumAdapter) {
this.albumAdapter = albumAdapter;
}
public void loadImage(AlbumAdapter albumAdapter) {
// Hold a reference to the adapter
this.albumAdapter = albumAdapter;
if (imageUri != null && !imageUri.equals("")) {
new ImageLoadTask().execute(imageUri);
}
}
// Async task
private class ImageLoadTask extends AsyncTask<String, String, Bitmap> {
#Override
protected void onPreExecute() {
Log.i("ImageLoadTask", "Loading Image..");
}
// Param[0] is Image URI
protected Bitmap doInBackground(String... param) {
Log.i("ImageLoadTask", "Attempting to load image Uri: " + param[0]);
try {
Bitmap bitmap = ImageService.getBitmapFromURI(param[0]);
return bitmap;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
protected void onProgressUpdate(String... progress) {
// No Op
}
protected void onPostExecute(Bitmap ret) {
if (ret != null) {
Log.i("ImageLoadTask", "Successfully loaded" + album + " image");
image = ret;
if (albumAdapter != null) {
// When image is loaded, notify the adapter
albumAdapter.notifyDataSetChanged();
}
} else {
Log.e("ImageLoadTask", "Failed to load " + album + " image");
}
}
}
}
AlbumAdapter Class:
package another.music.player;
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
public class AlbumAdapter extends BaseAdapter {
private LayoutInflater mInflater;
private List items = new ArrayList();
public AlbumAdapter(Context context, List items) {
mInflater = LayoutInflater.from(context);
this.items = items;
}
public int getCount() {
return items.size();
}
public Album getItem(int position) {
return items.get(position);
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
Album album = items.get(position);
if (convertView == null) {
convertView = mInflater.inflate(R.layout.row_layout, null);
holder = new ViewHolder();
holder.name = (TextView) convertView.findViewById(R.id.name);
holder.image = (ImageView) convertView.findViewById(R.id.image);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.name.setText(album.getName());
if (s.getImage() != null) {
holder.pic.setImageBitmap(s.getImage());
} else {
// MY DEFAULT IMAGE
holder.pic.setImageResource(R.drawable.generic_profile_man);
}
return convertView;
}
static class ViewHolder {
TextView name;
ImageView pic;
}
}

You have a generic List, which will contain Object instances. That means any instance of the Object class or a class that extends Object (essentially all of them). When you attempt to get an item out of that list, the compiler knows that it's an Object, but your method signature is saying you're returning an Album; the compiler doesn't know that the element you're retrieving from the list definitely is an Album, so it can't compile the code.
You can solve this is one of two ways. You can declare your List to only contain instances of the Album class:
List<Album> items = new ArrayList<Album>();
Or you can cast the object you're retrieving from the list, to tell the compiler that you are definite that it's an Album even if it isn't:
Album getItem(int position) {
return (Album)items.get(position);
}

Try to make your list this way:
List<Album> items = new ArrayList<Album>();

You should use the specialized form of the List/ArrayList, or cast the result yourself.
Album getItem(int position) {
return (Album)items.get(position);
}
or better:
private List<Album> items = new ArrayList<Album>();

Related

GridView loads items only when clicked

So I'm trying to create a GridView that loads images from my server that's running Parse API.
The problem that I'm having is when I load the images from the server into the GridView, I have to click on the Fragment to load them onscreen.
I have no idea why this is happening.
This is my AsyncTask:
private class RemoteDataTask extends AsyncTask<Void, Void, Void> {
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected Void doInBackground(Void... params) {
thumbItemsList = new ArrayList<PostItems>();
final String userObjectID = ProfileActivity.objectID;
ParseQuery<ParseUser> query = ParseUser.getQuery();
query.getInBackground(userObjectID, new GetCallback<ParseUser>() {
#Override
public void done(ParseUser parseUser, ParseException e) {
if (e == null) {
try {
ParseQuery<ParseObject> postQuery = new ParseQuery<ParseObject>(
"Post");
postQuery.whereEqualTo("user", parseUser);
postQuery.orderByDescending("createdAt");
objectList = postQuery.find();
for (final ParseObject parseObject : objectList) {
thumbItems = new PostItems();
String first_name = parseObject.getString("first_name");
String last_name = parseObject.getString("last_name");
thumbItems.setName(first_name + " " + last_name);
ParseFile image = (ParseFile) parseObject.get("image");
image.getDataInBackground(new GetDataCallback() {
#Override
public void done(byte[] bytes, ParseException e) {
if (e == null) {
Bitmap userImage = BitmapFactory.decodeByteArray(bytes, 0
, bytes.length);
thumbItems.setImage(userImage);
} else {
Log.d("ParseException", "Error: " + e.getMessage());
e.printStackTrace();
}
}
});
thumbItemsList.add(thumbItems);
}
} catch (ParseException e1) {
e1.printStackTrace();
}
} else {
e.printStackTrace();
Log.i("ParseException", e.getMessage());
}
}
});
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
gridViewAdapter = new GridViewAdapter(getContext(), thumbItemsList);
gridView.setAdapter(gridViewAdapter);
swipeRefreshLayout.setRefreshing(false);
}
}
And this is my GridView adapter:
package co.eshg.limo4.adapter;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.List;
import co.eshg.limo4.PostActivity;
import co.eshg.limo4.R;
import co.eshg.limo4.data.PostItems;
import co.eshg.limo4.data.GridViewItem;
import co.eshg.limo4.data.ThumbnailItems;
public class GridViewAdapter extends BaseAdapter implements View.OnClickListener {
Context context;
LayoutInflater inflater;
private List<PostItems> thumbnailsItemsList = null;
private ArrayList<PostItems> arrayList;
public GridViewAdapter(Context context, List<PostItems> thumbnailItemsList) {
this.context = context;
this.thumbnailsItemsList = thumbnailItemsList;
inflater = LayoutInflater.from(context);
this.arrayList = new ArrayList<>();
this.arrayList.addAll(thumbnailItemsList);
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.imgThumb:
break;
}
}
public class ViewHolder {
GridViewItem gridViewItem;
}
#Override
public int getCount() {
return thumbnailsItemsList.size();
}
#Override
public Object getItem(int position) {
return thumbnailsItemsList.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(final int position, View view, ViewGroup parent) {
ViewHolder holder;
if (view == null) {
holder = new ViewHolder();
view = inflater.inflate(R.layout.profile_viewpager_tab1_children, null);
holder.gridViewItem = (GridViewItem) view.findViewById(R.id.imgThumb);
view.setTag(holder);
} else {
holder = (ViewHolder) view.getTag();
}
final Bitmap image = thumbnailsItemsList.get(position).getImage();
holder.gridViewItem.setImageBitmap(image);
holder.gridViewItem.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(context, PostActivity.class);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = stream.toByteArray();
intent.putExtra("PostImage", byteArray);
intent.putExtra("FullName", thumbnailsItemsList.get(position).getName());
context.startActivity(intent, null);
}
});
return view;
}
}
What I've tried so far is loading a local image from my drawable folder instead of the image from the server, and that works perfectly. I've done that by replacing this line from the adapter: final Bitmap image = thumbnailsItemsList.get(position).getImage(); with final Bitmap image = BitmapFactory.decodeResource(context.getResources(), R.drawable.dairy_back);
Any help would be appreciated.
image.getDataInBackground
this line of code will get data from server. It will take a while. But you are putting it on Thread. So it will execute this thumbItemsList.add(thumbItems) before you get the bitmap from the server. It's cause the gridview won't load the image because the array list still don't have the bitmap value but you are already add it on the adapter.
You should add thumbItems.setUrl(stringUrlImages) and move image.getDataInBackground into getView method

Android : Adapter is not working with Arraylist of Hashmap

I am trying to show some data offline from SQLite database. Data is coming fine but I am not able to show that on listview with adapter.
libraryList.clear();
libraryList = dBhelper.localQuestionBank(student_uuid, questionBankId);
if (libraryList.size() > 0) {
offSetFlag = libraryList.size();
libraryListView.setAdapter(questionBankAdapter);
questionBankAdapter.notifyDataSetChanged();
libraryListView.removeFooterView(loadMoreView);
loadingMore = false;
}
localQuestionBank method:
public ArrayList<Map<String, String>> localQuestionBank(String student_uuid, String question_bank_id) {
ArrayList<Map<String, String>> localQuestionBankList = new ArrayList<>();
ArrayList<Map<String, String>> questionBankList = new ArrayList<>();
String[] columns = new String[]{QUESTION_BANK_LABEL, AIP_UUID, QUESTION_SEQUENCE_NAME, QUESTION_MARKS, DESCRIPTION};
SQLiteDatabase db = mDbHelper.getWritableDatabase();
try {
Cursor cursor = db.query(LOCAL_QUESTION_BANK, columns, STUDENT_UUID+"=?" +" and "+ QUESTION_BANK_ID+"=?", new String[] { student_uuid, question_bank_id }, null, null, null);
if (cursor.moveToFirst()) {
do {
Map<String, String> subMap = new HashMap<String, String>();
subMap.put("aep_uuid", cursor.getString(cursor.getColumnIndex(AIP_UUID)));
subMap.put("question_marks", cursor.getString(cursor.getColumnIndex(QUESTION_MARKS)));
subMap.put("question_sequence_name", cursor.getString(cursor.getColumnIndex(QUESTION_SEQUENCE_NAME)));
subMap.put("description", cursor.getString(cursor.getColumnIndex(DESCRIPTION)));
subMap.put("question_bank_label", cursor.getString(cursor.getColumnIndex(QUESTION_BANK_LABEL)));
questionBankList.add(subMap);
} while (cursor.moveToNext());
localQuestionBankList.addAll(questionBankList);
questionBankList.clear();
}
cursor.close();
db.close();
} catch (SQLException sqlEx) {
sqlEx.printStackTrace();
}
return localQuestionBankList;
}
Listview and Adapter:
questionBankAdapter = new QuestionBankAdapter(getActivity(),libraryList);
libraryListView.setAdapter(questionBankAdapter);
questionBankAdapter:
package eukti.myafterclass.adapter;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.text.Html;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebView;
import android.widget.BaseAdapter;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.Map;
import eukti.myafterclass.R;
import eukti.myafterclass.ui.QuestionBankDetailsView;
import eukti.myafterclass.utils.InternetConnectionDetector;
public class QuestionBankAdapter extends BaseAdapter {
private final Activity context;
private ArrayList<Map<String, String>> dashboardList;
private static LayoutInflater inflater=null;
public QuestionBankAdapter(Activity context,
ArrayList<Map<String, String>> dashList) {
this.context = context;
this.dashboardList = dashList;
if (context!=null) {
inflater = (LayoutInflater) context.
getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
}
#Override
public int getCount() {
return dashboardList.size();
}
#Override
public Object getItem(int position) {
return dashboardList.size();
}
#Override
public long getItemId(int position) {
return position;
}
public class Holder
{
TextView sequence;
TextView marks;
WebView description;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
Holder holder=new Holder();
View rowView;
Log.d("LocalQBadapter:", dashboardList.get(position).get("question_sequence_name"));
rowView = inflater.inflate(R.layout.questionbank_items, null);
holder.sequence=(TextView) rowView.findViewById(R.id.questionSequenceName);
holder.marks=(TextView) rowView.findViewById(R.id.marks);
holder.description=(WebView) rowView.findViewById(R.id.description);
holder.description.getSettings().setJavaScriptEnabled(true);
String heading = dashboardList.get(position).get("question_sequence_name");
holder.sequence.setText(Html.fromHtml(heading));
holder.marks.setText("Marks : "+dashboardList.get(position).get("question_marks"));
String description = dashboardList.get(position).get("description");
String allAns = "<html><body>"+description.replace("\r\n", "<br/>")+"</body></html>";
holder.description.loadDataWithBaseURL(null, allAns, "text/html", "utf-8", null);
holder.description.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
return true;
}
});
holder.description.setLongClickable(false);
rowView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (!InternetConnectionDetector.isNetworkAvailable(context)) {
Toast.makeText(context, "Check your connection and try again.", Toast.LENGTH_LONG).show();
} else {
Intent redirect = new Intent(context, QuestionBankDetailsView.class);
redirect.putExtra("aep_uuid", dashboardList.get(position).get("aep_uuid"));
redirect.putExtra("question_sequence_name", dashboardList.get(position).get("question_sequence_name"));
redirect.putExtra("question_marks", dashboardList.get(position).get("question_marks"));
redirect.putExtra("description", dashboardList.get(position).get("description"));
context.startActivity(redirect);
}
}
});
return rowView;
}
}
It seems fine for me but data is not showing in Listview.
What you have typed
#Override
public Object getItem(int position) {
return dashboardList.size();
}
what i think it should be
#Override
public Object getItem(int position) {
return dashboardList.get(position);
}
try it and let me know
In your getItem() method you are returning the size. You need to give the actual object from the dataset dashboardList.
#Override
public Object getItem(int position) {
return dashboardList.get(position);
}
Also in the getView code you can call the getItem method instead of accessing the dataset directly.
Map<String, String> item = (Map<String, String>) getitem(position);
Bind your UI from this item object. Also try to use a Viewholder pattern in your Adapter.

Making ListView Scrolling Smooth position?

I read the article about "Making ListView Scrolling Smooth" on android.developer.com but there is one thing I don't understand : where to put or get the value of "position" because my eclipse says :"Cannot refer to a non-final variable position inside an inner class defined in a different method".
I put this code in my adapter class in the getView method.
package com.example.filemanager;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Formatter.BigDecimalLayoutForm;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.DateSorter;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
public class ItemAdapter extends ArrayAdapter<Item> {
private final Context context;
private final ArrayList<Item> values;
public int position;
public ItemAdapter(Context context, ArrayList<Item> values) {
super(context, R.layout.item, values);
this.context = context;
this.values = values;
}
#Override
public Item getItem(int position) {
// TODO Auto-generated method stub
return super.getItem(position);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rowView = inflater.inflate(R.layout.item, parent, false);
TextView title = (TextView) rowView.findViewById(R.id.title);
TextView info = (TextView) rowView.findViewById(R.id.info);
ImageView icon = (ImageView) rowView.findViewById(R.id.icon);
ImageView menu = (ImageView) rowView.findViewById(R.id.menu);
title.setText(values.get(position).getName());
info.setText(sizeString(position) + " " + dateString(position) + " "
+ rightsString(position));
if (values.get(position).isisDir()) {
icon.setImageResource(R.drawable.dir);
} else if (!values.get(position).isisDir()) {
if (values.get(position).getPath().endsWith(".jpg")
|| values.get(position).getPath().endsWith(".png")) {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 50;
options.inDensity = 100;
options.inScreenDensity = 1;
Bitmap myBitmap = BitmapFactory.decodeFile(values.get(position).getPath(), options);
icon.setImageBitmap(myBitmap);
}else{
icon.setImageResource(R.drawable.file);
}
}
menu.setImageResource(R.drawable.menu);
ViewHolder holder = new ViewHolder();
holder.icon = (ImageView) convertView.findViewById(R.id.icon);
holder.menu = (ImageView) convertView.findViewById(R.id.menu);
holder.title = (TextView) convertView.findViewById(R.id.title);
holder.info = (TextView) convertView.findViewById(R.id.info);
holder.progress = (ProgressBar) convertView.findViewById(R.id.progressSpinner);
convertView.setTag(holder);
// Using an AsyncTask to load the slow images in a background thread
new AsyncTask<ViewHolder, Void, Bitmap>() {
private ViewHolder v;
#Override
protected Bitmap doInBackground(ViewHolder... params) {
v = params[0];
//return mFakeImageLoader.getImage();
return null;
}
#Override
protected void onPostExecute(Bitmap result) {
super.onPostExecute(result);
if (v.position == position) {
// If this item hasn't been recycled already, hide the
// progress and set and show the image
v.progress.setVisibility(View.GONE);
v.icon.setVisibility(View.VISIBLE);
v.icon.setImageBitmap(result);
}
}
}.execute(holder);
return rowView;
}
private String rightsString(int position) {
String temp = "";
if (values.get(position).isReadable()) {
temp += "r";
} else {
temp += "-";
}
if (values.get(position).isWritable()) {
temp += "w";
} else {
temp += "-";
}
if (values.get(position).isExecutable()) {
temp += "x";
} else {
temp += "-";
}
return temp;
}
private String dateString(int position) {
DateFormat df = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
return df.format(values.get(position).getDate());
}
private String sizeString(int position) {
long bytes = values.get(position).getSize();
double kb = bytes / 1024.0;
double mb = kb / 1024.0;
double gb = mb / 1024.0;
if (gb >= 1) {
return gb + " GB";
}
if (mb >= 1) {
return mb + " MB";
}
if (kb >= 1) {
return kb + " KB";
} else {
return bytes + " bytes";
}
}
static class ViewHolder {
ImageView icon, menu;
TextView title, info;
ProgressBar progress;
int position;
}
}
Okay, so you need to understand a little bit about outer,inner and anonymous classes,etc. What's going on is that you are creating an anonymous class with the declaration of
new AsyncTask()....
and any variables inside of that class need to be final or not referencing the parent class.
So, what you need to do is this:
final int finalPosition = position;
// Using an AsyncTask to load the slow images in a background thread
new AsyncTask() {
private ViewHolder v;
#Override
protected Bitmap doInBackground(ViewHolder... params) {
v = params[0];
return null;
}
#Override
protected void onPostExecute(Bitmap result) {
super.onPostExecute(result);
if (v.position == finalPosition) {
// If this item hasn't been recycled already, hide the
// progress and set and show the image
v.progress.setVisibility(View.GONE);
v.icon.setVisibility(View.VISIBLE);
v.icon.setImageBitmap(result);
}
}
}.execute(holder);
Reference

ListView with Facebook ProfilePictureView widget cause lag

I've done a custom adapter for a listview containing a ProfilePictureView element from facebook SDK 3.5. Here the problem: there is a lot of lag while scrolling due to the loading profile image (if I remove it it's much less) although it's loaded asynchronously.
How can I solve this problem? below my custom adapter.
public class UserAdapter extends ArrayAdapter<User> {
/** Contacts list */
private List<User> Users;
/** Context */
private Activity Ctx;
public UserAdapter(Context context, int textViewResourceId, List<User> users) {
super(context, textViewResourceId, users);
this.Ctx = (Activity) context;
this.Users = users;
}
#Override
public View getView(int position, View v, ViewGroup parent) {
// Keeps reference to avoid future findViewById()
UsersViewHolder viewHolder;
if (v == null) {
LayoutInflater li = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = li.inflate(R.layout.row_friend, parent, false);
viewHolder = new UsersViewHolder();
viewHolder.mUserName = (CustomTextView) v.findViewById(R.id.friendName);
viewHolder.mUserDescription = (CustomTextView) v.findViewById(R.id.friendDescription);
viewHolder.mProfilePicture = (ProfilePictureView) v.findViewById(R.id.userPicture);
v.setTag(viewHolder);
} else {
viewHolder = (UsersViewHolder) v.getTag();
}
User mUser = Users.get(position);
if (mUser != null) {
viewHolder.mUserName.setText(""+mUser.getName()+" "+mUser.getLastName());
viewHolder.mUserDescription.setText(mUser.getEmail());
viewHolder.mProfilePicture.setProfileId(mUser.getIdFB()); //problem
viewHolder.mProfilePicture.setCropped(true); //problem
}
return v;
}
static class UsersViewHolder {
LinearLayout mLayout;
ProfilePictureView mProfilePicture;
CustomTextView mUserName;
CustomTextView mUserDescription;
}
}
I got the same problem, so I decided to use an ImageView and download directly the image from facebook api. Here is the code to do so:
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.ImageView;
public class DownloadImage extends AsyncTask<String, Void, Bitmap> {
private ImageView layoutimage;
private Bitmap bitmap;
private boolean malformedUrl;
public DownloadImage(ImageView Image) {
this.layoutimage = Image;
}
public Bitmap getBitmap() {
return bitmap;
}
#Override
protected void onPreExecute() {
}
#Override
protected Bitmap doInBackground(String... Image_URL) {
String url = Image_URL[0];
try {
// Handle redirect
URL obj = new URL(url);
HttpURLConnection conn = (HttpURLConnection) obj.openConnection();
int status = conn.getResponseCode();
if (status != HttpURLConnection.HTTP_OK) {
if (status == HttpURLConnection.HTTP_MOVED_TEMP
|| status == HttpURLConnection.HTTP_MOVED_PERM
|| status == HttpURLConnection.HTTP_SEE_OTHER)
url = conn.getHeaderField("Location");
}
// Get bitmap from URL
Bitmap bitmap = null;
try {
// Download Image from URL
InputStream input = new java.net.URL(url)
.openStream();
bitmap = BitmapFactory.decodeStream(input);
} catch (Exception e) {
// Error Log
Log.e("Error", e.getMessage());
e.printStackTrace();
}
return bitmap;
} catch (MalformedURLException e) {
malformedUrl = true;
return null;
} catch (IOException e) {
malformedUrl = true;
return null;
}
}
#Override
protected void onPostExecute(Bitmap result) {
// Set image into image.xml layout
if (layoutimage != null)
layoutimage.setImageBitmap(result);
bitmap = result;
}
}
And then you just call the class with the facebook id, like this:
DownloadImage di = new DownloadImage(category.findViewById(R.id.your_image_view));
di.execute("http://graph.facebook.com/" + FACEBOOK_ID + "/picture?type=small);

Slow scrolling when populating gridview of images with AsyncTask

good day, i am having a bit of a problem here. i am using and async task to display images from external or internal storage.Now it works but the problem is, it is very slow and slightly jerky in scrolling. I have no idea why? please any solution or an idea how to do this.
import java.io.IOException;
import java.util.ArrayList;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Debug;
import android.os.Environment;
import android.provider.MediaStore;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.Toast;
import android.widget.AdapterView.OnItemClickListener;
public class Wallpaper extends Activity implements OnItemClickListener{
/*Instance variables*/
private GridView grid;
private ImageAdapter imageAdapter;
private Display display;
Cursor cursor;
boolean inInternalStorage = false;
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
setContentView(R.layout.wallpaper_images);
display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
setupViews();
setProgressBarIndeterminateVisibility(true);
loadImages();
}
#Override
protected void onStop(){
super.onStop();
}
/*methods called from AsyncTask as appropriate when its querying and processing the images from the MediaStore*/
private void loadImages() {
final Object data = getLastNonConfigurationInstance();
if(data == null){
new LoadImagesFromSDCard().execute();
}else {
final LoadedImage[] photos = (LoadedImage[])data;
if(photos.length == 0){
new LoadImagesFromSDCard().execute();
}
for(LoadedImage photo:photos){
addImage(photo);
}
}
}
private void addImage(LoadedImage... value) {
for(LoadedImage photo: value){
imageAdapter.addPhotos(photo);
imageAdapter.notifyDataSetChanged();
}
}
private void setupViews() {
grid = (GridView)findViewById(R.id.gridview);
grid.setNumColumns(display.getWidth()/95);
grid.setClipToPadding(false);
imageAdapter = new ImageAdapter(getApplicationContext());
grid.setAdapter(imageAdapter);
grid.setOnItemClickListener(this);
}
protected void onDestroy() {
super.onDestroy();
final GridView gridview = grid;
final int count = grid.getChildCount();
ImageView v = null;
for (int i = 0; i < count; i++) {
v = (ImageView) grid.getChildAt(i);
((BitmapDrawable) v.getDrawable()).setCallback(null);
}
unbindDrawables(findViewById(R.id.gridview));
System.gc();
}
/*public Object onRetainNonConfigurationInstance(){
final GridView gridview = grid;
final int count = grid.getChildCount();
final LoadedImage[] list = new LoadedImage[count];
for(int i = 0; i < count; i++){
final ImageView v = (ImageView)grid.getChildAt(i);
list[i] = new LoadedImage(((BitmapDrawable) v.getDrawable()).getBitmap());
}
return list;
}*/
/*utility method called that prevents screen from crashing when screen orientation changes*/
private void unbindDrawables(View view){
if(view.getBackground() != null){
view.getBackground().setCallback(null);
}
if(view instanceof ViewGroup){
for(int i = 0; i < ((ViewGroup) view).getChildCount(); i++){
unbindDrawables(((ViewGroup)view).getChildAt(i));
}
try{
((ViewGroup)view).removeAllViews();
}catch(Exception e){
e.printStackTrace();
}
}
}
/*AsyncTask thats queries the MediaStore for images and creates thumbnail images from bitmaps*/
class LoadImagesFromSDCard extends AsyncTask<Object, LoadedImage, Object> {
#Override
protected Object doInBackground(Object... params) {
Cursor cursor;
Bitmap bitmap = null;
Bitmap newbitmap = null;
Uri uri = null;
String [] img = {MediaStore.Images.Media._ID};
String state = Environment.getExternalStorageState();
if(Environment.MEDIA_MOUNTED.equals(state)){
cursor = getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, img,
MediaStore.Images.Media.DATA + " like ? " , new String[]{ "%dcim%"}, null);
} else {
cursor = getContentResolver().query(MediaStore.Images.Media.INTERNAL_CONTENT_URI, img, null, null, null);
inInternalStorage = true;
}
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID);
int size = cursor.getCount();
if(size == 0){
Toast.makeText(getApplicationContext(), "There are no Images on the sdcard", Toast.LENGTH_SHORT).show();
}else {
}
for(int i = 0; i < size; i++){
cursor.moveToPosition(i);
int ImageId = cursor.getInt(column_index);
if(inInternalStorage == true){
uri = Uri.withAppendedPath(MediaStore.Images.Media.INTERNAL_CONTENT_URI, "" + ImageId);
}else {
uri = Uri.withAppendedPath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "" + ImageId);
}
try {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize=4;
bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri), null, options);
if(bitmap != null){
newbitmap = Bitmap.createScaledBitmap(bitmap, 100, 100, true);
bitmap.recycle();
}
if(newbitmap != null){
publishProgress(new LoadedImage(newbitmap));
}
}catch(IOException e){
}
}
cursor.close();
return null;
}
#Override
public void onProgressUpdate(LoadedImage... value){
addImage(value);
}
#Override
protected void onPostExecute(Object result) {
setProgressBarIndeterminateVisibility(false);
}
}
private static class LoadedImage {
Bitmap mBitmap;
LoadedImage(Bitmap bitmap) {
mBitmap = bitmap;
}
public Bitmap getBitmap() {
return mBitmap;
}
}
/*Image Adapter to populate grid view of images*/
public class ImageAdapter extends BaseAdapter {
private Context mContext;
private ArrayList<LoadedImage> photos = new ArrayList<LoadedImage>();
public ImageAdapter(Context context){
this.mContext = context;
}
public void addPhotos(LoadedImage photo){
photos.add(photo);
}
#Override
public int getCount() {
return photos.size();
}
#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) {
System.gc();
ImageView image;
ViewHolder holder;
if(convertView == null){
LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.image_list,null);
holder = new ViewHolder();
holder.image = (ImageView)convertView.findViewById(R.id.image_list_id);
convertView.setTag(holder);
} else{
holder = (ViewHolder)convertView.getTag();
}
holder.image.setLayoutParams(new GridView.LayoutParams(100, 100));
holder.image.setImageBitmap(photos.get(position).getBitmap());
return convertView;
}
}
static class ViewHolder {
ImageView image;
}
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Cursor cursor = null;
int image_column_index = 0;
String[] proj = {MediaStore.Images.Media.DATA};
String state = Environment.getExternalStorageState();
if(Environment.MEDIA_MOUNTED.equals(state)){
cursor = managedQuery(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,proj, MediaStore.Images.Media.DATA + " like ? ", new String[]{"%dcim%"},null);
}else{
cursor = managedQuery(MediaStore.Images.Media.INTERNAL_CONTENT_URI,proj,null, null,null);
}
cursor.moveToPosition(position);
image_column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
String info = cursor.getString(image_column_index);
Intent imageviewer = new Intent(getApplicationContext(), ViewImage.class);
imageviewer.putExtra("pic_name", info);
startActivity(imageviewer);
}
}
i think what i am after is a way to allow doBackground() to run for a while before i call publishProgress(). just like how the gallery app in android does.
Any ideas on why this could be slow or how to solve this problem will be greatly appreciated as i have been stuck on how to improve the perfomance for a while now.
The biggest bottleneck will be accessing the images from the device (internal or external). Therefore, you'll want to have as many images in memory as possible (within reason of course). You can do this in a number of ways:
Load perhaps 18 images first (as you said, prebuffer before displaying). Check to make sure the number of images doesn't exceed whatever number you choose though.
Create a 'thumbs.db' type file which will store 100x100 px thumbnails of the bitmaps. This will allow much faster reading since you'd only need to read in one file then extract each bitmap. If the user clicks an image, then request it from the storage. This method requires more work, but will be able to load the thumbnails really fast. You'd probably have to design your own simple file headers, such as:
<file header (size of file, number of images)>
<image header (img id, size in bytes)>
<image bitmap data>
<image header (img id, size in bytes)>
<image bitmap data>
<image header (img id, size in bytes)>
<image bitmap data>
...

Categories

Resources