OutOfMemoryError when setting Uri with setImageURI - java

The user select a image, the path of the image is then stored in my SQLite database. I then populate the text/images from SQLite in a GridView using a CursorAdapter:
public class MyNiftyAdapter extends CursorAdapter{
private LayoutInflater mInflater;
private Cursor cur;
public MyNiftyAdapter(Context context, Cursor c) {
super(context,c);
this.mInflater = LayoutInflater.from(context);
this.cur = c;
}
public MyNiftyAdapter(Context context, Cursor c, boolean autoRequery)
{
super(context, c, autoRequery);
this.mInflater = LayoutInflater.from(context);
this.cur = c;
}
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
ViewHolder viewHolder;
if(convertView == null)
{
convertView = this.mInflater.inflate(R.layout.single_item, null);
viewHolder = new ViewHolder();
viewHolder.name = (TextView)convertView.findViewById(R.id.txtName);
viewHolder.Age = (TextView)convertView.findViewById(R.id.studentage);
viewHolder.Id = (TextView)convertView.findViewById(R.id.rowid);
viewHolder.Image = (CircularImageView)convertView.findViewById(R.id.imgFood);
convertView.setTag(viewHolder);
}else
{
viewHolder = (ViewHolder)convertView.getTag();
}
this.cur.moveToPosition(position);
viewHolder.name.setText(this.cur.getString(this.cur.getColumnIndex(SQLiteHelper.NAME)));
viewHolder.Age.setText(this.cur.getString(this.cur.getColumnIndex(SQLiteHelper.AGE)));
viewHolder.Id.setText(this.cur.getString(this.cur.getColumnIndex(SQLiteHelper._ID)));
Uri jg = Uri.parse(this.cur.getString(this.cur.getColumnIndex("imagepath")));
viewHolder.Image.setImageURI(jg);
return convertView;
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
// Dont need to do anything here
return null;
}
static class ViewHolder
{
TextView name;
TextView Age;
TextView Id;
CircularImageView Image;
}
}
When I select large images I get a out of memory error:
java.lang.OutOfMemoryError: Failed to allocate a 21566748 byte allocation with 8448604 free bytes and 8MB until OOM
I have seen that the bitmap should be scaled down, but I'm just using the Uri of the file and displaying it. Is there anyway to avoid this issue as I don't want to save another(compressed) file to the device?
I have tried android:hardwareAccelerated="false" in manifest.
This is what worked for me:
I ended up using picasso as the answers suggested, but something important to note is that when loading a URI from device is to load the URI like this:
Picasso.with(mContext)
.load(new File(String.valueOf(Uri)))
.placeholder(R.drawable.profile)
.resize(800, 800)
.centerCrop()
.into(viewHolder.Image);
note that I used .load(new File(String.valueOf(Uri))) instead of .load(uri).
If you load the URI directly the view will return empty.
If you load a image from a url, it should be loaded like this:
Picasso.with(mContext)
.load(URL)
.placeholder(R.drawable.profile)
.resize(800, 800)
.centerCrop()
.into(viewHolder.Image);
So my adapter ended up looking like this:
public class MyNiftyAdapter extends CursorAdapter{
private LayoutInflater mInflater;
private Cursor cur;
private Context mContext;
public MyNiftyAdapter(Context context, Cursor c) {
super(context,c);
this.mInflater = LayoutInflater.from(context);
this.mContext = context;
this.cur = c;
}
public MyNiftyAdapter(Context context, Cursor c, boolean autoRequery)
{
super(context, c, autoRequery);
this.mInflater = LayoutInflater.from(context);
this.cur = c;
this.mContext = context;
}
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
ViewHolder viewHolder;
if(convertView == null)
{
convertView = this.mInflater.inflate(R.layout.single_item, null);
viewHolder = new ViewHolder();
viewHolder.name = (TextView)convertView.findViewById(R.id.txtName);
viewHolder.Age = (TextView)convertView.findViewById(R.id.studentage);
viewHolder.Id = (TextView)convertView.findViewById(R.id.rowid);
viewHolder.Image = (CircularImageView)convertView.findViewById(R.id.imgFood);
convertView.setTag(viewHolder);
}else
{
viewHolder = (ViewHolder)convertView.getTag();
}
this.cur.moveToPosition(position);
viewHolder.name.setText(this.cur.getString(this.cur.getColumnIndex(SQLiteHelper.NAME)));
viewHolder.Age.setText(this.cur.getString(this.cur.getColumnIndex(SQLiteHelper.AGE)));
viewHolder.Id.setText(this.cur.getString(this.cur.getColumnIndex(SQLiteHelper._ID)));
Uri jg = Uri.parse(this.cur.getString(this.cur.getColumnIndex("imagepath")));
Picasso.with(mContext)
.load(new File(String.valueOf(jg)))
.placeholder(R.drawable.profile)
.resize(800, 800)
.centerCrop()
.into(viewHolder.Image);
return convertView;
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
// Dont need to do anything here
return null;
}
static class ViewHolder
{
TextView name;
TextView Age;
TextView Id;
CircularImageView Image;
}
}
Thank you all for the help.

