I implemented my ListView using custom adapter extended from ArrayAdapter.
My problem is sometimes ListView is loaded slowly. That means a blank activity is loaded first without the ListView, then the ListView comes out. At worst case, I am prompted to "force closed or wait". I like to improve that slow loading as it is annoying to the user.
But sometimes, loading is fast and almost immediate.
But I like to make sure my ListView design is correct and the design does not have any problem with that slow loading. So that this discussion will be useful for other people who are facing the same problem as mine.
My ListView is designed as follow.
Each ListItem has three components, thumbnail image, ID text, and arrow image as shown in the figure attached .
In loading process of the ListView, (1)All ID text are retrieved from the database and populated into a ListArray List<String> listIDs
public class MyListFragment extends ListFragment implements OnItemClickListener {
dbHelper = new TrackerDBAdapter(getActivity());
dbHelpLatLong = new LatLogDBAdapter(getActivity());
dbHelpNotification = new NotificationDatabaseAdapter(getActivity());
dbHelper.open();
Cursor cursor = dbHelper.fetchAllTrackerInTheList();
listIDs = new ArrayList<String>();
activationStatus = new ArrayList<String>();
thisListFragmentContext = getActivity();
for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
listIDs.add(cursor.getString(1));
}
dbHelper.close();
(2)Then my custom list adapter is called.
adapter = new customList_Adaptor(thisListFragmentContext,
R.layout.list_row, listIDs, this);
}
That is the loading process inside my `ListFragment`.
(3) The following class is my custom ArrayAdapter and I implemented to load thumbnail ImageView using AsyncTask. My query are
(i)I still have retrieving ID text from database, and loading arrow image. Should I put those processes into AsyncTask as well?
(ii)If I need it, should I implement another AsyncTask or use the same AsyncTask used for thumbnail image loading?
(iii)Among these, which aspect of the program design I still can improve that is suspicious to my slow loading?
public class customList_Adaptor extends ArrayAdapter<String>{
protected static final int CAMERA_REQUEST = 0;
private TrackerDBAdapter dbHelper;
private Context context;
private List<String> listIDs = new ArrayList<String>();
private List<String> activationState = new ArrayList<String>();
public MyListFragment mMyListFragment;
public customList_Adaptor(Context context, int textViewResourceId,
List<String> objects, List<String> activationStatus, MyListFragment mMyListFragment) {
super(context, textViewResourceId, objects);
this.setContext(context);
this.listIDs = objects;
this.activationState = activationStatus;
this.mMyListFragment= mMyListFragment;
dbHelper = new TrackerDBAdapter(context);
}
#Override
public int getCount() {
// TODO Auto-generated method stub
if(listIDs != null)
return listIDs.size();
else
return 0;
}
#Override
public String getItem(int arg0) {
// TODO Auto-generated method stub
if(listIDs != null)
return listIDs.get(arg0);
else
return null;
}
#Override
public long getItemId(int arg0) {
// TODO Auto-generated method stub
return arg0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View vi=convertView;
ViewHolder viewHolder=new ViewHolder();
LayoutInflater inflater = (LayoutInflater) this.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if(vi==null){
vi = inflater.inflate(R.layout.list_row, parent, false);
viewHolder.id=(TextView)vi.findViewById(R.id.title);
viewHolder.thumbnailImage=(ImageView)vi.findViewById(R.id.list_image);
viewHolder.activationStatus = (TextView)vi.findViewById(R.id.activated);
//lazy load image
BitmapWorkerTask task = new BitmapWorkerTask(viewHolder.thumbnailImage);
task.execute(position);
viewHolder.arrow=(ImageView)vi.findViewById(R.id.list_arrow);
vi.setTag(viewHolder);
}
else
viewHolder=(ViewHolder) vi.getTag();
viewHolder.thumbnailImage.setOnClickListener(new onMyClick(position));
// Setting all values in listview
viewHolder.id.setText(listIDs.get(position));
if(activationState.get(position).equals("Not activated yet")){
viewHolder.activationStatus.setText(activationState.get(position));
viewHolder.activationStatus.setTextColor(android.graphics.Color.GRAY);
}
else if(activationState.get(position).equals("Activated"))
viewHolder.activationStatus.setText("");
return vi;
}
public class onMyClick implements OnClickListener {
private final int pos;
public onMyClick(int pos) {
this.pos = pos;
}
#Override
public void onClick(View v) {
MyListFragment.clickedimageView = (ImageView) v.findViewById(R.id.list_image);
mMyListFragment.imagepos(pos);
}
}
public Context getContext() {
return context;
}
public void setContext(Context context) {
this.context = context;
}
//Lazy image update
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
private final WeakReference<ImageView> imageViewReference;
private int data = 0;
public BitmapWorkerTask(ImageView imageView) {
// Use a WeakReference to ensure the ImageView can be garbage collected
imageViewReference = new WeakReference<ImageView>(imageView);
}
// Decode image in background.
#Override
protected Bitmap doInBackground(Integer... params) {
setData(params[0]);
Bitmap bitmap = null;
dbHelper.open();
Cursor mCursor = dbHelper.getImagebyIDnumber(getData());
byte[] img_bytes = mCursor.getBlob(13);
bitmap = BitmapFactory.decodeByteArray(img_bytes, 0, img_bytes.length);
dbHelper.close();
return bitmap;
}
// Once complete, see if ImageView is still around and set bitmap.
#Override
protected void onPostExecute(Bitmap bitmap) {
if (imageViewReference != null && bitmap != null) {
final ImageView imageView = imageViewReference.get();
if (imageView != null) {
imageView.setImageBitmap(bitmap);
}
}
}
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
}
}
public class ViewHolder {
TextView id;
TextView activationStatus;
ImageView thumbnailImage;
ImageView arrow;
}
I did a few things to make it faster in loading the app.
I am not sure which one is the solution.
(1) I load all data from sql database including text and thumbnail images using AsyncTask.
(2) I change thumbnail image format from png to jpg.
(3) Then I clear the cache manually.
The app looks like faster in loading, but sometimes it is still slow. Most of the times, it is faster than before.
I am still making improvement to my app.
Thanks
Related
I'm trying to build a list with an image that is taken from the device and a text. It turns out that taking images from the phone that was from the phone's camera is a task that takes a while so I'm trying to make it as fast as possible so the user experience won't get slower. All I got from this is that it looks like all the images are loaded in one ImageView and than the images spread to all the other ImageViews (I'm not completely sure that my implementation of the ViewHolder technique and Custom CursorAdapter is correct).
public class MyCustomCurserAdapter extends CursorAdapter {
static class ViewHolder {
public TextView nameText;
public ImageView imageThumbnail;
}
Cursor cursor;
public MyCustomCurserAdapter(Context context, Cursor c, int flags) {
super(context, c, flags);
// TODO Auto-generated constructor stub
}
#Override
public void bindView(View view, Context arg1, Cursor cursor) {
ViewHolder holder = (ViewHolder)view.getTag();
int pathCol = cursor.getColumnIndex(NewPicSQLiteHelper.COLUMN_PATH);
String imageInSD = cursor.getString(pathCol);
File imgFile = new File(imageInSD);
if(imgFile.exists()){
int nameCol = cursor.getColumnIndex(NewPicSQLiteHelper.COLUMN_PIC_NAME);
String name = cursor.getString(nameCol);
if (name != null)
holder.nameText.setText(name);
ImageTask task = new ImageTask(holder.imageThumbnail);
task.execute(imgFile);
}
}
#Override
public View newView(Context arg0, Cursor cur, ViewGroup parent) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View view = inflater.inflate(R.layout.new_pic_item, parent, false);
ViewHolder holder = new ViewHolder();
holder = new ViewHolder();
holder.nameText = (TextView) view.findViewById(R.id.pic_name_entry);
holder.imageThumbnail = (ImageView) view.findViewById(R.id.pic_thumbnail);
// The tag can be any Object, this just happens to be the ViewHolder
view.setTag(holder);
return view;
}
private class ImageTask extends AsyncTask<File, Void, Bitmap>{
private final WeakReference <ImageView> imageViewReference;
public ImageTask(ImageView imageView) {
imageViewReference = new WeakReference <ImageView> (imageView);
}
#Override
protected Bitmap doInBackground(File... params) {
String path = params[0].getAbsolutePath();
return decodeSampledBitmapFromResource(path,75,75);
}
#Override
protected void onPostExecute(Bitmap result) {
if (imageViewReference != null) {
ImageView imageView = imageViewReference.get();
if (imageView != null) {
if (result != null) {
imageView.setImageBitmap(result);
imageView.setVisibility(ImageView.VISIBLE);
} else {
imageView.setVisibility(ImageView.INVISIBLE);
}
}
}
}
private Bitmap decodeSampledBitmapFromResource(String orgImagePath, int reqWidth, int reqHeight) {
}
private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
}
}
I think probable reason thats its taking time is because the images would be of at least 1 mb size further you can change to thumbnail and retrieve it and also if still taking time you could put lazy downloading which is done when we take image from the server(Basically what it does is it loads the text and shows image when we get the image)
Remove ImageTask AsyncTask ..
Use a Libraries like Glide or Picasso . Very effective for almost any case where you need to fetch, resize, and display a remote image.
I used a glide for loading images from phone storage uri
Use any of the above and see the difference
I want to load images from a server, AFTER loading the data in a list view.
I know that a lot of topics existing for this problem but I haven't found the solution...
So this is my code :
//asyncTackClass for loadingpictures
public class LoadImagesThread extends AsyncTask<Bundle, Void, Bitmap> {
private ImageView view;
private Bitmap bm;
private Context context;
private final WeakReference<ImageView> imageViewReference;
private final String BUNDLE_URL = "url";
private final String BUNDLE_NAME = "name";
private final String BUNDLE_BM = "bm";
public LoadImagesThread(Context context, ImageView view) {
this.context=context;
imageViewReference = new WeakReference<ImageView>(view);
}
#Override
protected Bitmap doInBackground(Bundle... b) {
Bitmap bm =null;
if (StorageHelper.getBitmap(b[0].getString(BUNDLE_NAME)) != null) { // Check the sdcard
bm = StorageHelper.getBitmap(b[0].getString(BUNDLE_NAME));
Log.w("LoadImagesThread", "Get image from sdcard : "+b[0].getString(BUNDLE_NAME));
} else { // Check the server
bm = ServiceHelper.getBitmapFromURL(b[0].getString(BUNDLE_URL));
StorageHelper.saveBitmap(bm, b[0].getString(BUNDLE_NAME)); // Save image on sdcard
Log.w("LoadImagesThread", "Get image from server : "+b[0].getString(BUNDLE_NAME));
}
return bm;
}
#Override
protected void onPostExecute(final Bitmap bm) {
super.onPostExecute(bm);
if (bm != null){ //if bitmap exists...
view = imageViewReference.get();
// Fade out
Animation fadeOutAnimation = AnimationUtils.loadAnimation(context, R.anim.fadeoutimage);
fadeOutAnimation.setAnimationListener(new AnimationListener() {
public void onAnimationStart(Animation animation) {
}
public void onAnimationRepeat(Animation animation) {
}
public void onAnimationEnd(Animation animation) {
// Fade in
view.setImageBitmap(bm);
Animation fadeInAnimation = AnimationUtils.loadAnimation(context, R.anim.fadeinimage);
view.startAnimation(fadeInAnimation);
}
});
// Launch the fadeout
view.startAnimation(fadeOutAnimation);
}else{ //if not picture, display the default ressource
view.setImageResource(R.drawable.productcarre);
}
}
}
The code is used to display a Bitmap in a ImageView
And this is the adapter:
public class ListViewShoplistStoresAdapter extends BaseAdapter {
private ArrayList<Shop> shopList;
private Activity activity;
private HashMap<Integer, ImageView> views;
private final String BUNDLE_URL = "url";
private final String BUNDLE_NAME = "name";
private final String BUNDLE_POS = "pos";
private final String BUNDLE_ID = "id";
public ListViewShoplistStoresAdapter(Activity activity, ArrayList<Shop> shopList) {
super();
this.activity = activity;
this.shopList = shopList;
this.views = new HashMap<Integer, ImageView>();
}
public int getCount() {
return shopList.size();
}
public Object getItem(int position) {
return shopList.get(position);
}
public long getItemId(int position) {
return shopList.get(position).getId();
}
private class ViewHolder {
public TextView store;
public TextView name;
public ImageView ImageStore;
}
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder view;
LayoutInflater inflator = activity.getLayoutInflater();
if(convertView == null) {
view = new ViewHolder();
convertView = inflator.inflate(R.layout.listviewshops, null);
view.store = (TextView) convertView.findViewById(R.id.store);
view.name = (TextView) convertView.findViewById(R.id.name);
view.ImageStore = (ImageView) convertView.findViewById(R.id.imgstore);
convertView.setTag(view);
}else {
view = (ViewHolder) convertView.getTag();
}
Typeface regular=Typeface.createFromAsset(activity.getAssets(), "fonts/RobotoRegular.ttf");
view.store.setTypeface(regular);
Typeface light=Typeface.createFromAsset(activity.getAssets(), "fonts/RobotoLight.ttf");
view.store.setTypeface(light);
Brand brand = StorageHelper.getBrand(activity, shopList.get(position).getBrandId());
if (brand == null) {
Log.e("SetShopInAdapter","Brand null");
Toast.makeText(activity, "Impossible d'afficher la liste de magasins", Toast.LENGTH_LONG).show();
} else {
view.store.setText(brand.getName());
view.name.setText(shopList.get(position).getName());
view.ImageStore.setImageResource(R.drawable.productcarre);
}
Bundle b = new Bundle();
//url of the pict
b.putString(BUNDLE_URL, ServiceHelper.getImageUrl("brand", brand.getName()));
// name of image
b.putString(BUNDLE_NAME, ServiceHelper.getCleanImageName(brand.getName()));
//position in the listView
b.putInt(BUNDLE_POS, position);
//id of the current object
b.putInt(BUNDLE_ID, brand.getId());
//put info in the map in order to display in the onPostExecute
if(views.get(position)==null){
views.put(position, view.ImageStore);
// launch thread
new LoadImagesThread(activity.getApplicationContext(), view.ImageStore).execute(b);
}
return convertView;
}
}
So, when I used a GridView, there were no problems, but when I use a ListView the image is changed only in the first item !
Example:
I want to display product images for "car", "house" and "apple" items.
The code will launch the thread and all images (car then house and finally apple) will be displayed in the first item (the car item)...
And the house and the apple while not have images !!
Do you know what I should do ?
Thanks
There is a lot about this here on SO..
asynchrnous loading like that is called "Lazy Loading"
https://github.com/thest1/LazyList
for full implementation of such a process with list
For loading images in general i recomend this :
https://github.com/nostra13/Android-Universal-Image-Loader
You can use volley to improve in listview performance.
Take a look at this simple example: Android Custom ListView with Image and Text using Volley
I read a lot of threads here and elsewhere about loading images in custom listviews but I got nowhere! So, I thought I'd better ask!
The sittuation is as follows:
I have an activity that has a listview on it with 6 items (lets say Item1, Item2 etc). When the user clicks on an item I launch a new activity with another listview but this time the list view is a custom one with an image and some text.
The first activity's onCreate method is this:
GetImageFromURL eventImageLoader = new GetImageFromURL(FirstActivity.this, eventMultimediaID);
eventImageLoader.execute();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
CustomAdapter adapter = new CustomAdapter(this, android.R.layout.simple_expandable_list_item_1, R.id.textView1, eventName, bitmap);
eventList = (ListView) findViewById(R.id.secEventList);
eventList.setAdapter(adapter);
eventList.setOnItemClickListener(eventNameListClickListener);
I create and start an AsyncTask to load the images into a bitmap List.
eventMultimediaID is a String array and so is eventName.
The GetImageFromURL Task is this:
public class GetImageFromURL extends AsyncTask<Void, Void, List<Bitmap>>
{
private ProgressDialog progressDialog = null;
private Context callingContext = null;
private List<Bitmap> bitmap = new ArrayList<Bitmap>();
private String[] eventMultimediaID = null;
public GetImageFromURL(Context callingContext, String[] eventMultimediaID)
{
this.callingContext = callingContext;
this.eventMultimediaID = eventMultimediaID;
}
#Override
protected void onPreExecute()
{
super.onPreExecute();
progressDialog = ProgressDialog.show(callingContext, "Loading Events", "Retrieving data from server, please wait...", true);
}
#Override
protected List<Bitmap> doInBackground(Void... params)
{
try
{
int length = eventMultimediaID.length;
for(int i = 0; i < length; i++)
{
URL imageUrl = new URL("https://www.blahblah/servlet?blahblah=" + eventMultimediaID[i]);
bitmap.add(i, BitmapFactory.decodeStream(imageUrl.openConnection().getInputStream()));
}
}
catch (IOException e)
{
e.printStackTrace();
}
return bitmap;
}
#Override
protected void onPostExecute(List<Bitmap> bitmap)
{
for(int i = 0; i < bitmap.size(); i++)
{
ShowEventCategory.bitmap.add(i, bitmap.get(i));
}
progressDialog.dismiss();
}
}
and this is the CustomAdapter:
public class CustomAdapter extends ArrayAdapter<String>
{
private Context currentContext;
private String[] data;
private List<Bitmap> evImage;
public CustomAdapter(Context context, int resource, int textViewResourceId, String[] objects, List<Bitmap> bitmap)
{
super(context, resource, textViewResourceId, objects);
this.currentContext = context;
this.data = objects;
this.evImage = bitmap;
}
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
LayoutInflater inflater = (LayoutInflater) currentContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View eventListItem = inflater.inflate(R.layout.show_event_category_list_item, parent, false);
ImageView eventListItemImage = (ImageView) eventListItem.findViewById(R.id.eventImage);
TextView eventListItemText = (TextView) eventListItem.findViewById(R.id.eventText);
eventListItemText.setText(data[position]);
eventListItemImage.setImageBitmap(evImage.get(position));
return eventListItem;
}
}
What I tried to do was to call the async task from the Activity to load the images and while doing so display a loading dialog. After the loading is complete the dialog is dismissed and the adapter takes the filled Bitmap array.
This doesn't work. After the Task is started the execution goes on (I think thats what SHOULD happen) and the adapter gets an empty array so I get an OutOfBoundsException. That's why I placed Thread.sleep(5000) in the main thread to wait for the load operation to complete. But of course that's not an acceptable solution!
:( Any suggetions?! I've spent all day on that thing!!!!
Thanx a lot in advance!
I have one ListView which can hold an image. It depends if image exists or not in SDCARD.
Here my example code:
public class MainActivity extends Activity {
ListView mListView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mListView = new ListView(this);
setContentView(mListView);
String[] arr = new String[] {
"/example/images/1.jpg", "/example/images/2.jpg",
"/example/images/3.jpg", "/example/images/4.jpg",
"/example/images/5.jpg", "/example/images/6.jpg",
"/example/images/7.jpg", "/example/images/8.jpg",
"/example/images/9.jpg", "/example/images/1.jpg",
"/example/images/2.jpg", "/example/images/3.jpg",
"/example/images/4.jpg", "/example/images/5.jpg",
"/example/images/6.jpg", "/example/images/7.jpg",
"/example/images/8.jpg", "/example/images/9.jpg",
"/example/images/1.jpg", "/example/images/2.jpg",
"/example/images/3.jpg", "/example/images/4.jpg",
"/example/images/5.jpg", "/example/images/6.jpg",
"/example/images/7.jpg", "/example/images/8.jpg",
"/example/images/9.jpg", "/example/images/1.jpg",
"/example/images/2.jpg", "/example/images/3.jpg",
"/example/images/4.jpg", "/example/images/5.jpg",
"/example/images/6.jpg", "/example/images/7.jpg",
"/example/images/8.jpg", "/example/images/9.jpg"};
List<String> list = Arrays.asList(arr);
MyAdapter adapter = new MyAdapter(this, R.layout.listitem_imv, list);
mListView.setAdapter(adapter);
}
class MyAdapter extends ArrayAdapter<String>{
List<String> mList;
LayoutInflater mInflater;
int mResource;
public MyAdapter(Context context, int resource,
List<String> objects) {
super(context, resource, objects);
mResource = resource;
mInflater = getLayoutInflater();
mList = objects;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view;
if(convertView == null){
view = mInflater.inflate(mResource, null);
}else{
view = convertView;
}
ImageView imageView = (ImageView) view.findViewById(R.id.imv);
TextView textView = (TextView) view.findViewById(R.id.txv);
imageView.setTag(mList.get(position));//tag of imageView == path to image
new LoadImage().execute(imageView);
textView.setText(mList.get(position).toString());
return view;
}
}
class LoadImage extends AsyncTask<Object, Void, Bitmap>{
private ImageView imv;
private String path;
#Override
protected Bitmap doInBackground(Object... params) {
imv = (ImageView) params[0];
path = imv.getTag().toString();
Bitmap bitmap = null;
File file = new File(
Environment.getExternalStorageDirectory().getAbsolutePath() + path);
if(file.exists()){
bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
}
return bitmap;
}
#Override
protected void onPostExecute(Bitmap result) {
if(result != null && imv != null){
imv.setVisibility(View.VISIBLE);
imv.setImageBitmap(result);
}else{
imv.setVisibility(View.GONE);
}
}
}
}
The 'sdcard/example/images' directory has the images: 1.jpg, 2.jpg, 3.jpg, 4.jpg, 6.jpg, 7.jpg and 9.jpg.
the expected result is:
But, if I scroll the list quickly, some images are inserted in the wrong items.
It happens due to use of convertView in getView() method.
If I use the following code, the code works fine:
//if(convertView == null){
// view = mInflater.inflate(mResource, null);
//}else{
// view = convertView;
//}
view = mInflater.inflate(mResource, null);
When list scrolled quickly, two asyncTasks can reference one same View, due to use of convertView.
How Can I cancel AsyncTask when the View is no longer visible?(and is useb by another item of ListView)
edit
#Override
protected void onPostExecute(Bitmap result) {
if(result != null && imv != null){
if(imv.getTag().equals(path)){
imv.setVisibility(View.VISIBLE);
imv.setImageBitmap(result);
}else{
imv.setVisibility(View.GONE);
}
}else{
imv.setVisibility(View.GONE);
}
}
You can send in the ImageView to the task constructor and keep a reference to the image path there. Now at onPostExecute, check if the current tag of the ImageView is the same as the one that you started with. If yes, then set the image. If no, don't do anything.
However, this means that the image will be downloaded in any case. You'll just not set the wrong image on the view.
EDIT:
First pass the ImageView to the task constructor:
new LoadImage(imageView).execute()
Then save a reference to the ImageView and image path in LoadImage constructor. It is important to save the path in the constructor and not in doInBackground to ensure that we don't run into multi threading problems. Then at onPostExecute we check the current path.
class LoadImage extends AsyncTask<Object, Void, Bitmap>{
private ImageView imv;
private String path;
public LoadImage(ImageView imv) {
this.imv = imv;
this.path = imv.getTag().toString();
}
#Override
protected Bitmap doInBackground(Object... params) {
Bitmap bitmap = null;
File file = new File(
Environment.getExternalStorageDirectory().getAbsolutePath() + path);
if(file.exists()){
bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
}
return bitmap;
}
#Override
protected void onPostExecute(Bitmap result) {
if (!imv.getTag().toString().equals(path)) {
/* The path is not same. This means that this
image view is handled by some other async task.
We don't do anything and return. */
return;
}
if(result != null && imv != null){
imv.setVisibility(View.VISIBLE);
imv.setImageBitmap(result);
}else{
imv.setVisibility(View.GONE);
}
}
}
This Android Developers Blog post will give you a complete reference project for this complete with caching. Just replace the Http access code with SD card file reads.
I hope this helps.
After lot of search I have this working solution.
public class CustomAdapter extends ArrayAdapter<String>{
/*
public CustomAdapter(Context context , String[] video) {
super(context,R.layout.custom_row, video);
}
*/
private final Activity context;
private final String[] video;
static class ViewHolder {
public TextView videoTitle;
public ImageView videoThumbnail;
public int position;
public String path;
}
public CustomAdapter(Activity context, String[] video) {
super(context, R.layout.custom_row, video);
this.context = context;
this.video = video;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
LayoutInflater videoInflator = LayoutInflater.from(getContext());
View customView = videoInflator.inflate(R.layout.custom_row, parent, false);
ViewHolder viewHolder = new ViewHolder();
viewHolder.position = position;
viewHolder.path = video[position];
viewHolder.videoTitle = (TextView) customView.findViewById(R.id.videoTitle);
viewHolder.videoThumbnail = (ImageView) customView.findViewById(R.id.videoThumbnail);
//rowView.setTag(viewHolder);
//}
customView.setTag(viewHolder);
final String videoItem = video[position];
int index=videoItem.lastIndexOf('/');
String lastString=(videoItem.substring(index +1));
index = lastString.indexOf(".mp4");
lastString=(lastString.substring(0,index));
viewHolder.videoTitle.setText(lastString);
new AsyncTask<ViewHolder, Void, Bitmap>() {
private ViewHolder v;
#Override
protected Bitmap doInBackground(ViewHolder... params) {
v = params[0];
Bitmap thumb = ThumbnailUtils.createVideoThumbnail(videoItem, MediaStore.Images.Thumbnails.MINI_KIND);
return thumb;
}
#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.videoThumbnail.setImageBitmap(result);
}
}
}.execute(viewHolder);
return customView;
}
}
Maybe you should try:
view = mInflater.inflate(mResource,parent,null);
Check this blog it explains the similar issue:
http://www.doubleencore.com/2013/05/layout-inflation-as-intended/
What I would do (unless you have thousands of images):
1. create a data structure - a simple class holding a String name to be displayed and a bitmap
2. create an adapter for it
3. in the getView method assign the correct bitmap to the correct ImageView.
In your case though you can create a similar data structure but holding not a bitmap but an AsyncTask. Anyway you need to bind the asynctask to the string into one item. An array (or arraylist) of such items will be fed to your adapter. Displayed will be an imageview and a textview.
AsyncTask can be cancelled with cancel().
Hey I found the solution to this problem just use following function instead of your function
#Override
protected void onPostExecute(Bitmap result) {
if (!imv.getTag().toString().equals(rec_id)) {
return;
}
if(result != null && imv != null){
int index = id.indexOf(imv.getTag().toString());
if(list.getFirstVisiblePosition()<=index && index<=list.getLastVisiblePosition())
{
imv.setVisibility(View.VISIBLE);
imv.setImageBitmap(result);
}
}else{
imv.setImageBitmap(icon);
imv.setVisibility(View.GONE);
}
}
Here list is the object of listview. Just pass your list view object to your adapter and paste this function instead of your onPostExecute function.
friends,
i am using following global variables in my activity
private String Session_ID;
private String uid;
// menu item starts
private final int Trash = 0x003;
private final int More = 0x005;
private final int SignOut = 0x006;
private final int SignIn = 0x007;
//menu item ends
private EfficientAdapter adap;
private String[] Msg_id;
private String[] Msg_body;
private String[] Sent_by;
private String[] Sent_on;
private String[] Is_my_message;
private String[] Photo_thumbnail;
private String[] Photo_full_path;
private String Conversation_id;
ProgressBar progressBar;
Button getMoreButton;
boolean callComplete = false;
private Handler mHandler = new Handler();
private int PageSize = Constants.pageSizeForMessages;
Object serviceData = null;
private String ConversationName;
private Uri selectedImage;
public class EfficientAdapter extends BaseAdapter implements Filterable {
private LayoutInflater mInflater;
private Context context;
public EfficientAdapter(Context context) {
mInflater = LayoutInflater.from(context);
this.context = context;
}
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder;
convertView = mInflater.inflate(R.layout.adaptor_contentmessagedetail, null);
holder = new ViewHolder();
holder.ImgPhoto = (ImageView)convertView.findViewById(R.id.ImgPhoto);
holder.lblMsgSentBy = (TextView) convertView.findViewById(R.id.lblSentBy);
holder.lblMsgBody = (TextView) convertView.findViewById(R.id.lblMessageBody);
holder.lblMsgSentOn = (TextView) convertView.findViewById(R.id.lblSentOn);
convertView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if (!((MessageDetail)v.getContext()).isConnected()) {
Constants.DisplayMessage(v.getContext(),
Constants.CONNECTION_ERROR_MESSAGE);
return;
}
if(!Photo_full_path[position].equals(""))
{
String str= Photo_full_path[position].substring(Photo_full_path[position].length() - 3);
if(str.equals("pdf"))
{
}else
{
Intent myIntent = new Intent(v.getContext(), ViewSingleImage.class);
Bundle b = new Bundle();
b.putString("single_image_path", Photo_full_path[position] );
myIntent.putExtras(b);
v.getContext().startActivity(myIntent);
}
}
}
});
convertView.setTag(holder);
// Bind the data efficiently with the holder.
if(Is_my_message[position].equals("1"))
holder.lblMsgSentBy.setTextColor(Color.BLACK);
else
holder.lblMsgSentBy.setTextColor(Color.rgb(255, 107, 1));
SimpleDateFormat fromUser = new SimpleDateFormat(Constants.SERVICE_DATE_FORMAT);
java.text.DateFormat df=new SimpleDateFormat(Constants.DATE_FORMAT);
Date dt=new Date();
try
{
dt = fromUser.parse(Sent_on[position]);
} catch (java.text.ParseException e) {
e.printStackTrace();
}
// display photo
if(!Photo_thumbnail[position].equals(""))
{
// resize it
holder.ImgPhoto.setImageBitmap(DisplayLiveImage(Photo_thumbnail[position]));
}else
{
holder.ImgPhoto.setVisibility(View.GONE);
}
// display photo
holder.lblMsgSentBy.setText(Constants.GetSpecialCharacters(Sent_by[position]));
holder.lblMsgBody.setText(Constants.GetSpecialCharacters(Msg_body[position]));
holder.lblMsgSentOn.setText(df.format(dt));
return convertView;
}
class ViewHolder {
ImageView ImgPhoto;
TextView lblMsgSentBy;
TextView lblMsgBody;
TextView lblMsgSentOn;
}
#Override
public Filter getFilter() {
// TODO Auto-generated method stub
return null;
}
#Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return 0;
}
#Override
public int getCount() {
// TODO Auto-generated method stub
return Msg_id.length;
}
#Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return Msg_id[position];
}
}
public Bitmap DisplayLiveImage(String ImageSrc)
{
Bitmap bm;
try {
URL aURL = new URL(ImageSrc);
URLConnection conn = aURL.openConnection();
conn.connect();
InputStream is = null;
try
{
is= conn.getInputStream();
}catch(IOException e)
{
return null;
}
BufferedInputStream bis = new BufferedInputStream(is);
bm = BitmapFactory.decodeStream(bis);
bis.close();
is.close();
} catch (IOException e) {
return null;
}
return bm;
}
i have made them global in activity because i need them all in more than 1 functions
now my question is how to improve performance of my activity it is too slow
should i make them static or what?
any help would be appreciated.
Your global variables are almost certainly not the cause of your poor performance. Unless you're accessing them a million times, it must be something else. If you tell us what exactly is performing slower than you would expect and post the relevant code, we might be able to help.
You have a LOT of code in your getView() method. this method gets called every single time a new view gets displayed. So when the listview is created, it's called N times where N being the number of list elements that are seen. Then when you scroll, every time a new element comes onto the screen, getView() gets called again. Even if you then scroll back up, it calls getView() again.
You need to refactor your code that doesn't need to be run every time a view is created out of the view.
it is recommended to cache images and dont bring them all again and again from internet.
so my case while using custom adapter and scrolling it was again and again loading images from internet
which was causing poor performance.
and memory leakage problem too.
so what i did i followed following tutorial to load live images and my problem resolved
Answer: LazyList
http://mobilebitworks.wordpress.com/2010/11/05/android-listview-and-dynamically-loading-images-from-the-web