I am developing an app that show all album cover images of the songs. So I am using glide for loading and caching images and to avoid OutofMemoryError but I still get that error:
11-11 11:05:55.866 11120-11120/com.xrobot.andrew.musicalbumsE/AndroidRuntime﹕ FATAL EXCEPTION: main Process: com.xrobot.andrew.musicalbums, PID: 11120 java.lang.OutOfMemoryError: OutOfMemoryError thrown while trying to throw OutOfMemoryError; no stack trace available
This is my getView in the AlbumAdapter:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
RelativeLayout albumsLay = (RelativeLayout)songInf.inflate
(R.layout.album_layout, parent, false);
ImageView coverView = (ImageView)albumsLay.findViewById(R.id.song_cover);
//get song using position
Song currSong = songs.get(position);
if (Drawable.createFromPath(currSong.getCover()) != null) {
Drawable img = Drawable.createFromPath(currSong.getCover());
Glide.with(mContext).load("").placeholder(img).override(50,50).into(coverView);
}
albumsLay.setTag(position);
return albumsLay;
}
Try using Glide directly with the image path:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
RelativeLayout albumsLay = (RelativeLayout)songInf.inflate
(R.layout.album_layout, parent, false);
ImageView coverView = (ImageView)albumsLay.findViewById(R.id.song_cover);
//get song using position
Song currSong = songs.get(position);
// If you are sure currSong.getCover() exists you can remove the if statement
if(new File(currSong.getCover().exists))
Glide.with(mContext).load(currSong.getCover()).override(50,50).into(coverView);
albumsLay.setTag(position);
return albumsLay;
}
And you could also use a holder for the view. This would reduce the memory usage:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
CoverHolder holder = null;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.album_layout, null);
holder = new CoverHolder();
holder.coverView = (ImageView)convertView.findViewById(R.id.song_cover);
convertView.setTag(holder);
} else {
holder = (CoverHolder)convertView.getTag();
}
Glide.with(mContext).load(currSong.getCover()).override(50,50).into(holder.coverView);
return convertView;
}
// The holder
public static class CoverHolder{
public ImageView coverView;
}
Still, if you really need performance on a huge list. You can take a look at the RecyclerView here.
Related
I want to add single row to my ListView. When I run the code below, there are 2 lines added, one with the text I want to add, and an empty one. Why is the empty one added? (lines[0] is a file with (for now) 1 line in it)
listAdapter = new ArrayAdapter<String>(getApplicationContext(),R.layout.listviewrow, R.id.txt_listviewItem, lines){
#Override
public View getView(int position, View convertView, ViewGroup parent){
View myView = convertView;
if (myView == null) {
LayoutInflater li = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
myView = li.inflate(R.layout.listviewrow, null);
} else {
TextView txt_listviewItem = (TextView)myView.findViewById(R.id.txt_listviewItem);
txt_listviewItem.setText(lines[0]);
}
return myView;
};
...
listView.setAdapter(listAdapter);
When I debug, listview.setAdapter(listAdapter) is called before AND after the getView code runs. Why? I only call it once.
you have use Viewholder to avoid duplication.
Just a wild guess, but does your text file include a "new line" character on the second row? Or, should I say, does your text file have, indeed, one line or two lines out of which the second is just a \n character?
Try this
listAdapter = new ArrayAdapter<String>(getApplicationContext(),R.layout.listviewrow, R.id.txt_listviewItem, lines){
#Override
public View getView(int position, View convertView, ViewGroup parent){
View myView = convertView;
if(myView==null){
LayoutInflater li = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
myView = li.inflate(R.layout.listviewrow, null);
TextView txt_listviewItem = (TextView)myView.findViewById(R.id.txt_listviewItem);
txt_listviewItem.setText(lines[0]);
}
return myView;
};
listView.setAdapter(listAdapter);
Consider the code below:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView = (ImageView) convertView; //here
if (imageView == null) {
imageView = (ImageView) inflater.inflate(R.layout.item_gallery_image, parent, false);
}
ImageLoader.getInstance().displayImage(IMAGE_URLS[position], imageView, options);
return imageView;
}
Findbugs plugin from Android Studio is complaining about the first line from the method getview, it says:
Unchecked/unconfirmed cast
This cast is unchecked, and not all instances of the type casted from can be cast to the type it is being cast to. Check that your program logic ensures that this cast will not fail.
Any ideas of how do I resolve this issue?
If you want to please FindBugs you can assert that convertView is an ImageView.
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = inflater.inflate(R.layout.item_gallery_image, parent, false);
}
assert convertView instanceof ImageView : convertView;
ImageLoader.getInstance().displayImage(IMAGE_URLS[position], (ImageView) imageView, options);
return imageView;
}
Not every View is an ImageView, so FindBugs is trying to warn you that the cast may result in a ClassCastException.
However, you actually can't get a ClassCastException because the way convertView works is that you either recieve null or you receive a View previously returned by the getView() method. Since your method always returns an ImageView, everything is fine.
FindBugs finds potential problems with your code, and sometimes they are actually not problems at all.
i think you try to fill every row of a listView or recycle view with image...first you should make layout that contains an image view then you can do it in this way:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
view=inflater.inflate(R.layout.adapter_row,parent,false);
ImageView mImage=(ImageView) view.findViewById(R.id.imageid);
ImageLoader.getInstance().displayImage(IMAGE_URLS[position], mImage, options);
return view;
}
I know that many similar questions have been posted on stackoverflow, so please don't think I haven't searched high and low. I think my problems simply comes from now completely understanding listViews and the lifecycles of list items. I have a list view that can contain two types of messages, outbound or inbound. Originally, my listView would use a different background color depending on the type of message (outbound vs inbound), and it worked flawlessly. Now my application doesn't require a different background for list items, but it actually requires different layouts for different list items.
This is a clip of my Adapter.
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
SoapBoxMessage thisMessage = messages.get(position);
if (v == null) {
LayoutInflater inflater = (LayoutInflater) getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (thisMessage.isOutbound()) {
v = inflater.inflate(R.layout.outbound_row, null);
} else {
v = inflater.inflate(R.layout.inbound_row, null);
}
}
Adapters can support different ViewItemTypes that will solve your recycling problems.
static public enum LAYOUT_TYPE {
INBOUND,
OUTBOUND
}
#Override
public int getViewTypeCount () {
return LAYOUT_TYPE.values().length;
}
#Override
public int getItemViewType (int position) {
if ( messages.get(position).isOutbound())
return LAYOUT_TYPE.OUTBOUND.ordinal();
else
return LAYOUT_TYPE.INBOUND.ordinal();
}
#Override
public View getView (int position, View convertView, ViewGroup parent) {
LAYOUT_TYPE itemType = LAYOUT_TYPE.values()[getItemViewType(position)];
... (code until inflater )
switch (itemType){
case INBOUND:
convertview = /inflate & configure inbound layout
break;
case OUTBOUND:
convertview = /inflate & configure outbound layout
break;
}
you don't need to worry about recycling views because the listview will respect the ViewItemTypes for each position and it will only provide a convertview of the correct viewtype for that position
The problem is that listview is recycling the view so when you check if the view is null it wont pass that because the view is not null when it is recycled
you would need to inflate the view each time getView is called, basically removing if(v == null)
Try to use a ViewHolder like this:
ViewHolder holder;
public View getView(int position, View convertView, ViewGroup parent) {
convertView = null;
SoapBoxMessage thisMessage = messages.get(position);
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (thisMessage.isOutbound()) {
convertView = inflater.inflate(R.layout.outbound, null, false);
//specific to your outbound layout
holder = new ViewHolder();
holder.text= (TextView)convertView.findViewById(R.id.textview);
holder.group = (RadioGroup)convertView.findViewById(R.id.toggleGroup);
holder.toggle = (ToggleButton)convertView.findViewById(R.id.toggleButton);
} else {
convertView = inflater.inflate(R.layout.inbound, null, false);
//specific to your inbound layout
holder = new ViewHolder();
holder.text= (TextView)convertView.findViewById(R.id.textview);
holder.group = (RadioGroup)convertView.findViewById(R.id.toggleGroup);
holder.toggle = (ToggleButton)convertView.findViewById(R.id.toggleButton);
}
convertView.setTag(holder);
}
else{
holder = (ViewHolder) convertView.getTag();
}
//Here you can set the text or other code you want to implement
holder.text.setText("Whatever!");
return convertView;
}
static class ViewHolder {
//TODO put components you use like:
TextView text;
RadioGroup group;
ToggleButton toggle;
}
It's because of the recycling that is happening. You would need something along these lines:
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
SoapBoxMessage thisMessage = messages.get(position);
if (convertView == null) {
LayoutInflater mInflater = (LayoutInflater) context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
convertView = mInflater.inflate(R.layout.listview_feedlog_item, null);
holder = new ViewHolder();
holder.txtTime = (TextView) convertView.findViewById(R.id.textTime);
holder.txtDate = (TextView) convertView.findViewById(R.id.textDate);
convertView.setTag(holder);
} else
holder = (ViewHolder) convertView.getTag();
// I don't know how your SoapBoxMessage is made up so here are two sample methods
holder.txtTime.setText(thisMessage.getTime());
holder.txtDate.setText(thisMessage.getDate());
return convertView;
}
/* private view holder class */
private class ViewHolder {
TextView txtTime;
TextView txtDate;
}
Also, remember to always reset or initiate a value in the getView method. Since a View can be recycled it might carry with it properties of its former life.
Whether this is a good practice or not, Removing if (v == null) will solve the problem.
Anyway, you will have to re-inflate the view.
Hi i'm having this kind of problem, when scrolling imageviews change their positions and
their background images. I saw other answers on this topic on this site, but non of them
helped me.
Like this once:
grid view scrolling issue
GridView scrolling problem on Android
GridView elements changes their place dynamically when scroll screen
and many others...but they don't solve my problem.
Important thing is that i don't use custom layout for gridview or gridview items(imageViews).I create them programmatically. This is very important to me so if someone know the answer pls help me...Thanks.
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView==null){
imageView = new ImageView(ctx);
} else {
imageView = (ImageView) convertView;
}
imageView.setLayoutParams(new GridView.LayoutParams(85, 85));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setPadding(8, 8, 8, 8);
imageView.setBackgroundResource(tmp[position]);
imageView.setImageResource(blank);
return imageView;
}
Its a simple method. Adapters recycle each and every view while scrolling. So we just need to create the ones which are being re-used after the first time. Holders help to avoid expensive calls like findViewById and re-use the items by just getting the old ones and changing its properties.
One point you need to keep in mind is that we need to hold the images which are to be displayed in any one of the containers like array[] or List and reset each time before returning the view, or else it will display the previously last recycled view.
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
LayoutInflater inflater = (LayoutInflater) activity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (convertView == null) {
convertView = inflater.inflate(R.layout.item_layout, null);
holder = new ViewHolder();
holder.cover = (ImageView) convertView
.findViewById(R.id.item_cover);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.cover.setBackgroundResource(tmp[position]);
return convertView ;
}
static class ViewHolder {
ImageView cover;
}
try this
class Holder {
ImageView imageView;
}
public View getView(int position, View convertView, ViewGroup parent) {
Holder holder;
if(holder.imageView==null){
holder=new Holder();
holder.imageView = new ImageView(ctx);
holder.imageView.setLayoutParams(new GridView.LayoutParams(85, 85));
holder.imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
holder.imageView.setPadding(8, 8, 8, 8);
imageView.setTag(holder);
} else {
holder= (Holder) imageView.getTag();
}
holder.imageView.setBackgroundResource(tmp[position]);
holder.imageView.setImageResource(blank);
return imageView;
}
I have developed an app in which I display data in Gridview. All data comes from local storage. I am able to display each data correctly and there are no issues with it. But when I have scroll the Gridview and goto the bottom and getback to Top,it changes position. And sometimes when I scroll down and getback to Top, a blank screen appears on screen;no data found at all!
So I thought that there is issue with getView(). I am unable to figure out the problem
Code of getView():
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
ViewHolder viewholder;
// LayoutInflater inflator = activit.getLayoutInflater();
if(convertView == null) {
// recycled view is null so create it.
viewholder = new ViewHolder();
convertView = inflator.inflate(R.layout.gridviewrow, null);
viewholder.imgvGridicon = (ImageView) convertView.findViewById(R.id.imgvGridicon);
viewholder.txtGridItemlabel = (TextView) convertView.findViewById(R.id.txtGridItemlabel);
convertView.setTag(viewholder);
} else {
viewholder = (ViewHolder) convertView.getTag();
}
if ((lstpinfo.get(position).appname.toString()) != null) {
viewholder.imgvGridicon.setImageDrawable((lstpinfo.get(position).icon));
viewholder.txtGridItemlabel.setText(lstpinfo.get(position).appname.toString());
}
return convertView;
}
Update::
Intitalize of inflater::
private LayoutInflater inflator;
private ArrayList<PInfo> lstpinfo = new ArrayList<PInfo>();
public GridViewAdapter(Context cntx, ArrayList<PInfo> lstpinfo) {
activit = cntx;
inflator = LayoutInflater.from(cntx);
this.lstpinfo = lstpinfo;
}
You have to set your view height with fixed value. There's a scrolling bug in android gridview with different heights. Please see this for reference.