You can use Picasso library to display images.
compile 'com.squareup.picasso:picasso:2.5.2'
And in your adapter something like this :
Uri jg = Uri.parse(this.cur.getString(this.cur.getColumnIndex("imagepath")));
Picasso.with(context).load(jg).into(viewHolder.Image);

For Context
public class MyNiftyAdapter extends CursorAdapter{
private LayoutInflater mInflater;
private Cursor cur;
private Context mContext;
public MyNiftyAdapter(Context context, Cursor c) {
super(context,c);
this.mInflater = LayoutInflater.from(context);
this.mContext= context;
this.cur = c;
}
public MyNiftyAdapter(Context context, Cursor c, boolean autoRequery)
{
super(context, c, autoRequery);
this.mInflater = LayoutInflater.from(context);
this.cur = c;
this.mContext= context;
}
an then use :
Uri jg = Uri.parse(this.cur.getString(this.cur.getColumnIndex("imagepath")));
Picasso.with(mContext).load(jg).into(viewHolder.Image);

Related

class adapter in android studio

i have class adapter that contain image view and text view , i receive arraylist from the post excute then pass this array to the my adapter, when run my application it's give me this error.
java.lang.NullPointerException: Attempt to read from field 'android.widget.ImageView com.gmplatform.gmp.MainActivity_navagation_custmoer$itemAdapter3$ViewHolder.ivpic1' on a null object reference
at com.gmplatform.gmp.MainActivity_navagation_custmoer$itemAdapter3$ViewHolder.access$2600(MainActivity_navagation_custmoer.java:2410)
at com.gmplatform.gmp.MainActivity_navagation_custmoer$itemAdapter3.getView(MainActivity_navagation_custmoer.java:2372)
below here the adapter class
public class itemAdapter3 extends BaseAdapter{
private ArrayList<favouriteitems> itemCatalog;
private int resource;
private LayoutInflater inflater;
public itemAdapter3(Context context, int resource, List<favouriteitems> objects) {
// super(context, resource, objects);
itemCatalog = (ArrayList<favouriteitems>) objects;
this.resource = resource;
inflater = (LayoutInflater) getApplicationContext().getSystemService(LAYOUT_INFLATER_SERVICE);
}
public void notifyDataSetChanged() {
super.notifyDataSetChanged();
}
#Override
public int getCount() {
return itemCatalog.size();
}
#Override
public Object getItem(int position) {
return itemCatalog.get(position);
}
#Override
public long getItemId(int position) {
return itemCatalog.indexOf(getItem(position));
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
final int pos = position;
final favouriteitems items = itemCatalog.get(pos);
ViewHolder holder = null;
if (convertView == null) {
holder = new ViewHolder();
convertView = inflater.inflate(resource, null);
holder.ivpic1 = (ImageView) convertView.findViewById(R.id.iv);
holder.st = (ImageView)convertView.findViewById(R.id.st);
holder.sh=(ImageView)convertView.findViewById(R.id.sh);
holder.name = (TextView) convertView.findViewById(R.id.t4);
holder.pr = (TextView) convertView.findViewById(R.id.textView32);
holder.av = (TextView) convertView.findViewById(R.id.av);
holder.t5 = (TextView) convertView.findViewById(R.id.t5);
final ViewHolder finalHolder = holder;
}
else {
holder = (MainActivity_navagation_custmoer.itemAdapter3.ViewHolder) convertView.getTag();
}
final ProgressBar progressBar = (ProgressBar) convertView.findViewById(R.id.progressBar);
// Then later, when you want to display image
ImageLoader.getInstance().displayImage(itemCatalog.get(position).getM1().getLoc2().getItmc().getIt().getPic(), holder.ivpic1 , new ImageLoadingListener() {
#Override
public void onLoadingStarted(String imageUri, View view) {
progressBar.setVisibility(View.VISIBLE);
}
#Override
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
progressBar.setVisibility(View.GONE);
}
#Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
progressBar.setVisibility(View.GONE);
}
#Override
public void onLoadingCancelled(String imageUri, View view) {
progressBar.setVisibility(View.GONE);
}
});
holder.pr.setText(String.valueOf(itemCatalog.get(position).getM1().getLoc2().getItmc().getPrice() + " SR"));
holder.name.setText(itemCatalog.get(position).getM1().getLoc2().getItmc().getIt().getName());
if (itemCatalog.get(position).getM1().getLoc2().getItmc().isInSc() == true) {
holder.sh.setImageResource(R.drawable.customern2);
} else {
holder.sh.setImageResource(R.drawable.customern1);
}
holder.t5.setText(itemCatalog.get(position).getM1().getmName()+" ("+itemCatalog.get(position).getM1().getLoc2().getArea()+" )");
return convertView;
}
class ViewHolder {
private ImageView ivpic1,sh,st;
private TextView name,pr,t5,av;
}
}
There is a problem in your Constructor:
Uncomment the super call and run it, you will stop getting null pointer exception.
public itemAdapter3(Context context, int resource, List<favouriteitems> objects) {
super(context, resource, objects);
itemCatalog = (ArrayList<favouriteitems>) objects;
this.resource = resource;
inflater = (LayoutInflater) getApplicationContext().getSystemService(LAYOUT_INFLATER_SERVICE);
}
> holder = (ViewHolder) convertView.getTag();
you donot have a corrensponding convertView.setTag(holder) after the holder was created with holder = new ViewHolder();.

