I am new in android so I am trying to show the images size more than 50 Xhdpi in gridview but while doing this when I execute my program it makes screen hold for sometime and then execute.
Can any one tell the reason why it is taking time.
Here is my code:
mGv_detail = (GridView) findViewById(R.id.gridView1);
init();
key_value = getIntent().getIntExtra("values", 0);
switch (key_value) {
case 0:
mGv_detail.setAdapter(new DetailAdapter(this, first_images));
break;
#Override
public View getView(final int position, View view, ViewGroup parent) {
View v = view;
RecordHolder holder = null;
LayoutInflater mInflater = (LayoutInflater) mContext
.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
if (v == null) {
v = mInflater.inflate(R.layout.items, parent, false);
holder = new RecordHolder();
holder.imagev = (ImageView) v.findViewById(R.id.imag_v);
v.setTag(holder);
} else {
Log.e("Position", "" + position);
holder = (RecordHolder) v.getTag();
}
Bitmap bitmap1 = BitmapFactory.decodeResource(v.getResources(),
items.get(position));
holder.imagev.setImageBitmap(bitmap1);
// holder.imagev.setImageResource(items.get(position));
holder.imagev.setAdjustViewBounds(true);
holder.imagev.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Intent i = new Intent(mContext, Christmas.class);
i.putExtra("clickposition", position);
Log.e("postiont clicked", position+"");
mContext.startActivity(i);
}
});
return v;
}
static class RecordHolder {
ImageView imagev;
}
}
That's a lot of images. It will lag, even for local image files.
You should use a tool like "Universal Image Loader" -
https://github.com/nostra13/Android-Universal-Image-Loader/blob/master/library/src/com/nostra13/universalimageloader/core/ImageLoader.java
Basically, given that many images, it will take a few moments. I have a GridView with about that many images, and using Universal Image Loader it takes about 3 or 4 seconds even on a fast, new device. However, it will display immediately and you can watch the grid fill with images and it isn't a big deal if you use a tool. If you don't, you really feel the lag.
Your getView method which is resposible for displaying the cells in your GridView is loading your bitmaps into memory synchronously. The delay is very natural given this order of events. If you use one of the many image loading libraries available for Android, this work can be quite easily offloaded to a background thread and called back when ready. My personal favorite is Picasso.
http://square.github.io/picasso/
You can add Picasso as a jar file or via gradle, and then image loading would look something like this:
int resId = images.get(position);
Picasso.with(getContext()).load(resId).into(holder.imagev);
Related
I am working on social application and it's about to complete but I got stuck on one issue that is image flickering. When there is around 9 to 10 images on screen and if I scroll the page then the image flicker take place.
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
final ViewHolder holder;
if (convertView == null) {
LayoutInflater inf = (LayoutInflater) act.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inf.inflate(R.layout.view_grid_explore, null);
holder = new ViewHolder();
holder.img = (ImageView) convertView.findViewById(R.id.img_grid_album);
} else {
holder = (ViewHolder) convertView.getTag();
}
ImageLoader.getInstance().displayImage(
Static_Urls.explore_pic + data.get(position).talk_pic,
holder.img);
convertView.setTag(holder);
notifyDataSetChanged();
return convertView;
}
Note : Don't forget to remove notifyDataSetChanged();.
This is happening because once the images are downloaded in the device by UIL(Universal Image Loader), it caches the images in Memory and device.
By using this code :
ImageLoader.getInstance().displayImage(Static_Urls.explore_pic +data.get(position).talk_pic,
holder.img);
every time getView() is called UIL tries to fetch the image from network, but by the time it releases that image is already being cached so it shows the image after making a network request first.
so in order to get rid of this flickering use this code :
ImageLoader imageLoader = ImageLoader.getInstance();
File file = imageLoader.getDiskCache().get(Static_Urls.explore_pic +data.get(position).talk_pic);
if (file==null) {
//Load image from network
imageLoader.displayImage(Static_Urls.explore_pic +data.get(position).talk_pic,
holder.img);
}
else {
//Load image from cache
holder.img.setImageURI(Uri.parse(file.getAbsolutePath()));
}
This code will first check whether the image is already cached or not, Then accordingly fetch image from Network or from cache.
The notifyDataSetChanged() line is redundant there. Working with adapters always keep in mind that(in case of adapters extending BaseAdapter) the getView() method is responsible for inflating the layout of the list item and also updating the UI if if you handle it so(normally you do)
Calling notifyDataSetChanged() will cause the getView() being called again right away which is why you see the flickering.
You should only call notifyDataSetChanged() when you would like to update the adapter content. One example would be when you build yourself a "refresh()" method inside your adapter like:
public void refresh(List<Object> list) {
data.clear();// Assuming data is a List<> object or an implementation of it like ArrayList();
data.addAll(list);
notifyDataSetChanged(); // This will let the adapter know that something changed in the adapter and this change should be reflected on the UI too, thus the getView() method will be called implicitly.
}
I have a ListView of receipt data, which includes a vendor, value of the receipt, and the date on the receipt.
I'd like to add an lazy-loaded image of the receipt to my ListView. I have thumbnail images of the receipt stored in an AWS location, but the receipt images could be 'processing' and not immediately available. Unfortunately, the URL for the remote image is not available in my current ListView dataset, which means I have to call an async task to retrieve the receipt image data for each displayed row in my adapter.
To get the receipt image data, I have created a service call (I'm using Retrofit) to hit my API to pull down the JsonObject that contains the receipt image thumbnail location OR an indication that the image is still processing/not available. The receipt image data is requested via a receipt id.
Here's what I currently have:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
ReceiptHolder holder = null;
if (row == null) {
LayoutInflater inflater = ((Activity)context).getLayoutInflater();
row = inflater.inflate(layoutResourceId, parent, false);
holder = new ReceiptHolder();
holder.txtVendor = (TextView)row.findViewById(R.id.vendor_field);
holder.txtAmount = (TextView)row.findViewById(R.id.amount_field);
holder.txtDate = (TextView)row.findViewById(R.id.date_field);
row.setTag(holder);
} else {
holder = (ReceiptHolder)row.getTag();
}
Receipts r = data[position];
String fullValue = r.amount_symbol + r.amount;
holder.txtVendor.setText(r.vendor);
holder.txtAmount.setText(fullValue);
holder.txtDate.setText(r.date);
return row;
}
public class ReceiptHolder {
public TextView txtVendor;
public TextView txtAmount;
public TextView txtDate;
public ProgressBar receiptFetch;
public ImageView receiptThumb;
}
So, when my custom adapter is performing the getView, I'd like the following to happen:
The ProgressBar receiptFetch to be set as visible on initial draw to indicate that the image is still being retrieved/processed. I'd like the receipt data to be visible and a ProgressBar indicating that the image is still being retrieved or processed.
Some kind of non-blocking mechanism to go back and set ProgressBar's visibility to GONE and simultaneously setting ImageView receiptThumb with the asynchronously retrieved URL for each row item as the task completes for that particular row.
Now, here's my question:
How do I get the View holder pattern to play nice without hanging up the UX? Ideally when the adapter is doing its thing, I could kick off an async task to retrieve the receipt image data for each receipt while populating the other fields inherent to receipt data... but I'm not sure how to capture the response to turn off the ProgressBar and go back to populate the ImageView.
I've been unsuccessfully dabbling at this for a bit, and I'm stumped. Does my question make sense? Any ideas?
You can use UniversalImageLoader library to load your remote image.
Refer this: https://github.com/nostra13/Android-Universal-Image-Loader
final View imageLayout = inflater.inflate(R.layout.list_item, null);
final ImageView imageView = ...
imageLoader.displayImage(images[position], imageView, options, new SimpleImageLoadingListener() {
#Override
public void onLoadingStarted(String imageUri, View view) {
spinner.setVisibility(View.VISIBLE);
}
#Override
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
spinner.setVisibility(View.GONE);
}
#Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
spinner.setVisibility(View.GONE);
}
});
Picasso. Picasso is the answer.
Universal Image Loader would also work, but was simply overkill for the problem I was trying to solve. Picasso does inline lazy loading, and has options to allow for a temporary #Drawable to be shown while the thumbnail is retrieved.
I ended up doing the following - didn't need the ProgresBar anymore since Picasso took care of it. Note that Picasso can be used in a static way - but I had to create a custom OkHttpClient for our networking configuration :
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
ReceiptHolder holder = null;
if (row == null) {
LayoutInflater inflater = ((Activity) context)
.getLayoutInflater();
row = inflater.inflate(layoutResourceId, parent, false);
holder = new ReceiptHolder();
holder.txtVendor = (TextView) row
.findViewById(R.id.vendor_field);
holder.txtAmount = (TextView) row
.findViewById(R.id.amount_field);
holder.txtDate = (TextView) row.findViewById(R.id.date_field);
holder.receiptThumb = (ImageView) row
.findViewById(R.id.receipt_image);
row.setTag(holder);
} else {
holder = (ReceiptHolder) row.getTag();
}
Receipts r = data[position];
String fullValue = r.amount_symbol + r.amount;
holder.txtVendor.setText(r.vendor);
holder.txtAmount.setText(fullValue);
holder.txtDate.setText(r.date);
picasso.with(getActivity())
.load(r.medium_jpg_url)
.placeholder(R.drawable.ic_empty)
.error(R.drawable.ic_error)
.resize(100, 100)
.centerCrop()
.into(holder.receiptThumb);
return row;
}
I'm having trouble with a listview in android. When I start scrolling down my List, it is very slow and I see that the GC is called. When I'm at the bottom of my List, everything works fine and smooth. I think that at this point my ViewHolder does the work.
But I can't find the source that is calling the GC. I searched which lead to:
DDMS 436816 byte[] 1 android.graphics.Bitmap nativeCreate
I can't interpret that line. My ArrayAdapter and it's getView method looks like this:
public class DiagnoseAdapter extends ArrayAdapter<Visualizer> {
#Override
public View getView(int position, View convertView, ViewGroup parent) {
int type = TYPE_DEFAULT;
final Visualizer item = getItem(position);
switch(item.getType()){
case TYPE_DEFAULT:
convertView = DefaultTextView.getView(position, convertView, mlayoutInflater, item, parent);
break;
// more cases/types
}
return convertView;
}
}
which is calling the following getView Method of the class DefaultTextView
public class DefaultTextView{
public static View getView(int position, View convertView, LayoutInflater layoutInflater, Visualizer item, ViewGroup parent){
ViewHolder holder;
if (convertView == null || item.getReleatedObject() == null || convertView.getTag()!=TAG_DEFAULT) {
convertView = layoutInflater.inflate(R.layout.diagnose_item, null);
holder = new ViewHolder();
holder.value = (TextView) convertView.findViewById(R.id.diagnose_function_value);
holder.name = (TextView) convertView.findViewById(R.id.diagnose_function_setname);
holder.mLinLayout = (LinearLayout) convertView.findViewById(R.id.default_linlayout);
convertView.setTag(TAG_DEFAULT);
convertView.setTag(R.layout.diagnose_item,holder);
item.setReleatedObject(convertView);
} else {
holder = (ViewHolder) convertView.getTag(R.layout.diagnose_item);
}
holder.value.setText(item.toString());
holder.name.setText(item.getToolTip());
holder.mLinLayout.removeAllViews();
if (item.getUpdateFlag(4)) {
if (holder.back == null){
holder.back = new ImageView(convertView.getContext());
holder.back.setScaleType(ScaleType.FIT_CENTER);
holder.back.setAdjustViewBounds(true);
holder.back.setImageBitmap(bm1);
}
holder.mLinLayout.addView(holder.back);
}
if (item.getUpdateFlag(1)) {
if (holder.update == null){
holder.update = new ImageView(convertView.getContext());
holder.update.setScaleType(ScaleType.FIT_CENTER);
holder.update.setAdjustViewBounds(true);
holder.update.setImageBitmap(bm2);
}
holder.mLinLayout.addView(holder.update);
}
if (item.getUpdateFlag(2)) {
if (holder.timer == null){
holder.timer = new ImageView(convertView.getContext());
holder.timer.setScaleType(ScaleType.FIT_CENTER);
holder.timer.setAdjustViewBounds(true);
holder.timer.setImageBitmap(bm3)
}
holder.mLinLayout.addView(holder.timer);
}
if (item.getUpdateFlag(3)) {
if (holder.log == null){
holder.log = new ImageView(convertView.getContext());
holder.log.setScaleType(ScaleType.FIT_CENTER);
holder.log.setAdjustViewBounds(true);
holder.log.setImageBitmap(bm4);
}
holder.mLinLayout.addView(holder.log);
}
if (item.getUpdateFlag(0)) {
if (holder.forward == null){
holder.forward = new ImageView(convertView.getContext());
holder.forward.setScaleType(ScaleType.FIT_CENTER);
holder.forward.setAdjustViewBounds(true);
holder.forward.setImageBitmap(bm5);
}
holder.mLinLayout.addView(holder.forward);
}
return convertView;
}
static class ViewHolder {
TextView name, value;
ImageView back, update, timer, log, forward;
LinearLayout mLinLayout;
}
}
Even if I comment the LinearLayout out, so I just have a List with two TextViews.
So my Question. Do I miss anything. Some stupid thing? How do I get my ListView smoother?
BTW: I read in a different thread, that it is happening if the ListView has the attribute android:cacheColorHint="#00000000. I don't have this attribute.
I hope anyone has a solution. Thanks!
About the source of GC calls. If I'm understanding your code correctly, everytime your ListView items are recycled and you call removeAllViews(), a previously dynamically created ImageView is removed and its Bitmap is garbage collected. So, Maybe those GC calls would be avoided if you use the same ImageView declaring it in your xml layout and just replace the Bitmap according to your getUpdateFlag().
And two more things about ListViews and Images. First thing is that if the image is too big, your ListView is going to be laggy no matter what. You would need to scale the image down if that is the case( Loading Large Bitmaps Efficiently). And second, maybe you would also need to implement a Lazy List, which loads images on demand, there is a famous question about that --> How do I do a lazy load of images in ListView?
I've finally solved my Problem. Like above, I thought the problem was based on the images of my list items. But that wasn't the problem. I just didn't use my ViewHolders and the getItemViewType(int position) method correctly. I have a list with many different item layouts and I saw, that my code above created a new convertView and a new ViewHolder for every single item, which wasn't supposed to be. I found a great tutorial about how to use multiple item layouts (see link below):
Multiple List Item Layouts
Being a complete novice to Android and (admittedly) not the strongest programmer - I want to ask for some advice on loading thumbnail images into a Bitmap Array, which is loaded into a custom adapter.
The thumbnails are very small (around 5KB).
I add the thumbnails to a Bitmap array in an Async task. I am using drawables which are dummy images. So I load the entire list with dummy images (I load the actual images later on).
I am worried if the user browses a folder with 200+ images. I could possibly get an out of memory error. I want a way to prevent this, perhaps only load what is needed in the visible display, and load more if needed?
I have read a lot of other questions and advice on recycling Bitmaps, but I'm still not sure where tog o from here.
#Override
protected Boolean doInBackground(DbxFileSystem... params) {
//Opens thumbnails for each image contained in the folder
try {
DbxFileSystem fileSystem = params[0];
Bitmap image=null;
int loopCount=0; //I use this to identify where in the adapter the real image should go
for (DbxFileInfo fileInfo: fileSystem.listFolder(currentPath)) {
try{
if(!fileInfo.isFolder)
{
image = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
pix.add(image);
paths.add(fileInfo.path);
loopCount++;
}
else
{
//must be a folder if it has no thumb, so add folder icon
image = BitmapFactory.decodeResource(getResources(), R.drawable.dbfolder);
pix.add(image);
paths.add(fileInfo.path);
loopCount++;
}
}
catch(Exception e)
{
e.printStackTrace();
}
System.gc();
}
}
catch (Exception e) {
e.printStackTrace();
return false;
} finally {
loadingDialog.dismiss();
}
return true;
}
Here is the getView from the Custom Adapter:
public View getView(final int position, View arg1, ViewGroup arg2) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = arg1;
ViewHolder holder;
if (arg1 == null) {
LayoutInflater vi = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.list_row, null);
holder = new ViewHolder();
holder.title = (TextView) v.findViewById(R.id.filename);
holder.iconImage = (ImageView) v.findViewById(R.id.list_image);
holder.checkbox = (CheckBox)v.findViewById(R.id.checkBox1);
v.setTag(holder);
} else {
holder = (ViewHolder) v.getTag();
}
holder.title.setText(folderName.get(position).toString());
holder.iconImage.setImageBitmap(images.get(position));
The first thing you need to know is that when using an adapter, the views are created only when they are displayed on the screen. It means that you don't need and you must not decode all the bitmaps.
The best practice is to decode each bitmap in an AsyncTask when the associated view is created. The bitmap will be decoded in the doInBackground method and set to the ImageView in the onPostExecute method (as it's executed on the UI thread).
Then you might also want to use RAM or disk cache to reload previously decoded bitmaps more efficiently.
Please take a look at http://developer.android.com/training/displaying-bitmaps/index.html for more information about how to display bitmaps efficiently.
I load data from Cursor to listview, but my Listview not really display "smooth". The data change when I drag up and down on the scollbar in my ListView. And some items look like duplicate display in my list.
I hava a "complex ListView" (two textview, one imageview) So I used newView(), bindView() to display data. Can someone help me?
I will describe you how to get such issue that you have. Possibly this will help you.
So, in list adapter you have such code:
public View getView(int position, View contentView, ViewGroup arg2)
{
ViewHolder holder;
if (contentView == null) {
holder = new ViewHolder();
contentView = inflater.inflate(R.layout.my_magic_list,null);
holder.label = (TextView) contentView.findViewById(R.id.label);
contentView.setTag(holder);
} else {
holder = (ViewHolder) contentView.getTag();
}
holder.label.setText(getLabel());
return contentView;
}
As you can see, we set list item value only after we have retrieved holder.
But if you move code into above if statement:
holder.label.setText(getLabel());
so it will look after like below:
if (contentView == null) {
holder = new ViewHolder();
contentView = inflater.inflate(R.layout.my_magic_list,null);
holder.label = (TextView) contentView.findViewById(R.id.label);
holder.label.setText(getLabel());
contentView.setTag(holder);
}
you will have your current application behavior with list item duplication.
Possibly it will help.
ListView is a tricky beast.
Your second question first: you're seeing duplicates because ListView re-uses Views via convertView, but you're not making sure to reset all aspects of the converted view. Make sure that the code path for convertView!=null properly sets all of the data for the view, and everything should work properly.
You'll want your getView() method to look roughly like the following if you're using custom views:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
final MyCustomView v = convertView!=null ? (MyCustomView)convertView : new MyCustomView();
v.setMyData( listAdapter.get(position) );
return v;
}
If you're not using your own custom view, just replace the call to new MyCustomView() with a call to inflater.inflate(R.layout.my_layout,null)
As to your first question, you'll want to watch Romain's techtalk on ListView performance here: http://code.google.com/events/io/sessions/TurboChargeUiAndroidFast.html
From his talk and in order of importance from my own experience,
Use convertView
If you have images, don't scale your images on the fly. Use Bitmap.createScaledBitmap to create a scaled bitmap and put that into your views
Use a ViewHolder so you don't have to call a bunch of findViewByIds() every time
Decrease the complexity of the views in your listview. The fewer subviews, the better. RelativeLayout is much better at this than, say, LinearLayout. And make sure to use if you're implementing custom views.
I'm facing this problem as well, but in my case I used threads to fetch the external images. It is important that the current executing thread do not change the imageView if it is reused!
public View getView(int position, View vi, ViewGroup parent) {
ViewHolder holder;
String imageUrl = ...;
if (vi == null) {
vi = inflater.inflate(R.layout.tweet, null);
holder = new ViewHolder();
holder.image = (ImageView) vi.findViewById(R.id.row_img);
...
vi.setTag(holder);
} else {
holder = (ViewHolder) vi.getTag();
}
holder.image.setTag(imageUrl);
...
DRAW_MANAGER.fetchDrawableOnThread(imageUrl, holder.image);
}
And then on the fetching thread I'm doing the important check:
final Handler handler = new Handler() {
#Override
public void handleMessage(Message message) {
// VERY IMPORTANT CHECK
if (urlString.equals(url))
imageView.setImageDrawable((Drawable) message.obj);
};
Thread thread = new Thread() {
#Override
public void run() {
Drawable drawable = fetchDrawable(urlString);
if (drawable != null) {
Message message = handler.obtainMessage(1, drawable);
handler.sendMessage(message);
}
}};
thread.start();
One could also cancel the current thread if their view is reused (like it is described here), but I decided against this because I want to fill my cache for later reuse.
Just one tip: NEVER use transparent background of item layout - it slows performance greatly
You can see the reocurring text in multiple rows if you handle it in the wrong way. I've blogged a bit about it recently - see here. Other than that you might want to take a look at ListView performance optimization. Generally it's because of the view reuse and I've seen it few times already.