basically I have a gridview, in which I show my products (from 10 or 20 up to 1K+ items) each item has a couple of custom textviews, and an Imageview.
I use ViewHolder pattern and also Picasso library to load images asynchronously. I have no problem with showing items, speed or whatsoever, so I think it's safe to say that my getview function works quite nice, although I have one major problem which is with scrolling the gridview, up and down, my memory monitor only shows increase of course the increment rate depends on the item-view contents as when I commented the image loading part for example, the rate decreased.
what I guess is somehow the generated views are always staying in the memory and something prevents them from getting collected by the GC.
although sometimes, when I scroll slowly GC comes and collects and memory gets free, but scrolling fast will always result in OOM, sooner or later.
my main question is that if I have a memory leak or not?! since the gc sometimes works (slow scrlling), and sometimes lets the app crashes with OOM Exception.
and clearly I hope to find out my problem and every little bit of help would be appreciated in advance.
xml of the fragment-layout which hosts the Gridview:
<GridView
android:id="#+id/gv_Main"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_marginLeft="2dp"
android:layout_marginRight="2dp"
android:animationCache="false"
android:clickable="true"
android:descendantFocusability="beforeDescendants"
android:gravity="center"
android:scrollingCache="false"
android:verticalSpacing="10dp"
/>
my adapter(this extends CursorAdapter, although I have exactly same problem whit baseadapter as well):
public class ShygunInventoryGridCursorAdapter extends CursorAdapter {
private Context mContext;
private Cursor cursor;
private int numColumns;
private int listItemId, listItemNoImageId, gridItemId;
private String[] type;
private String[] columnTitle;
private int[] viewId;
private String textTemp;
private CyberSetting cyberSetting;
private String unitDesc = "";
private boolean isMain;
private int size;
private LayoutInflater inflater;
private int thumbSize = 200;
private File pic;
private TPictureCatalog pictureCatalog;
private TProductCatalog productCatalog;
//private ViewGroup hostActivity;
//int idTemp;
private LayoutInflater cursorInflater;
public ShygunInventoryGridCursorAdapter(Context context, Cursor c, int flags) {
super(context, c, flags);
this.mContext = context;
cursorInflater = (LayoutInflater) context.getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
//this.cursor = c;
productCatalog=TProductCatalog.getInstance();
cyberSetting=CyberSetting.getInstance();
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View view = cursorInflater.inflate( R.layout.listview_inventory_product_griditems, parent, false);
ViewHolder holder = new ViewHolder();
holder.tvAttachment = (ImageView) view.findViewById(R.id.iv_inventory_products_griditems_attachment);
holder.imageCount = (TextView) view.findViewById(R.id.imagecount);
holder.tvItemCode = (TextView) view.findViewById(R.id.tv_inventory_products_griditems_ItemCode);
holder.tvProductName = (TextView) view.findViewById(R.id.tv_inventory_products_griditems_Title);
holder.tvPrice = (RialTextView) view.findViewById(R.id.tv_inventory_products_griditems_Price);
holder.tvRemain = (TextView) view.findViewById( R.id.tv_inventory_products_griditems_Remain);
holder.btnMore =(com.rey.material.widget.Button) view.findViewById(R.id.btn_inventory_products_griditems_More);
holder.btnPlus = (com.rey.material.widget.Button) view.findViewById(R.id.btn_inventory_products_griditems_addOne);
view.setTag(holder);
Log.d("CurAd","newView");
return view;
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
Log.d("CurAd","bindView");
ViewHolder holder;
holder = (ViewHolder) view.getTag();
holder.itemId = cursor.getString(cursor.getColumnIndex(DatabaseColumnContent.COL_PRODUCT_ITEM_ID.toString()));
TSimpleProduct tempProduct = productCatalog.getSimpleProductById(Integer.parseInt(holder.itemId));
holder.itemGuId = cursor.getString(cursor.getColumnIndex(DatabaseColumnContent.COL_PRODUCT_GUID.toString()));
holder.tvItemCode.setText(cursor.getString(cursor.getColumnIndex(DatabaseColumnContent.COL_PRODUCT_ITEMCODE.toString())));
holder.tvProductName.setText(cursor.getString(cursor.getColumnIndex(DatabaseColumnContent.COL_PRODUCT_ITEMDESC.toString())));
//Remain
if (cyberSetting.getSettingValue(TCyberSettingKey.SHOWITEMREMAIN).equals("1")) {
textTemp = (mContext.getString(R.string.restrictedInfo));
} else {
if (tempProduct.getDefaultUnitValue() == 2 && tempProduct.isUnitDependent()) {
String titleRemain2 = DatabaseColumnContent.COL_PRODUCT_CURSOR_REMAIN2.toString();
textTemp = cursor.getString(cursor.getColumnIndex(titleRemain2));
}
if (cyberSetting.getSettingValue(TCyberSettingKey.SHOWITEMREMAIN).equals("2")) {
if (textTemp == null) {
textTemp = "0";
}
int t = Integer.parseInt(textTemp);
if (t > 0) {
textTemp = mContext.getString(R.string.productAvailable);
} else {
textTemp = mContext.getString(R.string.productUnAvailable);
}
}
}
holder.tvRemain.setText(textTemp);
//Price
String priceLevel = "0";
try {
Register register = Register.getInstance();
priceLevel = register.getPriceLevel();
} catch (NoDaoSetException e) {
e.printStackTrace();
}
if(!priceLevel.equals("0"))
textTemp = cursor.getString(cursor.getColumnIndex(priceLevel));
else
textTemp = "0.0";
if (tempProduct.getDefaultUnitValue() == 2 && tempProduct.isUnitDependent()) {
double price2;
price2 = TLineItem.convertPrice1ToPrice2(Double.parseDouble(textTemp), tempProduct.isUnit1Bigger(), tempProduct.getUnitCoef());
textTemp = TGeneralTools.ConvertDoubleToEnglishString(price2);
if (tempProduct.getUnitDesc2() != null && !tempProduct.getUnitDesc2().equals(""))
unitDesc = " (" + tempProduct.getCompleteUnitDesc2() + ")";
} else {
if (tempProduct.getUnitDesc1() != null && !tempProduct.getUnitDesc1().equals(""))
unitDesc = " (" + tempProduct.getCompleteUnitDesc1() + ")";
}
holder.priceDef = textTemp;
holder.tvPrice.setText(textTemp + unitDesc);
holder.tvRemain.setText(holder.tvRemain.getText() + unitDesc);
//image
pictureCatalog = TPictureCatalog.getInstance();
String defGuid = "";
if (tempProduct.getHasAttachContent() >= 1 && pictureCatalog.isDownloadedAlbumAvailable()) {
defGuid = pictureCatalog.getDefaultPictureGuid(holder.itemGuId);
if (tempProduct.getHasAttachContent() == 1) {
holder.imageCount.setVisibility(View.GONE);
} else {
holder.imageCount.setVisibility(View.VISIBLE);
holder.imageCount.setText(String.valueOf(tempProduct.getHasAttachContent()));
}
} else {
holder.imageCount.setVisibility(View.GONE);
}
String filename = Environment.getExternalStorageDirectory().getPath()
+ FileAddressContent.APPLICATION_HOME_DIRECTORY
+ FileAddressContent.PICTURES_ROOT_DIRECTORY
//+ FileAddressContent.PICTURES_THUMBS_DIRECTORY.toString()
+ defGuid + FileAddressContent.PICTURES_EXTENSION;
pic = new File(filename);
if (pic.exists())
Picasso.with(mContext)
.load(pic)
.error(R.drawable.noimage)
//.placeholder(R.drawable.loading)
.resize(thumbSize, thumbSize)
.centerInside()
.into(holder.tvAttachment);
else
Picasso.with(mContext)
.load(R.drawable.noimage)
.resize(thumbSize, thumbSize)
.centerInside()
.into(holder.tvAttachment);
holder.tvAttachment.setMinimumHeight(thumbSize);
setupGridView(view, holder);
}
private void setupGridView(View view, final ViewHolder holder) {
/*final ImageView iv = (ImageView) view.findViewById(R.id.iv_inventory_products_griditems_attachment);
com.rey.material.widget.Button btMore = (com.rey.material.widget.Button) view.findViewById(R.id.btn_inventory_products_griditems_More);
com.rey.material.widget.Button btPlus = (com.rey.material.widget.Button) view.findViewById(R.id.btn_inventory_products_griditems_addOne);
//final TextView tvTitle = (TextView) view.findViewById(R.id.tv_inventory_products_griditems_Title);
final RialTextView tvPrice = (RialTextView) view.findViewById(R.id.tv_inventory_products_griditems_Price);
final TextView tvItemId = (TextView) view.findViewById(R.id.tv_inventory_products_griditems_ItemId);
final TextView tvGuId = (TextView) view.findViewById(R.id.tv_inventory_products_griditems_GuId);
//final TextView tvRemain = (TextView) view.findViewById(R.id.tv_inventory_products_griditems_Remain);*/
holder.tvAttachment.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
/* BitmapDrawable bitmapDrawable = ((BitmapDrawable) iv.getDrawable());*/
showImageDialog(holder.itemGuId);
}
});
holder.btnMore.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
/*addToInvoiceDialog(tvTitle.getText().toString(), tvPrice.getText().toString(), tvItemId.getText().toString(),
tvRemain.getText().toString(),iv.getDrawable(),tvGuId.getText().toString());*/
showAddToInvoiceFragment(holder.itemId, holder.priceDef);
}
});
holder.btnPlus.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//InventoryFragment.AddToInvoice(holder.itemId, 1, holder.priceDef, null, null);
}
});
}
public Object getItem(int position) {
return position;
}
public long getItemId(int position) {
return position;
}
private void showImageDialog(String ReferenceGuId) {
//InventoryFragment.openAlbum(ReferenceGuId);
}
private void showAddToInvoiceFragment(String itemId, String priceDef) {
//InventoryFragment.showAddToInvoiceFragment(itemId, priceDef);
}
}
this is the part in my java code where I set the adapter:
ShygunInventoryGridCursorAdapter listAdapter1 = new ShygunInventoryGridCursorAdapter(context,cursor, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
gridView.setNumColumns(colCount);
gridView.setAdapter(listAdapter1);
MAT screen( I could not reach anywhere here, I mean non of roots or ... was my own code)
Not to mention, adding System.gc() at the end of get View method in the adapter will solve everything, but I'm sure it's not the right thing to do
Related
I just created an application on android optimized for the mobile phone. Now as the client asked to optimize for 17inch screen size without building separate apk. I almost implemented with some minor UI changes. But in case of listviews, they need Gridviews. So What I did was I created a separate layout for bigger screen with gridview I used a code to detect the screen size and in activity I used a code to detect the screen size and if it's bigger, then I initialized Gridview and after async task i added to a cardview adapter and set the gridview adapter to cardview adapter.
I switched gridview and listview as:
if(Constants.isScreenLarge(getActivity())) {
// width > height, better to use Landscape
mygrid = (GridView) getView().findViewById(R.id.card_gridview);
} else {
listView = (ListView)getView().findViewById(R.id.card_listView);
listView.addHeaderView(new View(getActivity()));
listView.addFooterView(new View(getActivity()));
}
Using Restclient helper:
public void fetchTransaction(){
loading.setVisibility(View.VISIBLE);
pd.setMessage("Fetching Transactions..Please Wait");
pd.setCancelable(false);
pd.show();
String fromDate = from.getText().toString();
String toDate = to.getText().toString();
//mydb.deleteAllTransactions();
cardArrayAdapter = new CardArrayAdapter(this.getActivity().getApplicationContext(), R.layout.list_item_card);
cardArrayAdapter.clear();
String android_id = Settings.Secure.getString(getActivity().getContentResolver(),
Settings.Secure.ANDROID_ID);
String userid = prefs.getString("userId","0");
Log.e("TRANSACTON", Constants.BASE_URL_TRANSACTIONS+"?deviceid="+android_id+"&userid="+userid+"&from="+fromDate+"&to="+toDate);
RestClientHelper.getInstance().get(Constants.BASE_URL_TRANSACTIONS+"?deviceid="+android_id+"&userid="+userid+"&from="+fromDate+"&to="+toDate, new RestClientHelper.RestClientListener() {
#Override
public void onSuccess(String response) {
pd.dismiss();
try{
mydb.deleteAllTransactions();
JSONObject result = new JSONObject(response);
JSONArray posts = result.optJSONArray("result");
for (int i = 0; i < posts.length(); i++) {
JSONObject post = posts.optJSONObject(i);
String id = post.optString("id");
String heading = post.optString("heading");
String description = post.optString("description");
String amount = post.optString("amount");
String imageurl = post.optString("imageurl");
String credit = post.optString("credit");
String date = post.optString("date");
String type = post.optString("type");
String service = post.optString("service");
String cost = post.optString("cost");
String balance = post.optString("balance");
String amountinfo = post.optString("amountinfo");
Log.e("ENTERING TO DB","DATA"+i);
Card card = new Card(heading, description,amount,date,credit,imageurl,cost,balance,amountinfo,id,service);
cardArrayAdapter.add(card);
//mydb.addTransactionContact(new TransactionData(heading, description, amount, imageurl,credit,type,date));
}
} catch (JSONException e) {
e.printStackTrace();
}
catch(Exception e){
}
finally {
//showTransaction();
rl = (RelativeLayout)getView().findViewById(R.id.noItems);
rl.setVisibility(view.INVISIBLE);
if(Constants.isScreenLarge(getActivity())) {
// width > height, better to use Landscape
mygrid.setAdapter(cardArrayAdapter);
} else {
listView.setAdapter(cardArrayAdapter);
}
cardArrayAdapter.notifyDataSetChanged();
}
}
#Override
public void onError(String error) {
pd.dismiss();
SnackbarManager.show(Snackbar.with(getActivity()) // context
.text("An Error occured.. Try after sometime"));
textView2.setText("Some Error Ocurred");
rl = (RelativeLayout)getView().findViewById(R.id.noItems);
rl.setVisibility(view.VISIBLE);
loading.setVisibility(View.INVISIBLE);
}
});
}
My CardArrayAdapter code is:
public class CardArrayAdapter extends ArrayAdapter<Card> {
private static final String TAG = "CardArrayAdapter";
private List<Card> cardList = new ArrayList<Card>();
private Context mContext;
DatabaseUserTable mydb;
PreferenceHelper prefs;
ProgressDialog pd;
static class CardViewHolder {
TextView line1;
TextView line2;
ImageView cimageView;
TextView amount;
TextView credit;
TextView date;
TextView cost;
TextView balance;
TextView amountinfo;
TextView costinfo;
TextView balanceinfo;
}
public CardArrayAdapter(Context context, int textViewResourceId) {
super(context, textViewResourceId);
this.mContext = context;
mydb = new DatabaseUserTable(this.mContext);
prefs = new PreferenceHelper(this.mContext);
pd = new ProgressDialog(this.mContext);
}
#Override
public void add(Card object) {
cardList.add(object);
super.add(object);
}
#Override
public int getCount() {
return this.cardList.size();
}
#Override
public Card getItem(int index) {
return this.cardList.get(index);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
CardViewHolder viewHolder;
if (row == null) {
LayoutInflater inflater = (LayoutInflater) this.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
row = inflater.inflate(R.layout.list_item_card, parent, false);
viewHolder = new CardViewHolder();
viewHolder.line1 = (TextView) row.findViewById(R.id.line1);
viewHolder.line2 = (TextView) row.findViewById(R.id.line2);
viewHolder.amount = (TextView) row.findViewById(R.id.amount);
viewHolder.date = (TextView) row.findViewById(R.id.date);
viewHolder.credit = (TextView) row.findViewById(R.id.credit);
viewHolder.cimageView = (ImageView) row.findViewById(R.id.imageId);
viewHolder.cost = (TextView) row.findViewById(R.id.cost);
viewHolder.balance = (TextView) row.findViewById(R.id.balance);
viewHolder.amountinfo = (TextView) row.findViewById(R.id.amountinfo);
viewHolder.costinfo = (TextView) row.findViewById(R.id.costinfo);
viewHolder.balanceinfo = (TextView) row.findViewById(R.id.balanceinfo);
row.setTag(viewHolder);
} else {
viewHolder = (CardViewHolder)row.getTag();
}
Card card = getItem(position);
viewHolder.line1.setText(card.getLine1());
viewHolder.line1.setTextColor(Color.parseColor("#000000"));
viewHolder.line2.setText(card.getLine2());
viewHolder.line2.setTextColor(Color.parseColor("#999999"));
viewHolder.amount.setText(card.getAmount());
viewHolder.amount.setTextColor(Color.parseColor("#000000"));
viewHolder.date.setText(card.getDate());
viewHolder.date.setTextColor(Color.parseColor("#999999"));
viewHolder.credit.setText(card.getCredit());
viewHolder.credit.setTextColor(Color.parseColor("#000000"));
viewHolder.cost.setText(card.getCost());
viewHolder.cost.setTextColor(Color.parseColor("#000000"));
viewHolder.balance.setText(card.getBalance());
viewHolder.balance.setTextColor(Color.parseColor("#000000"));
viewHolder.amountinfo.setText(card.getAmountinfo());
viewHolder.amountinfo.setTextColor(Color.parseColor("#000000"));
viewHolder.costinfo.setText("Cost");
viewHolder.costinfo.setTextColor(Color.parseColor("#000000"));
viewHolder.balanceinfo.setText("Balance");
viewHolder.balanceinfo.setTextColor(Color.parseColor("#000000"));
final String salesid = card.getSalesid();
final String servicename = card.getServiceName();
row.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
Log.e("TRANSACTIONS SERVICENAM",servicename);
if(servicename.equals("VOUCHER")) {
alertBox(v,salesid);
}
else if(servicename.equals("WALLET")) {
SnackbarManager.show(Snackbar.with(v.getRootView().getContext()) // context
.text("No Reprinting available for this transaction"));
}
else if(servicename.equals("ELECTRICITY")) {
errorAlert(v,"Printing not yet supported");
//alertBoxElectricity(v,salesid);
}
else if(servicename.equals("MONEY TRANSFER")) {
//errorAlert(v,"Printing not yet supported");
alertBoxMoney(v,salesid);
}
else{
alertBoxOther(v,salesid);
}
//Toast.makeText(v.getContext(), amountinfo, Toast.LENGTH_SHORT).show();
/*SnackbarManager.show(Snackbar.with(this) // context
.text(amountinfo));*/
}
});
Log.e("card.getLine1()",card.getLine1());
Log.e("TRASACTION URL",Constants.BASE_URL_IMAGE+card.getUrl());
Picasso.with(mContext).load(Constants.BASE_URL_IMAGE+card.getUrl()).fit().placeholder(R.drawable.mobeeloadicon).error(R.drawable.mobeeloadicon).into(viewHolder.cimageView);
return row;
}
But I got the error as:
java.lang.NullPointerException: Attempt to invoke virtual method 'void
android.widget.GridView.setAdapter(android.widget.ListAdapter)' on a
null object reference
How can I change CardArrayAdapter as GridViewAdapter preserving all the Card data. Any Help please
UPDATE
I got this right. It was a silly mistake. I was populating the wrong layout.
I`m create list of points of sales from SQLite using custom adapter
dbHelper = new YourVoiceDatabaseHelper(getActivity());
pointsList = new ArrayList<Point>();
List<Point> pl = new ArrayList<Point>();
PointsListArrayAdapter adapter;
adapter = new PointsListArrayAdapter(getActivity().getApplicationContext(),
pointsList,
getActivity().getFilesDir(),
getResources().getString(R.string.Local_Resorces_Folder),
dbHelper);
pointsListView.setAdapter(adapter);
pl = dbHelper.getAllPoints();
if(myLocation != null) {
for (Point el : pl) {
elLocation.setLatitude(Double.parseDouble(el.getLatitude()));
elLocation.setLongitude(Double.parseDouble(el.getLongtitude()));
if (myLocation.distanceTo(elLocation) <= 999) {
pointsList.add(el);
}
}
} else {
Toast.makeText(getActivity(), "Не удалось определить ваше местоположение, проверте ваши разрешения и настройки",
Toast.LENGTH_LONG).show();
} adapter.notifyDataSetChanged();
To custom adapter I send instance of my database ad using it in there and this slows my main tread. I`m trying to use AsyncTask in adapter to get images path from database and set images but Async do absolutely NOTHING
package ua.com.it_artel.tvoy_golos.service;
public class PointsListArrayAdapter extends BaseAdapter {
Context context;
private List<Point> pointsList;
private File appFolder;
private String resoursesFolder;
private YourVoiceDatabaseHelper dbHelper;
private String imagePath;
public PointsListArrayAdapter(Context context, List<Point> pointsList, File appFolder, String resoursesFolder, YourVoiceDatabaseHelper dbHelper) {
this.context = context;
this.pointsList = pointsList;
this.appFolder = appFolder;
this.resoursesFolder = resoursesFolder;
this.dbHelper = dbHelper;
}
#Override
public int getCount() {
return pointsList.size();
}
#Override
public Object getItem(int position) {
return pointsList.get(position);
}
#Override
public long getItemId(int position) {
return pointsList.get(position).getId();
}
static class ViewHolder {
ImageView image;
TextView adress;
TextView name;
TextView distance;
Location elLocation;
Point point;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
View rowView = convertView;
if(convertView == null){
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
rowView = inflater.inflate(R.layout.list_element_layout_type_1, parent, false);
viewHolder = new ViewHolder();
viewHolder.elLocation = new Location("");
viewHolder.image = (ImageView) rowView.findViewById(R.id.item_image);
viewHolder.adress = (TextView) rowView.findViewById(R.id.item_text);
viewHolder.name = (TextView) rowView.findViewById(R.id.item_name);
viewHolder.distance = (TextView) rowView.findViewById(R.id.item_distance);
rowView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) rowView.getTag();
}
viewHolder.elLocation.setLatitude(Double.parseDouble(pointsList.get(position).getLatitude()));
viewHolder.elLocation.setLongitude(Double.parseDouble(pointsList.get(position).getLongtitude()));
viewHolder.point = pointsList.get(position);
new AdapterImageSetter().execute(viewHolder);
viewHolder.adress.setText(pointsList.get(position).getAddress());
viewHolder.name.setText(dbHelper.getPartnerByUID(pointsList.get(position).getPartnerUid()).getName());
viewHolder.distance.setText(String.valueOf(Math.round(MyLocationListener.myLocation.distanceTo(viewHolder.elLocation))) + " м");
return rowView;
}
private class AdapterImageSetter extends AsyncTask<ViewHolder, Void, ViewHolder> {
#Override
protected ViewHolder doInBackground(ViewHolder... params) {
ViewHolder viewHolder = params[0];
imagePath = dbHelper.getPointsPartnerImage(viewHolder.point.getPartnerUid());
if (imagePath.equals("")) {
viewHolder.image.setImageResource(R.drawable.no_image);
} else {
Drawable dr = Drawable.createFromPath(appFolder.getPath() + "/" + resoursesFolder + "/" + imagePath);
viewHolder.image.setImageDrawable(dr);
}
return viewHolder;
}
}
}
I have tried different variations in AsyncTask and Runnable but it doesn`t work, like if there are no code.. Hope u understand my English and someone give advice
you can not perform ui related operations in doInBackground method for ui related operation just use onpostexcecute method
more about asynch task http://developer.android.com/reference/android/os/AsyncTask.html
in your code you you setting imege
if (imagePath.equals("")) {
viewHolder.image.setImageResource(R.drawable.no_image);
} else {
Drawable dr = Drawable.createFromPath(appFolder.getPath() + "/" + resoursesFolder + "/" + imagePath);
viewHolder.image.setImageDrawable(dr);
}
just add this code in onPostExecute
It`s work! I also add some code in Async, all work BUT but toooo much time is necessary for images and names appeared.
private class AdapterImageSetter extends AsyncTask {
#Override
protected ViewHolder doInBackground(ViewHolder... params) {
ViewHolder viewHolder = params[0];
viewHolder.imagePath = dbHelper.getPointsPartnerImage(viewHolder.point.getPartnerUid());
viewHolder.pointName = dbHelper.getPartnerByUID(viewHolder.point.getPartnerUid()).getName();
return viewHolder;
}
#Override
protected void onPostExecute(ViewHolder viewHolder) {
if (viewHolder.imagePath.equals("")) {
viewHolder.image.setImageResource(R.drawable.no_image);
} else {
Drawable dr = Drawable.createFromPath(appFolder.getPath() + "/" + resoursesFolder + "/" + viewHolder.imagePath);
viewHolder.image.setImageDrawable(dr);
}
viewHolder.name.setText(viewHolder.pointName);
Log.i("IMAGE PATH", viewHolder.imagePath);
Log.i("PARTNER UID", viewHolder.point.getPartnerUid());
}
}
For more information: this list is on first screen when u open app.
In first activity I replace fragment and шт the fragment I'm doing all the operations. have any ideas how to speed up operations in AsyncTask?
Thank you very much for the help!
im try to make listview with dynamic images, using asyntask its download image and set into listview. my problem is while scroll down images get randomly changed..
class ps1 extends ArrayAdapter<String> {
Context context;
String[] images1;
List mList;
String[] namearray;
String[] rating;
static class ViewHolder {
ImageView localImageView1;
ImageView localImageView2;
ImageView localImageView3;
}
ps1(Context paramContext, String[] paramArrayOfString1, String[] paramArrayOfString2, String[] paramArrayOfString3) {
super(paramContext, R.layout.list2, R.id.imageView1, paramArrayOfString1);
this.context = paramContext;
this.images1 = paramArrayOfString3;
this.namearray = paramArrayOfString1;
this.rating = paramArrayOfString2;
}
public View getView(int paramInt, View paramView, ViewGroup paramViewGroup) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(context.LAYOUT_INFLATER_SERVICE);
ViewHolder viewHolder = new ViewHolder();
if (paramView == null) {
paramView = inflater.inflate(R.layout.list2, paramViewGroup, false);
}
viewHolder.localImageView1 = (ImageView) paramView
.findViewById(R.id.imageView1);
viewHolder.localImageView2 = (ImageView) paramView
.findViewById(R.id.imageView2);
viewHolder.localImageView3 = (ImageView) paramView
.findViewById(R.id.imageView3);
viewHolder.localImageView1.setScaleType(ImageView.ScaleType.FIT_XY);
viewHolder.localImageView2.setScaleType(ImageView.ScaleType.FIT_XY);
viewHolder.localImageView3.setScaleType(ImageView.ScaleType.FIT_XY);
viewHolder.localImageView1.setTag(this.namearray[paramInt]);
new LoadImage().execute(viewHolder.localImageView1);
viewHolder.localImageView2.setTag(this.rating[paramInt]);
new LoadImage().execute(viewHolder.localImageView2);
viewHolder.localImageView3.setTag(this.images1[paramInt]);
new LoadImage().execute(viewHolder.localImageView3);
return paramView;
}
}
class LoadImage extends AsyncTask<Object, Void, Bitmap> {
private ImageView imv;
private Bitmap download_Image(String paramString) {
Bitmap localBitmap = null;
try {
Object localObject = null;
localBitmap = BitmapFactory
.decodeStream(((HttpURLConnection) new URL(paramString)
.openConnection()).getInputStream());
localObject = localBitmap;
if (localObject != null) {
return localBitmap;
}
} catch (Exception e) {
}
return localBitmap;
}
protected Bitmap doInBackground(Object... paramVarArgs) {
this.imv = ((ImageView) paramVarArgs[0]);
Log.d("fsdf", (String) this.imv.getTag());
return download_Image((String) this.imv.getTag());
}
protected void onPostExecute(Bitmap paramBitmap) {
this.imv.setImageBitmap(paramBitmap);
}
}
I have also experienced the same . I am also searching for a right solution . As far as i have searched , i came to know that ListView clears the previous view while scrolling down and re-loads it when you scroll back . So while scrolling up and down, your images may get re-cycled and mis-aligned . ( I am also waiting for the correct solution ) .
But i have tackled it using SmartImageView , which is a library that directly downloads the image and sets it to the ImageView . It will maintain the images in cache and so you could get the right images .
Comparatively this was faster too .
Try this snippet code which i have used in application and it's working fine in my application and i am sure it will work at your end.
In my condition i am retrieving images and some data from server and maintain all images on list scrolling fine.
class OfferCustomListAdapter extends ArrayAdapter<String>
{
private Context context;
Boolean OddNumber;
ArrayList<String> getDealID = new ArrayList<String>();
ArrayList<String> getInAdpterUNamedlist = new ArrayList<String>();
ArrayList<String> getShopNData = new ArrayList<String>();
ArrayList<String> getUserFav = new ArrayList<String>();
ArrayList<String> getTotalAmt = new ArrayList<String>();
ArrayList<String> getDealImage = new ArrayList<String>();
ArrayList<Boolean> getBoolnState = new ArrayList<Boolean>();
//String Oflist[] ;
int favCount=0;
public OfferCustomListAdapter(Context context,ArrayList<String> dealIdlist, ArrayList<Boolean> AddBoolnList, ArrayList<String> dealNamelist,ArrayList<String> ShopNList,ArrayList<String> UserFave,ArrayList<String> TotalAmt,ArrayList<String> ImageList) {
super(context, android.R.layout.simple_list_item_1,dealNamelist);
this.context=context;
//Oflist = getFolwerUNamelis;
getDealID = dealIdlist;
getInAdpterUNamedlist = dealNamelist;
getShopNData = ShopNList;
getUserFav = UserFave;
getTotalAmt = TotalAmt;
getDealImage = ImageList;
getBoolnState = AddBoolnList;
}
#Override
public View getView(final int pos, View view, ViewGroup parent) {
final ViewHolder holder;
if (view == null) {
LayoutInflater inflater = LayoutInflater.from(this.context);
//view = inflater.inflate(R.layout.offer_custom_list, parent,false);
view = inflater.inflate(R.layout.reservatin_row, parent,false);
holder = new ViewHolder();
//holder.FollowrName = (TextView) view.findViewById(R.id.OfferNameTxt);
holder.DealName = (TextView) view.findViewById(R.id.tv_name);
holder.ShopName = (TextView) view.findViewById(R.id.tv_address);
holder.FavBtn = (ImageView) view.findViewById(R.id.Ofr_FavBtn);
holder.listLayout = (LinearLayout) view.findViewById(R.id.OfferListLayout);
holder.profile_image = (ImageView)view.findViewById(R.id.profile_img);
holder.OfferAmtBtn =(Button)view.findViewById(R.id.TotalOfrBtn);
//holder.FavBtn = (ImageView) view.findViewById(R.id.offerFavBtn);
holder.FavBtn.setTag(pos);
view.setTag(holder);
} else {
holder = (ViewHolder) view.getTag();
}
if ( pos % 2 == 0 ){
System.out.println("You entered an even number. "+pos % 2);
holder.listLayout.setBackgroundResource(R.drawable.offer_list_bg);
}else{
System.out.println("You entered an odd number.");
holder.listLayout.setBackgroundResource(R.drawable.special_offer_bg);
}
/*if(getUserFav.get(pos).equals("0")){
//BolArraylist.add(false);
holder.FavBtn.setBackgroundResource(R.drawable.fav_btn);
}else{
//BolArraylist.add(true);
holder.FavBtn.setBackgroundResource(R.drawable.fav_active_btn);
}*/
holder.DealName.setText(getInAdpterUNamedlist.get(pos));
holder.ShopName.setText(getShopNData.get(pos));
holder.OfferAmtBtn.setText("$"+getTotalAmt.get(pos));
imgLoader.DisplayImage(getDealImage.get(pos), holder.profile_image);
holder.FavBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
if (isNetworkAvailable()) {
if(!userid.equals("")){
Offer_ID = getDealID.get(pos);
GUsrFavState = getUserFav.get(pos);
if(GUsrFavState.equals("0")){
GUsrFavState="1";
getUserFav.remove(pos);
getUserFav.add(pos, "1");
holder.FavBtn.setBackgroundResource(R.drawable.fav_active_btn);
getBoolnState.set(pos, true);
new Call_OfferFavWS().execute();
}else{
GUsrFavState="0";
holder.FavBtn.setBackgroundResource(R.drawable.fav_btn);
getUserFav.remove(pos);
getUserFav.add(pos, "0");
getBoolnState.set(pos, false);
new Call_OfferFavWS().execute();
}
}else{
Intent CallSignIn = new Intent(DollarMainActivity.this,SingInActivity.class);
startActivity(CallSignIn);
}
} else {
Toast alrtMsg = Toast.makeText(DollarMainActivity.this, "No network connection available !!!", Toast.LENGTH_LONG);
alrtMsg.setGravity(Gravity.CENTER, 0, 0);
alrtMsg.show();
}
}
});
if(getBoolnState.get(pos)){
holder.FavBtn.setBackgroundResource(R.drawable.fav_active_btn);
}else{
holder.FavBtn.setBackgroundResource(R.drawable.fav_btn);
}
return view;
}
class ViewHolder {
public TextView DealName,ShopName;
public ImageView FavBtn, profile_image;
public LinearLayout listLayout;
public Button OfferAmtBtn;
}
}
Hope it will help you.
if you need any help pls let me know.
I have a ListView inside an Activity and each of its item is customized to have some TextViews along with a DropDownList item and an ImageView. Inside an OnScrollListener() implementation each item of a listView gets populated using an ArrayAdapter populating text views with values taken from an arrayList and ImageView with the .jpeg file stored on SD card. Following is the screenSHot of listView Item
The problem arises when the .jpeg file from sdCard is converted to a bitmap (i.e. BitmapFactory.decodeFile(fileName) ) and then gets assigned to an image View using setImageBitmap(Bitmap bmp). As the setting bitmap image to an image view is a lengthy process it cannot keep pace with the scroll listener implementation and the ImageView of different ListView rows gets populated with the image it was assigned to any row above. Can anybody please suggest some workout to cater this issue specifically the assignment of images from SD Card to an imageView. Its not like my listView item is overLoaded with controls that is why i am facing this problem. I also have tried it with single ImageView item inside each row and it behaves the same way. Your suggestion to improvise this are welcome and surely will be of great help. Thank you :-)
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
AssetDetailHolder assetDetailholder = null;
try {
if (row == null) {
LayoutInflater inflator = ((Activity) context)
.getLayoutInflater();
row = inflator.inflate(layoutResourceID, parent, false);
assetDetailholder = new AssetDetailHolder();
assetDetailholder.itemPosition = position;
assetDetailholder.txtVwlineCOde = (TextView) row
.findViewById(R.id.lineCodeValue_ad);
assetDetailholder.txtvwLocation = (TextView) row
.findViewById(R.id.locationValue_ad);
assetDetailholder.txtvwLocationDetail = (TextView) row
.findViewById(R.id.detailLocationValue_ad);
assetDetailholder.txtvwInventoryNo = (TextView) row
.findViewById(R.id.InventoryNoValue_ad);
assetDetailholder.spnrconditionCode = (Spinner) row
.findViewById(R.id.spinner_ad);
assetDetailholder.txtvwAssetName = (TextView) row
.findViewById(R.id.AssetNameValue_ad);
assetDetailholder.subNoThumbnail = (ImageView) row
.findViewById(R.id.IV_subNoThumbnail);
row.setTag(assetDetailholder);
} else {
assetDetailholder = (AssetDetailHolder) row.getTag();
assetDetailholder.itemPosition = position;
}
AssetDetail assetDetail = assetsDetailList[position];
new ThumbnailTask(position, assetDetailholder, assetDetail, context)
.execute();
if (assetDetail.assetLineCodeDesc.equals("")) {
assetDetailholder.txtVwlineCOde
.setText(assetDetail.strLineCOde);
} else {
assetDetailholder.txtVwlineCOde.setText(assetDetail.strLineCOde
+ "(" + assetDetail.assetLineCodeDesc + ")");
}
if (assetDetail.assetLocationNameDesc.equals("")) {
assetDetailholder.txtvwLocation
.setText(assetDetail.strLocationName);
} else {
assetDetailholder.txtvwLocation
.setText(assetDetail.strLocationName + "("
+ assetDetail.assetLocationNameDesc + ")");
}
assetDetailholder.txtvwLocationDetail
.setText(assetDetail.strLocationDetail);
if (assetDetail.strInventoryNumber.contains("-")) {
assetDetailholder.txtvwInventoryNo
.setText(assetDetail.strInventoryNumber.split("-")[0]);
} else {
assetDetailholder.txtvwInventoryNo
.setText(assetDetail.strInventoryNumber);
}
assetDetailholder.txtvwAssetName.setText(assetDetail.assetName);
String conditionCodeString = assetDetail.assetConditionCode;
if (conditionCodeString != "" || conditionCodeString != null) {
try {
int conditionCodeInteger = Integer
.parseInt(conditionCodeString);
assetDetailholder.spnrconditionCode
.setSelection(conditionCodeInteger);
} catch (Exception e) {
assetDetailholder.spnrconditionCode.setSelection(0);
}
} else {
assetDetailholder.spnrconditionCode.setSelection(0);
}
// String thumbnailDir = Common
// .getSubNoDirectory(context, assetDetail);
// if (new File(thumbnailDir).isDirectory()) {
//
// File thumbnailFile = new File(Common.getSubNoImgFilePath(
// thumbnailDir, assetDetail, SubNo_ImageSample.A));
//
// if (thumbnailFile.exists()) {
// assetDetailholder.subNoThumbnail
// .setImageBitmap(BitmapFactory
// .decodeFile(thumbnailFile.getAbsolutePath()));
// }
// }
} catch (Exception e) {
e.printStackTrace();
}
return row;
}
static class AssetDetailHolder {
TextView txtVwlineCOde;
TextView txtvwLocation;
TextView txtvwLocationDetail;
TextView txtvwInventoryNo;
TextView txtvwAssetName;
Spinner spnrconditionCode;
ImageView subNoThumbnail;
public int itemPosition;
}
private static class ThumbnailTask extends AsyncTask<Void, Void, Void> {
private int mPosition;
private AssetDetailHolder mHolder;
private Context cntxt;
private AssetDetail assetItem;
private Bitmap thumbnailBmp;
public ThumbnailTask(int position, AssetDetailHolder holder,
AssetDetail asset, Context context) {
mPosition = position;
mHolder = holder;
assetItem = asset;
cntxt = context;
}
#Override
protected Void doInBackground(Void... params) {
String thumbnailDir = Common.getSubNoDirectory(cntxt, assetItem);
if (new File(thumbnailDir).isDirectory()) {
File thumbnailFile = new File(Common.getSubNoImgFilePath(
thumbnailDir, assetItem, SubNo_ImageSample.A));
if (thumbnailFile.exists()) {
thumbnailBmp = BitmapFactory.decodeFile(thumbnailFile
.getAbsolutePath());
}
}
return null;
}
#Override
protected void onPostExecute(Void result) {
if (mHolder.itemPosition == mPosition && thumbnailBmp != null) {
mHolder.subNoThumbnail.setImageBitmap(thumbnailBmp);
}
// super.onPostExecute(result);
}
}
http://lucasr.org/2012/04/05/performance-tips-for-androids-listview/
This will be hopefull to you :P
In my app I have an option that allows users to browse for audio files on their phone to add to the app. I am having trouble however with creating a faster way of processing the query code. Currently it searches the entire external storage and causes the phone to prompt a force close/wait warning. I would like to take the code I have posted below and make it more efficient by either searching in a specific folder on the phone or by streamlining the process to make the file search quicker. I am not sure how to do this however. Thanks!
public class BrowseActivity extends DashboardActivity implements
OnClickListener, OnItemClickListener {
private List<Sound> soundsInDevice = new ArrayList<Sound>();
private List<Sound> checkedList;
private ListView browsedList;
private BrowserSoundAdapter adapter;
private long categoryId;
private Category category;
private String currentCategoryName;
private String description;
// private Category newCategory ;
private Button doneButton;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_browse);
checkedList = new ArrayList<Sound>();
browsedList = (ListView) findViewById(android.R.id.list);
doneButton = (Button) findViewById(R.id.doneButton);
soundsInDevice = getMediaSounds();
if (soundsInDevice.size() > 0) {
adapter = new BrowserSoundAdapter(this, R.id.browseSoundName,
soundsInDevice);
} else {
Toast.makeText(getApplicationContext(),
getString(R.string.no_sounds_available), Toast.LENGTH_SHORT)
.show();
}
browsedList.setAdapter(adapter);
browsedList.setOnItemClickListener(this);
doneButton.setOnClickListener(this);
}
private List<Sound> getMediaSounds() {
List<Sound> mediaSoundList = new ArrayList<Sound>();
ContentResolver cr = getContentResolver();
String[] projection = {MediaStore.Audio.Media._ID,
MediaStore.Audio.Media.DISPLAY_NAME,
MediaStore.Audio.Media.TITLE,
MediaStore.Audio.Media.DATA,
MediaStore.Audio.Media.DURATION};
final Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
Log.v("MediaStore.Audio.Media.EXTERNAL_CONTENT_URI", "" + uri);
final Cursor cursor = cr.query(uri, projection, null, null, null);
int n = cursor.getCount();
Log.v("count", "" + n);
if (cursor.moveToFirst()) {
do {
String soundName = cursor
.getString(cursor
.getColumnIndexOrThrow(MediaStore.Audio.Media.DISPLAY_NAME));
Log.v("soundName", "" + soundName);
String title = cursor
.getString(cursor
.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE));
Log.v("title", "" + title);
String path = cursor.getString(cursor
.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA));
Log.v("path", "" + path);
Sound browsedSound = new Sound(title, path, false, false,
false, false, 0);
Log.v("browsedSound", "" + browsedSound);
mediaSoundList.add(browsedSound);
Log.v("mediaSoundList", "" + mediaSoundList.toString());
} while (cursor.moveToNext());
}
return mediaSoundList;
}
public class BrowserSoundAdapter extends ArrayAdapter<Sound> {
public BrowserSoundAdapter(Context context, int textViewResourceId,
List<Sound> objects) {
super(context, textViewResourceId, objects);
}
#Override
public View getView(final int position, View convertView,
ViewGroup parent) {
ViewHolder viewHolder;
View view = convertView;
LayoutInflater inflater = getLayoutInflater();
if (view == null) {
view = inflater.inflate(R.layout.list_item_browse, null);
viewHolder = new ViewHolder();
viewHolder.soundNameTextView = (TextView) view
.findViewById(R.id.browseSoundName);
viewHolder.pathTextView = (TextView) view
.findViewById(R.id.browseSoundPath);
viewHolder.checkToAddSound = (CheckBox) view
.findViewById(R.id.browse_checkbox);
view.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) view.getTag();
}
final Sound sound = soundsInDevice.get(position);
if (sound.isCheckedState()) {
viewHolder.checkToAddSound.setChecked(true);
} else {
viewHolder.checkToAddSound.setChecked(false);
}
viewHolder.soundNameTextView.setText(sound.getName());
viewHolder.pathTextView.setText(sound.getUri());
viewHolder.checkToAddSound
.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
CheckBox cb = (CheckBox) v
.findViewById(R.id.browse_checkbox);
boolean checked = cb.isChecked();
boolean newValue = checked;
updateView(position, newValue);
doneButtonStatus(checkedList.size());
}
});
return view;
}
}
// Adapter view holder class
private class ViewHolder {
private TextView soundNameTextView;
private TextView pathTextView;
private CheckBox checkToAddSound;
}
// done button On Click
#Override
public void onClick(View view) {
boolean status = getIntent().getBooleanExtra("FromAddCat", false);
Log.v("for add category","enters in if");
if(status){
Log.v("for add category","enters in if1");
currentCategoryName = getIntent().getStringExtra("categoryName");
description = getIntent().getStringExtra("description");
boolean existCategory = SQLiteHelper.getCategoryStatus(currentCategoryName);
if (!existCategory) {
category = new Category(currentCategoryName, description,
false);
category.insert();
category.update();
Log.v("for add category","enters in if2");
}
}else{
categoryId = getIntent().getLongExtra("categoryId",-1);
category = SQLiteHelper.getCategory(categoryId);
}
for (Sound checkedsound : checkedList) {
checkedsound.setCheckedState(false);
checkedsound.insert();
category.getSounds().add(checkedsound);
final Intent intent = new Intent(this, CategoriesActivity.class);
finish();
startActivity(intent);
}
}
#Override
public void onItemClick(AdapterView<?> arg0, View view, int position,
long arg3) {
boolean checked = true;
boolean newValue = false;
CheckBox cb = (CheckBox) view.findViewById(R.id.browse_checkbox);
if (cb.isChecked()) {
cb.setChecked(!checked);
newValue = !checked;
} else {
cb.setChecked(checked);
newValue = checked;
}
updateView(position, newValue);
doneButtonStatus(checkedList.size());
}
private void doneButtonStatus(int size) {
if (size > 0) {
doneButton.setEnabled(true);
doneButton.setBackgroundResource(R.drawable.done_button_drawable);
} else {
doneButton.setEnabled(false);
doneButton.setBackgroundResource(R.drawable.done_btn_disabled);
}
}
private void updateView(int index, boolean newValue) {
System.out.println(newValue);
Sound sound = soundsInDevice.get(index);
if (newValue == true) {
checkedList.add(sound);
sound.setCheckedState(newValue);
} else {
checkedList.remove(sound);
sound.setCheckedState(newValue);
}
}
}
The issue is that you are performing the search in the application's main thread that is responsible for updating the UI. Long running tasks are fine: you just need to do them in a background thread. You can use an AsyncTask for that or a worker thread.