Android viewholder onclick updates more instances

For my android application i'm using the ViewHolder pattern.
Whenever i click on an imageview in my listview the row get updated but other rows are also getting updated.
Whenever i click the "like" button other images also receive the image that the are liked by the user. How is this possible?
Here is my viewholder code:
public class MyAdapter extends ArrayAdapter<MyModel> {
Context context;
int layoutResourceId;
List<MyModel> data = null;
public MyAdapter(Context context, int layoutResourceId,
List<MyModel> data) {
super(context, layoutResourceId, data);
this.layoutResourceId = layoutResourceId;
this.context = context;
this.data = data;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
final BlockHolder holder;
if (row == null) {
LayoutInflater inflater = ((Activity) context).getLayoutInflater();
row = inflater.inflate(layoutResourceId, parent, false);
holder = new BlockHolder();
holder.imgIcon = (ImageView) row.findViewById(R.id.imgIcon);
holder.totalVotes = (TextView) row.findViewById(R.id.totalVotes);
holder.likeIcon = (ImageView) row.findViewById(R.id.likeIcon);
holder.dislikeIcon = (ImageView) row.findViewById(R.id.dislikeIcon);
holder.saveIcon = (ImageView) row.findViewById(R.id.saveIcon);
row.setTag(holder);
} else {
holder = (BlockHolder) row.getTag();
}
MyModel myBlock = data.get(position);
ImageLoader.getInstance().displayImage(myBlock.getImage(),
holder.imgIcon);
final int totalLikes = myBlock.getLikes() - myBlock.getDislikes();
holder.totalVotes.setText(totalLikes + " likes");
final ImageView like = holder.likeIcon;
holder.likeIcon.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// ALSO UPDATES OTHER VIEWS...
holder.likeIcon.setBackgroundResource(R.drawable.thumbs_up_mouse);
}
});
return row;
}
private static class BlockHolder{
ImageView imgIcon;
TextView totalVotes;
ImageView dislikeIcon;
ImageView likeIcon;
ImageView saveIcon;
}

Weird behavior with CustomAdapter and CustomTextView

I have a custom TextView which I'm using in a custom GridView adapter. The custom TextView is using a custom font for translation purposes. This works fairly well in devices which have the locale installed by default.
However, in devices, in which the language is not installed, it is displaying a strange behavior. When the app loads the first time, the TextViews don't display with the custom font. However when I press the refresh button to reload the fragment, the TextViews display with the custom font.
I'm not sure why this is happening.
This is happening with all the custom Adapters in my application in which I'm using the custom TextView.
Pretty basic adapter:
public class CalendarWeekAdapter extends BaseAdapter{
private String[] weekdays;
Context mContext;
private LayoutInflater mInflater;
public CalendarWeekAdapter(Context context, int firstDay)
{
mContext=context;
mInflater = LayoutInflater.from(context);
weekdays = context.getResources().getStringArray(R.array.weekdays);
}
public int getCount()
{
return weekdays.length;
}
public Object getItem(int position)
{
return position;
}
public long getItemId(int position)
{
return position;
}
public View getView(int position, View convertView, ViewGroup parent)
{
ViewHolder holder=null;
if(convertView==null)
{
convertView = mInflater.inflate(R.layout.calendar_week, parent,false);
holder = new ViewHolder();
holder.txtWeekdays=(CustomTextView)convertView.findViewById(R.id.weekdays);
if(position==0)
{
convertView.setTag(holder);
}
}
else
{
holder = (ViewHolder) convertView.getTag();
}
holder.txtWeekdays.setText(weekdays[position]);
return convertView;
}
}
class ViewHolder
{
CustomTextView txtWeekdays;
}
Basic CustomTextView:
public class CustomTextView extends TextView {
public CustomTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public CustomTextView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public CustomTextView(Context context) {
super(context);
init();
}
private void init() {
if (!isInEditMode()) {
setTypeface(Utils.getFont(getContext()));
}
}
}
This is may not be the answer, but i don't understand this if-statement:
holder.txtWeekdays=(CustomTextView)convertView.findViewById(R.id.weekdays);
if(position==0)
{
convertView.setTag(holder);
}
Why is it there? All your newly inflated convertViews should have holder as their tag.
Just remove the 'if' around the convertView.setTag(holder):
if(convertView==null)
{
convertView = mInflater.inflate(R.layout.calendar_week, null,false);
holder = new ViewHolder();
holder.txtWeekdays=(CustomTextView)convertView.findViewById(R.id.weekdays);
convertView.setTag(holder);
}
...
and see if this will improve or even fix your situation.

Get ListItem position in custom SimpleLayout

I have a custom ListView with a TextView and a CheckBox. I also have a custom SimpleAdapter in which I override the getView() method and am able to retrieve clicks on the TextView and CheckBox changes. My problem is that I don't know how to get the correct clicked ListItem or CheckBox inside the OnCheckedChanged or OnClick.
UPDATE added whole CustomAdapter class:
public class CustomAdapter extends SimpleAdapter {
private LayoutInflater mInflater;
private List<HashMap<String, String>> mItems = null;
private Context mContext;
private int mPosicion;
public CustomAdapter(Context context, List<HashMap<String, String>> items, int resource, String[] from, int[] to) {
super(context, items, resource, from, to);
mInflater = LayoutInflater.from(context);
mItems = items;
mContext = context;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
mPosicion = position;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.custom_row_view, null);
holder = new ViewHolder();
holder.chkbxEstado = (CheckBox) convertView.findViewById(R.id.chkbxCompletado);
holder.txtTextoAgenda = (TextView) convertView.findViewById(R.id.txtTextoLista);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.txtTextoAgenda.setText(mItems.get(position).get("descripcion"));
convertView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Log.i("Posicion",""+mPosicion);//I don't know how to retrieve clicked position
}
});
holder.chkbxEstado.setOnCheckedChangeListener(new OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
Log.i("Posicion",""+mPosicion);//Or checked
}
});
return convertView;
}
private static class ViewHolder {
TextView txtTextoAgenda;
CheckBox chkbxEstado;
}
}
Any help is appreciated.
I found a solution. If anybody knows a better one, please let me know. Working CustomAdapter class:
public class CustomAdapter extends SimpleAdapter {
private LayoutInflater mInflater;
private List<HashMap<String, String>> mItems = null;
private Context mContext;
private OnClickListener mClick;
private OnCheckedChangeListener mChecked;
public CustomAdapter(Context context, List<HashMap<String, String>> items, int resource, String[] from, int[] to) {
super(context, items, resource, from, to);
mInflater = LayoutInflater.from(context);
mItems = items;
mContext = context;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.custom_row_view, null);
holder = new ViewHolder();
holder.chkbxEstado = (CheckBox) convertView.findViewById(R.id.chkbxCompletado);
holder.txtTextoAgenda = (TextView) convertView.findViewById(R.id.txtTextoLista);
holder.posicion = position; //Add the new position into the holder for each row.
convertView.setTag(holder);
mClick = new OnClickListener() {
#Override
public void onClick(View v) {
ViewHolder viewHolder = getViewHolder(v); //Get the ViewHolder for the clicked row.
Log.i("Posicion",""+v.posicion);
}
};
mChecked = new OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
ViewHolder viewHolder = getViewHolder(buttonView); //Get the ViewHolder for the clicked CheckBox
Log.i("Posicion",""+viewHolder.posicion);
}
};
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.txtTextoAgenda.setText(mItems.get(position).get("descripcion"));
convertView.setOnClickListener(mClick);
holder.chkbxEstado.setOnCheckedChangeListener(mChecked);
return convertView;
}
public ViewHolder getViewHolder(View v){ //This method returns the ViewHolder stored in the tag if available, if not it recursively checks the parent's tag.
if(v.getTag() == null){
return getViewHolder((View)v.getParent());
}
return (ViewHolder)v.getTag();
}
private static class ViewHolder {
TextView txtTextoAgenda;
CheckBox chkbxEstado;
int posicion; //Added position attribute to ViewHolder class.
}
}
Edited to re-use onClickListener() and onCheckedChangedListener() instances.
The OP's solution works. But if you are extending a CursorAdapter, the position parameter does not exist in newView() and bindView(). What to do?
They do supply a Cursor. Use cursor.getPosition(). It does the same thing.

Android custom cursor adapter

I have created a custom cursor adapter which fetches the value from the database and displays it in my defined view.
I have one textview and imageview.
Depends on the text in the database image changes accordingly.
I have defined MyArrayAdapter as follows, but it gives an error.
public class MyArrayAdapter extends SimpleCursorAdapter {
private final Context context;
private final Cursor cursor;
private final String[] from;
static class ViewHolder {
public ImageView imageView;
public TextView textView;
}
public MyArrayAdapter(Context context, int layout, Cursor c, String[] from,
int[] to) {
super(context, layout, c, from, to);
this.context = context;
this.cursor = c;
this.from = from;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View rowView = convertView;
ViewHolder holder;
if (rowView == null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
rowView = inflater.inflate(R.layout.todo_row,null,true);
holder = new ViewHolder();
holder.textView = (TextView) rowView.findViewById(R.id.todo_edit_summary);
holder.imageView = (ImageView) rowView.findViewById(R.id.icon);
rowView.setTag(holder);
}else{
holder = (ViewHolder) rowView.getTag();
}
String s = cursor.getString(cursor.getColumnIndex(from[0]));
holder.textView.setText(s);
if (cursor.getString(cursor.getColumnIndex("category")) == "Urgent"){
holder.imageView.setImageResource(R.drawable.urgent);
}else{
holder.imageView.setImageResource(R.drawable.reminder);
}
return rowView;
}
}
(Update from deleted answer)
The following code is working fine, but they are showing the same value in each and every entry in the ListView. I have extended SimpleCursorAdapter. Am I making any mistakes with managing the cursor?
public View getView(int position, View convertView, ViewGroup parent) {
View rowView = convertView;
ViewHolder holder;
if (rowView == null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
rowView = inflater.inflate(R.layout.todo_row,null,true);
holder = new ViewHolder();
holder.textView = (TextView) rowView.findViewById(R.id.label);
holder.imageView = (ImageView) rowView.findViewById(R.id.icon);
rowView.setTag(holder);
}else{
holder = (ViewHolder) rowView.getTag();
}
String s = cursor.getString(cursor.getColumnIndex(from[0]));
holder.textView.setText(s);
if ((cursor.getString(cursor.getColumnIndex("category"))).equals("Urgent")){
holder.imageView.setImageResource(R.drawable.reminder);
}else{
holder.imageView.setImageResource(R.drawable.urgent);
}
return rowView;
}
As far as I can tell the only part of SimpleCursorAdapter you are using is the from parameter which you can easily substitute with a literal string since you are extending it. While I'm not familiar with the "guts" of the code it's pretty safe to assume that since getView is not mentioned in SimpleCursorAdapter documentation that it's not meant to be overridden lightly since doing so can break it like in your example.
Based on what you have, I've adapted it to extend CursorAdapter instead and by splitting the code you had in getView into newView and bindView, both of which are defined as abstract methods in CursorAdapter.
public class MyCursorAdapter extends CursorAdapter {
static class ViewHolder {
public ImageView imageView;
public TextView textView;
}
public MyArrayAdapter(Context context, Cursor c) {
super(context, c);
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rowView = inflater.inflate(R.layout.todo_row,null,true);
ViewHolder holder = new ViewHolder();
holder.textView = (TextView) rowView.findViewById(R.id.todo_edit_summary);
holder.imageView = (ImageView) rowView.findViewById(R.id.icon);
rowView.setTag(holder);
return rowView;
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
ViewHolder holder = (ViewHolder) view.getTag();
String s = cursor.getString(cursor.getColumnIndex("whatever from[] represented"));
holder.textView.setText(s);
if (cursor.getString(cursor.getColumnIndex("category")) == "Urgent"){
holder.imageView.setImageResource(R.drawable.urgent);
}else{
holder.imageView.setImageResource(R.drawable.reminder);
}
}
}
To compare between value of two strings use equals() of String class. Donot use == operator in this case
So use the following code
if ((cursor.getString(cursor.getColumnIndex("category"))).equals("Urgent")){
holder.imageView.setImageResource(R.drawable.urgent);
}else{
holder.imageView.setImageResource(R.drawable.reminder);
}

Categories