I want to load images from a server, AFTER loading the data in a list view.
I know that a lot of topics existing for this problem but I haven't found the solution...
So this is my code :
//asyncTackClass for loadingpictures
public class LoadImagesThread extends AsyncTask<Bundle, Void, Bitmap> {
private ImageView view;
private Bitmap bm;
private Context context;
private final WeakReference<ImageView> imageViewReference;
private final String BUNDLE_URL = "url";
private final String BUNDLE_NAME = "name";
private final String BUNDLE_BM = "bm";
public LoadImagesThread(Context context, ImageView view) {
this.context=context;
imageViewReference = new WeakReference<ImageView>(view);
}
#Override
protected Bitmap doInBackground(Bundle... b) {
Bitmap bm =null;
if (StorageHelper.getBitmap(b[0].getString(BUNDLE_NAME)) != null) { // Check the sdcard
bm = StorageHelper.getBitmap(b[0].getString(BUNDLE_NAME));
Log.w("LoadImagesThread", "Get image from sdcard : "+b[0].getString(BUNDLE_NAME));
} else { // Check the server
bm = ServiceHelper.getBitmapFromURL(b[0].getString(BUNDLE_URL));
StorageHelper.saveBitmap(bm, b[0].getString(BUNDLE_NAME)); // Save image on sdcard
Log.w("LoadImagesThread", "Get image from server : "+b[0].getString(BUNDLE_NAME));
}
return bm;
}
#Override
protected void onPostExecute(final Bitmap bm) {
super.onPostExecute(bm);
if (bm != null){ //if bitmap exists...
view = imageViewReference.get();
// Fade out
Animation fadeOutAnimation = AnimationUtils.loadAnimation(context, R.anim.fadeoutimage);
fadeOutAnimation.setAnimationListener(new AnimationListener() {
public void onAnimationStart(Animation animation) {
}
public void onAnimationRepeat(Animation animation) {
}
public void onAnimationEnd(Animation animation) {
// Fade in
view.setImageBitmap(bm);
Animation fadeInAnimation = AnimationUtils.loadAnimation(context, R.anim.fadeinimage);
view.startAnimation(fadeInAnimation);
}
});
// Launch the fadeout
view.startAnimation(fadeOutAnimation);
}else{ //if not picture, display the default ressource
view.setImageResource(R.drawable.productcarre);
}
}
}
The code is used to display a Bitmap in a ImageView
And this is the adapter:
public class ListViewShoplistStoresAdapter extends BaseAdapter {
private ArrayList<Shop> shopList;
private Activity activity;
private HashMap<Integer, ImageView> views;
private final String BUNDLE_URL = "url";
private final String BUNDLE_NAME = "name";
private final String BUNDLE_POS = "pos";
private final String BUNDLE_ID = "id";
public ListViewShoplistStoresAdapter(Activity activity, ArrayList<Shop> shopList) {
super();
this.activity = activity;
this.shopList = shopList;
this.views = new HashMap<Integer, ImageView>();
}
public int getCount() {
return shopList.size();
}
public Object getItem(int position) {
return shopList.get(position);
}
public long getItemId(int position) {
return shopList.get(position).getId();
}
private class ViewHolder {
public TextView store;
public TextView name;
public ImageView ImageStore;
}
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder view;
LayoutInflater inflator = activity.getLayoutInflater();
if(convertView == null) {
view = new ViewHolder();
convertView = inflator.inflate(R.layout.listviewshops, null);
view.store = (TextView) convertView.findViewById(R.id.store);
view.name = (TextView) convertView.findViewById(R.id.name);
view.ImageStore = (ImageView) convertView.findViewById(R.id.imgstore);
convertView.setTag(view);
}else {
view = (ViewHolder) convertView.getTag();
}
Typeface regular=Typeface.createFromAsset(activity.getAssets(), "fonts/RobotoRegular.ttf");
view.store.setTypeface(regular);
Typeface light=Typeface.createFromAsset(activity.getAssets(), "fonts/RobotoLight.ttf");
view.store.setTypeface(light);
Brand brand = StorageHelper.getBrand(activity, shopList.get(position).getBrandId());
if (brand == null) {
Log.e("SetShopInAdapter","Brand null");
Toast.makeText(activity, "Impossible d'afficher la liste de magasins", Toast.LENGTH_LONG).show();
} else {
view.store.setText(brand.getName());
view.name.setText(shopList.get(position).getName());
view.ImageStore.setImageResource(R.drawable.productcarre);
}
Bundle b = new Bundle();
//url of the pict
b.putString(BUNDLE_URL, ServiceHelper.getImageUrl("brand", brand.getName()));
// name of image
b.putString(BUNDLE_NAME, ServiceHelper.getCleanImageName(brand.getName()));
//position in the listView
b.putInt(BUNDLE_POS, position);
//id of the current object
b.putInt(BUNDLE_ID, brand.getId());
//put info in the map in order to display in the onPostExecute
if(views.get(position)==null){
views.put(position, view.ImageStore);
// launch thread
new LoadImagesThread(activity.getApplicationContext(), view.ImageStore).execute(b);
}
return convertView;
}
}
So, when I used a GridView, there were no problems, but when I use a ListView the image is changed only in the first item !
Example:
I want to display product images for "car", "house" and "apple" items.
The code will launch the thread and all images (car then house and finally apple) will be displayed in the first item (the car item)...
And the house and the apple while not have images !!
Do you know what I should do ?
Thanks
There is a lot about this here on SO..
asynchrnous loading like that is called "Lazy Loading"
https://github.com/thest1/LazyList
for full implementation of such a process with list
For loading images in general i recomend this :
https://github.com/nostra13/Android-Universal-Image-Loader
You can use volley to improve in listview performance.
Take a look at this simple example: Android Custom ListView with Image and Text using Volley
Related
I have a custom adapter added to listview. Data is call logs from phone. I reduce list by show only records from 3 days. Problem is that when I try to scroll listview from top to bottom I have a huge lags. My Scroll isn't smooth. Is there any way to make listview scroll smoother?
Here is my custom adapter:
public class CallListAdapter extends ArrayAdapter<CallList> {
Activity activity;
public CallListAdapter(Context context, ArrayList<CallList> calls, Activity activity) {
super(context, 0, calls);
this.activity = activity;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
final CallList callList = getItem(position);
int actualPosition = 0;
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.call_list, parent, false);
}
final TextView call1 = convertView.findViewById(R.id.callNumber);
final TextView call2 = convertView.findViewById(R.id.callDate);
final TextView call3 = convertView.findViewById(R.id.conversationTime);
final TextView call4 = convertView.findViewById(R.id.callType);
final Button callView = convertView.findViewById(R.id.getViewName);
final ImageView bio = convertView.findViewById(R.id.lookBio);
final ImageView edit = convertView.findViewById(R.id.edit_call);
final ImageView block = convertView.findViewById(R.id.blockCall);
final ImageView call = convertView.findViewById(R.id.callUser);
final TextView bioLabel = convertView.findViewById(R.id.BioLabelSug);
final TextView editLabel = convertView.findViewById(R.id.NoteLabel);
final TextView blockLabel = convertView.findViewById(R.id.BlockLabelSug);
final TextView callLabel = convertView.findViewById(R.id.CallLabelSug);
final ConstraintLayout callContainer = convertView.findViewById(R.id.contact_container);
final ConstraintLayout bioContainer = convertView.findViewById(R.id.bio_container);
final ConstraintLayout blockContainer = convertView.findViewById(R.id.ignore_container);
final ConstraintLayout noteContainer = convertView.findViewById(R.id.note_container);
final TextView btnMarg = convertView.findViewById(R.id.buttonMargin);
final TextView callListNr2 = convertView.findViewById(R.id.callNumber2);
final LayoutInflater factory = activity.getLayoutInflater();
final View fullView = factory.inflate(R.layout.fragment_calls, null);
final RelativeLayout loading = fullView.findViewById(R.id.loadingBar);
String[] jsonData = new manageCalls().intentCallValues(position);
StringBuilder builder = new StringBuilder();
for (String s : jsonData) {
builder.append(s + "\n");
}
String str = builder.toString();
final String num = jsonData[0];
final String dat = jsonData[1];
final String typeCall = jsonData[2];
final String dur = jsonData[3];
final String authToken = SaveSharedPreferences.getPrefTokenName(getContext());
final Animation slideUp = AnimationUtils.loadAnimation(getContext(), R.anim.slideup);
final Animation slideDown = AnimationUtils.loadAnimation(getContext(), R.anim.slidedown);
final Handler handler = new Handler();
callView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (bioContainer.getVisibility() == View.GONE) {
callListNr2.setVisibility(View.GONE);
bio.setVisibility(View.VISIBLE);
bioLabel.setVisibility(View.VISIBLE);
edit.setVisibility(View.VISIBLE);
editLabel.setVisibility(View.VISIBLE);
} else if (bioContainer.getVisibility() == View.VISIBLE) {
handler.postDelayed(new Runnable() {
#Override
public void run() {
bio.setVisibility(View.GONE);
callContainer.setVisibility(View.GONE);
bioContainer.setVisibility(View.GONE);
noteContainer.setVisibility(View.GONE);
blockContainer.setVisibility(View.GONE);
}
}, 300);
}
}
});
if (actualPosition != position) {
if (bioContainer.getVisibility() == View.VISIBLE) {
bioContainer.setVisibility(View.GONE);
callContainer.setVisibility(View.GONE);
noteContainer.setVisibility(View.GONE);
blockContainer.setVisibility(View.GONE);
}
actualPosition = position;
}
call.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
call.setEnabled(false);
loading.setVisibility(View.VISIBLE);
Intent intentCall = new Intent(view.getContext(), CallUserActivity.class);
intentCall.putExtra("number", num);
intentCall.putExtra("authToken", authToken);
intentCall.putExtra("Date", dat);
activity.startActivityForResult(intentCall, position);
handler.postDelayed(new Runnable() {
#Override
public void run() {
call.setEnabled(true);
loading.setVisibility(View.GONE);
}
}, 1000);
}
});
call2.setText(callList.callDate);
call3.setText(callList.conversationTime);
call4.setText(callList.callType);
return convertView;
}
}
Try use ViewHolder and use AsyncTask to load bitmap.
You can try this way.
private static class ViewHolder {
public TextView call1;
public TextView call2;
public TextView call3;
public TextView call4;
public Button callView;
public ImageView bio;
public ImageView edit;
public ImageView block;
public ImageView call;
public TextView bioLabel;
public TextView editLabel;
public TextView blockLabel;
public TextView callLabel;
public ConstraintLayout callContainer;
public ConstraintLayout bioContainer;
public ConstraintLayout blockContainer;
public ConstraintLayout noteContainer;
public TextView btnMarg;
public TextView callListNr2;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
// inflate the layout
LayoutInflater inflater = LayoutInflater.from(getContext());
convertView = inflater.inflate(layoutResourceId, parent, false);
holder = new ViewHolder();
holder.call1 = convertView.findViewById(R.id....);
holder.call2 = convertView.findViewById(R.id....);
//Same for all other views
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.call1.setText(....);
//Lazy load for bitmap
loadBitmap(yourFileName..., bio)
return convertView;
}
static class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {
private final WeakReference<ImageView> imageViewReference;
BitmapWorkerTask(ImageView imageView) {
// Use a WeakReference to ensure the ImageView can be garbage collected
imageViewReference = new WeakReference<ImageView>(imageView);
}
// Decode image in background.
#Override
protected Bitmap doInBackground(String... params) {
return decodeSampledBitmapFromResource(params[0], 300, 300);
}
// Once complete, see if ImageView is still around and set bitmap.
#Override
protected void onPostExecute(Bitmap bitmap) {
if (imageViewReference != null && bitmap != null) {
final ImageView imageView = imageViewReference.get();
if (imageView != null) {
imageView.setImageBitmap(bitmap);
}
}
}
}
public void loadBitmap(String fileName, ImageView imageView) {
BitmapWorkerTask task = new BitmapWorkerTask(imageView);
task.execute(fileName);
}
public static Bitmap decodeSampledBitmapFromResource(String fileName,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(fileName, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(fileName, options);
}
Your getview is HUGE.
Your (if convertview==null) has basically almost no effect as you're setting up the view again anyways.
What you need to do is refactor the getview to not be so slow. one thing you can do is create a class that has all the findviews done already for you and put that then in the .tag of the converted view. change your onclicks to use that as well, in a manner where you don't have to recreate them(other ways to do that exist as well).
ideally your code for if you have a converted view already should be just the .settexts().
depending on the size of your list, you could just get away with creating a view for each callist and avoid recycling the converted views alltogether, in such case you could just create them in advance.
also depending on the size of your list you could just get away with creating a just a simple linearlayout instead inside a scrollview. if your list isn't huge and it's not for some old phones, it works just fine as well (Don't knock on this as bad advice until you try on your phone how huge it can be before a listview starts making more sense).
This is my code:
public class GetAllCategoriesListViewAdapter extends BaseAdapter{
private JSONArray dataArray;
private Activity activity;
private static final String baseUrlForCategoryImage = "link here";
private static LayoutInflater inflater = null;
public GetAllCategoriesListViewAdapter(JSONArray jsonArray, Activity a){
this.dataArray = jsonArray;
this.activity = a;
inflater = (LayoutInflater) this.activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
return this.dataArray.length();
}
#Override
public Object getItem(int position) {
return position;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
final ListCell cell;
if(convertView == null){
convertView = inflater.inflate(R.layout.get_all_categories_list_view_cell, null);
cell = new ListCell();
cell.category_name = (TextView) convertView.findViewById(R.id.category_name);
cell.category_image = (ImageView) convertView.findViewById(R.id.category_image);
cell.category_image.setTag(cell);
convertView.setTag(cell);
}else{
cell = (ListCell) convertView.getTag();
}
try{
JSONObject jsonObject = this.dataArray.getJSONObject(position);
cell.category_name.setText(jsonObject.getString("category_name"));
String nameOfImage = jsonObject.getString("category_image");
String urlForImageInServer = baseUrlForCategoryImage + nameOfImage;
new AsyncTask<String, Void, Bitmap>(){
protected Bitmap doInBackground(String... params){
String url = params[0];
Bitmap icon = null;
try{
InputStream in = new java.net.URL(url).openStream();
icon = BitmapFactory.decodeStream(in);
}catch (MalformedURLException e){
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}
return icon;
}
#Override
protected void onPostExecute(Bitmap result) {
cell.category_image.setImageBitmap(result);
}
}.execute(urlForImageInServer);
}catch (JSONException e){
e.printStackTrace();
}
return convertView;
}
private class ListCell{
private ImageView category_image;
private TextView category_name;
}
}
The code gets the images from my webhost and place it in every cell in my listvew. The problem is everytime I scroll, the images are shuffled and returns in few seconds. How to stop the images from changing when I scroll? I tried to use the solution on other post but it won't work. Please help.
Looks like you are new to android. So you are fetching the images in the getView method. The getView method is called every time a new list item is drawn. So For every image, a new request is made to internet. SO that will be a lot of requests . You should firstly get your images and get them in some ArryayList . Then pass that Arraylist to your adapter. Here is tutorial for you
Using AsyncTask
http://www.devexchanges.info/2015/04/android-custom-listview-with-image-and.html
Using Volley
https://www.androidhive.info/2014/07/android-custom-listview-with-image-and-text-using-volley/
Go for Volley for better performance. Cheers!
I am new to android development,I am parsing my data using JSON Parsing method,I extend my class with List Fragment and I want my data in list view but the problem is i am getting all the data perfectly except the images,i don't know how to solve it,my response looks like this
{"matching":[{"name":"Monic Dano","profile_id":"GM335695","image":"http://mywebsitename.com/images/Girlnoimage.jpg","cast":"","age":"24","location":"Ivory Coast"}]}
public class HomeFragment extends ListFragment {
//CustomAdapter adapter;
//private List<RowItem> rowItems;
private ProgressDialog pDialog;
//JSON parser class
JSONParser jsonParser = new JSONParser();
JSONArray matching=null;
ArrayList<HashMap<String,String>> aList;
private static String MATCH_URL = null;
private static final String TAG_MATCH="matching";
private static final String TAG_NAME="name";
private static final String TAG_PROFILE="profile_id";
private static final String TAG_IMAGE="image";
private static final String TAG_CAST="cast";
private static final String TAG_AGE="age";
private static final String TAG_LOCATION="location";
private ListView listview;
public HomeFragment(){}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
String strtext = getArguments().getString("user_login_id");
MATCH_URL = "http://mywebsitename.com/webservice/matching?version=apps&user_login_id="+strtext;
View rootView = inflater.inflate(R.layout.fragment_home, container, false);
aList = new ArrayList<HashMap<String,String>>();
// rowItems = new ArrayList<RowItem>();
listview=(ListView)rootView.findViewById(android.R.id.list);
new LoadAlbums().execute();
return rootView;
}
class LoadAlbums extends AsyncTask<String, String, String> {
/**
* Before starting background thread Show Progress Dialog
* */
#Override
protected void onPreExecute() {
super.onPreExecute();
pDialog = new ProgressDialog(HomeFragment.this.getActivity());
pDialog.setMessage("Loading...");
pDialog.setIndeterminate(false);
pDialog.setCancelable(false);
pDialog.show();
}
protected String doInBackground(String... args) {
ServiceHandler sh = new ServiceHandler();
// Making a request to url and getting response
String jsonStr = sh.makeServiceCall(MATCH_URL, ServiceHandler.GET);
Log.d("Response: ", "> " + jsonStr);
if (jsonStr != null) {
try {
JSONObject jsonObj = new JSONObject(jsonStr);
// Getting JSON Array node
matching = jsonObj.getJSONArray(TAG_MATCH);
// looping through All Contacts
for (int i = 0; i < matching.length(); i++) {
JSONObject c = matching.getJSONObject(i);
// Storing each json item values in variable
String user_name = c.getString(TAG_NAME);
String user_profile=c.getString(TAG_PROFILE);
String user_image=c.getString(TAG_IMAGE);
String user_cast=c.getString(TAG_CAST);
String user_age=c.getString(TAG_AGE);
String user_location=c.getString(TAG_LOCATION);
// creating new HashMap
HashMap<String, String> map = new HashMap<String, String>();
// adding each child node to HashMap key => value
map.put(TAG_NAME,user_name);
map.put(TAG_PROFILE, user_profile);
map.put(TAG_IMAGE, user_image);
map.put(TAG_CAST, user_cast);
map.put(TAG_AGE, user_age+" years");
map.put(TAG_LOCATION, user_location);
// adding HashList to ArrayList
aList.add(map);
}
} catch (JSONException e) {
e.printStackTrace();
}
} else {
Log.e("ServiceHandler", "Couldn't get any data from the url");
}
return null;
}
protected void onPostExecute(String file_url) {
super.onPostExecute(file_url);
// dismiss the dialog after getting all albums
if (pDialog.isShowing())
pDialog.dismiss();
// updating UI from Background Thread
/**
* Updating parsed JSON data into ListView
* */
// updating listview
CustomAdapter adapter = new CustomAdapter(getActivity(),aList);
setListAdapter(adapter);
}
}
}
Try to AndroidQuery with custom adapter :
public class CustomAdapter extends BaseAdapter {
private Context context;
private ArrayList<HashMap<String,String>> listData;
private AQuery aQuery;
private static final String TAG_NAME="name";
private static final String TAG_PROFILE="profile_id";
private static final String TAG_IMAGE="image";
private static final String TAG_CAST="cast";
private static final String TAG_AGE="age";
private static final String TAG_LOCATION="location";
public CustomAdapter(Context context,ArrayList<HashMap<String,String>> listData) {
this.context = context;
this.listData=listData;
aQuery = new AQuery(this.context);
}
#Override
public int getCount() {
return listData.size();
}
#Override
public Object getItem(int position) {
return listData.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
holder = new ViewHolder();
convertView = LayoutInflater.from(context).inflate(R.layout.list_item, null);
holder.propic = (ImageView) convertView.findViewById(R.id.propic);
holder.txtproname = (TextView) convertView.findViewById(R.id.txtproname);
holder.txtproid = (TextView) convertView.findViewById(R.id.txtproid);
holder.txtprofilecast = (TextView) convertView.findViewById(R.id.txtprofilecast);
holder.txtprofileage = (TextView) convertView.findViewById(R.id.txtprofileage);
holder.txtprofileplace = (TextView) convertView.findViewById(R.id.txtprofileplace);
convertView.setTag(holder);
}else{
holder = (ViewHolder) convertView.getTag();
}
holder.txtproname.setText(listData.get(position).get(TAG_NAME));
holder.txtproid.setText(listData.get(position).get(TAG_PROFILE));
holder.txtprofilecast.setText(listData.get(position).get(TAG_CAST));
holder.txtprofileage.setText(listData.get(position).get(TAG_AGE));
holder.txtprofileplace.setText(listData.get(position).get(TAG_LOCATION));
aQuery.id(holder.propic).image(listData.get(position).get(TAG_IMAGE),true,true,0,R.drawable.ic_launcher);
// image parameter : 1 : memory cache,2:file cache,3:target width,4:fallback image
return convertView;
}
class ViewHolder{
ImageView propic;
TextView txtproname;
TextView txtproid;
TextView txtprofilecast;
TextView txtprofileage;
TextView txtprofileplace;
}
}
How to set adapter to ListView :
CustomAdapter adapter = new CustomAdapter(getActivity(),aList);
setListAdapter(adapter);
You can use universal image loader for viewing images from your server.Z
Just pass the image url and your view and you are good to go.
For your reference here is the link to Universal Image loader with all its documentation.
https://github.com/nostra13/Android-Universal-Image-Loader
Hop it helps you.
I am hardly suggest you to use Android Query for this. Its mind blowing api given by Android itself. You can download image, download bitmap or whatever you wanna do you can.
You can download the jar file from here :here Download the jar file and set jar to your Build Path.
AQuery androidAQuery=new AQuery(this);
As an example to load image directly from url:
androidAQuery.id(YOUR IMAGEVIEW).image(YOUR IMAGE TO LOAD, true, true, getDeviceWidth(), ANY DEFAULT IMAGE YOU WANT TO SHOW);
As an example to get Bitmap from url:
androidAQuery.ajax(YOUR IMAGE URL,Bitmap.class,0,new AjaxCallback<Bitmap>(){
#Override
public void callback(String url, Bitmap object, AjaxStatus status) {
super.callback(url, object, status);
//You will get Bitmap from object.
}
});
It's very fast and accurate, and using this you can find many more features like Animation when loading; getting a bitmap, if needed; etc.
//Declare adapter globally.
private EfficientAdapter adapter;
//Initialize it in onCreate() method
adapter = new EfficientAdapter(this);
//Set your adapter like
listview.setAdapter(adapter);
//Adapter class code
private class EfficientAdapter extends BaseAdapter {
private LayoutInflater mInflater;
private Context context;
public EfficientAdapter(Context context) {
mInflater = LayoutInflater.from(context);
this.context = context;
}
#Override
public int getCount() {
return aList.size();
}
#Override
public Object getItem(int arg0) {
return null;
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
final ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.YOUR ITEM LAYOUT, null);
holder = new ViewHolder();
holder.txtName = (TextView) convertView.findViewById(R.id.txtName);
holder.txtProfile = (TextView) convertView.findViewById(R.id.txtProfile);
holder.txtCast = (TextView) convertView.findViewById(R.id.txtCast);
holder.txtAge = (ImageView) convertView.findViewById(R.id.txtAge);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.txtName.setText(aList.get(position).get(TAG_NAME));
holder.txtProfile.setText(aList.get(position).get(TAG_PROFILE));
holder.txtCast.setText(aList.get(position).get(TAG_CAST));
holder.txtAge.setText(aList.get(position).get(TAG_AGE));
aQuery.id(holder.imgUser).image(data.get(position).get(TAG_IMAGE), true, true);
return convertView;
}
class ViewHolder {
TextView txtName;
TextView txtProfile;
TextView txtCast;
TextView txtAge;
ImageView imgUser;
}
}
In source code of SimpleAdapter:
private void bindView(int position, View view) {
final Map dataSet = mData.get(position);
if (dataSet == null) {
return;
}
final ViewBinder binder = mViewBinder;
final String[] from = mFrom;
final int[] to = mTo;
final int count = to.length;
for (int i = 0; i < count; i++) {
final View v = view.findViewById(to[i]);
if (v != null) {
final Object data = dataSet.get(from[i]);
String text = data == null ? "" : data.toString();
if (text == null) {
text = "";
}
boolean bound = false;
if (binder != null) {
bound = binder.setViewValue(v, data, text);
}
if (!bound) {
if (v instanceof Checkable) {
if (data instanceof Boolean) {
((Checkable) v).setChecked((Boolean) data);
} else if (v instanceof TextView) {
// Note: keep the instanceof TextView check at the bottom of these
// ifs since a lot of views are TextViews (e.g. CheckBoxes).
setViewText((TextView) v, text);
} else {
throw new IllegalStateException(v.getClass().getName() +
" should be bound to a Boolean, not a " +
(data == null ? "<unknown type>" : data.getClass()));
}
} else if (v instanceof TextView) {
// Note: keep the instanceof TextView check at the bottom of these
// ifs since a lot of views are TextViews (e.g. CheckBoxes).
setViewText((TextView) v, text);
} else if (v instanceof ImageView) {
if (data instanceof Integer) {
setViewImage((ImageView) v, (Integer) data);
} else {
setViewImage((ImageView) v, text);
}
} else {
throw new IllegalStateException(v.getClass().getName() + " is not a " +
" view that can be bounds by this SimpleAdapter");
}
}
}
}
}
You can see if your view is ImageView , the code will use the url String be the resId in
/**
* Called by bindView() to set the image for an ImageView but only if
* there is no existing ViewBinder or if the existing ViewBinder cannot
* handle binding to an ImageView.
*
* By default, the value will be treated as an image resource. If the
* value cannot be used as an image resource, the value is used as an
* image Uri.
*
* This method is called instead of {#link #setViewImage(ImageView, int)}
* if the supplied data is not an int or Integer.
*
* #param v ImageView to receive an image
* #param value the value retrieved from the data set
*
* #see #setViewImage(ImageView, int)
*/
public void setViewImage(ImageView v, String value) {
try {
v.setImageResource(Integer.parseInt(value));
} catch (NumberFormatException nfe) {
v.setImageURI(Uri.parse(value));
}
}
And your error is here , so you need Override the getView function of SimpleAdapter.Here is code:
Uri uri = Uri.parse("http://gujjumatch.com/images/Girlnoimage.jpg");
image.setImageURI(uri);
You need to create adapter and extend it to BaseAdapter and add all your items and call it in your AsyncTask's method and it will return your output as said by Haresh Chellana.
I implemented my ListView using custom adapter extended from ArrayAdapter.
My problem is sometimes ListView is loaded slowly. That means a blank activity is loaded first without the ListView, then the ListView comes out. At worst case, I am prompted to "force closed or wait". I like to improve that slow loading as it is annoying to the user.
But sometimes, loading is fast and almost immediate.
But I like to make sure my ListView design is correct and the design does not have any problem with that slow loading. So that this discussion will be useful for other people who are facing the same problem as mine.
My ListView is designed as follow.
Each ListItem has three components, thumbnail image, ID text, and arrow image as shown in the figure attached .
In loading process of the ListView, (1)All ID text are retrieved from the database and populated into a ListArray List<String> listIDs
public class MyListFragment extends ListFragment implements OnItemClickListener {
dbHelper = new TrackerDBAdapter(getActivity());
dbHelpLatLong = new LatLogDBAdapter(getActivity());
dbHelpNotification = new NotificationDatabaseAdapter(getActivity());
dbHelper.open();
Cursor cursor = dbHelper.fetchAllTrackerInTheList();
listIDs = new ArrayList<String>();
activationStatus = new ArrayList<String>();
thisListFragmentContext = getActivity();
for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
listIDs.add(cursor.getString(1));
}
dbHelper.close();
(2)Then my custom list adapter is called.
adapter = new customList_Adaptor(thisListFragmentContext,
R.layout.list_row, listIDs, this);
}
That is the loading process inside my `ListFragment`.
(3) The following class is my custom ArrayAdapter and I implemented to load thumbnail ImageView using AsyncTask. My query are
(i)I still have retrieving ID text from database, and loading arrow image. Should I put those processes into AsyncTask as well?
(ii)If I need it, should I implement another AsyncTask or use the same AsyncTask used for thumbnail image loading?
(iii)Among these, which aspect of the program design I still can improve that is suspicious to my slow loading?
public class customList_Adaptor extends ArrayAdapter<String>{
protected static final int CAMERA_REQUEST = 0;
private TrackerDBAdapter dbHelper;
private Context context;
private List<String> listIDs = new ArrayList<String>();
private List<String> activationState = new ArrayList<String>();
public MyListFragment mMyListFragment;
public customList_Adaptor(Context context, int textViewResourceId,
List<String> objects, List<String> activationStatus, MyListFragment mMyListFragment) {
super(context, textViewResourceId, objects);
this.setContext(context);
this.listIDs = objects;
this.activationState = activationStatus;
this.mMyListFragment= mMyListFragment;
dbHelper = new TrackerDBAdapter(context);
}
#Override
public int getCount() {
// TODO Auto-generated method stub
if(listIDs != null)
return listIDs.size();
else
return 0;
}
#Override
public String getItem(int arg0) {
// TODO Auto-generated method stub
if(listIDs != null)
return listIDs.get(arg0);
else
return null;
}
#Override
public long getItemId(int arg0) {
// TODO Auto-generated method stub
return arg0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View vi=convertView;
ViewHolder viewHolder=new ViewHolder();
LayoutInflater inflater = (LayoutInflater) this.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if(vi==null){
vi = inflater.inflate(R.layout.list_row, parent, false);
viewHolder.id=(TextView)vi.findViewById(R.id.title);
viewHolder.thumbnailImage=(ImageView)vi.findViewById(R.id.list_image);
viewHolder.activationStatus = (TextView)vi.findViewById(R.id.activated);
//lazy load image
BitmapWorkerTask task = new BitmapWorkerTask(viewHolder.thumbnailImage);
task.execute(position);
viewHolder.arrow=(ImageView)vi.findViewById(R.id.list_arrow);
vi.setTag(viewHolder);
}
else
viewHolder=(ViewHolder) vi.getTag();
viewHolder.thumbnailImage.setOnClickListener(new onMyClick(position));
// Setting all values in listview
viewHolder.id.setText(listIDs.get(position));
if(activationState.get(position).equals("Not activated yet")){
viewHolder.activationStatus.setText(activationState.get(position));
viewHolder.activationStatus.setTextColor(android.graphics.Color.GRAY);
}
else if(activationState.get(position).equals("Activated"))
viewHolder.activationStatus.setText("");
return vi;
}
public class onMyClick implements OnClickListener {
private final int pos;
public onMyClick(int pos) {
this.pos = pos;
}
#Override
public void onClick(View v) {
MyListFragment.clickedimageView = (ImageView) v.findViewById(R.id.list_image);
mMyListFragment.imagepos(pos);
}
}
public Context getContext() {
return context;
}
public void setContext(Context context) {
this.context = context;
}
//Lazy image update
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
private final WeakReference<ImageView> imageViewReference;
private int data = 0;
public BitmapWorkerTask(ImageView imageView) {
// Use a WeakReference to ensure the ImageView can be garbage collected
imageViewReference = new WeakReference<ImageView>(imageView);
}
// Decode image in background.
#Override
protected Bitmap doInBackground(Integer... params) {
setData(params[0]);
Bitmap bitmap = null;
dbHelper.open();
Cursor mCursor = dbHelper.getImagebyIDnumber(getData());
byte[] img_bytes = mCursor.getBlob(13);
bitmap = BitmapFactory.decodeByteArray(img_bytes, 0, img_bytes.length);
dbHelper.close();
return bitmap;
}
// Once complete, see if ImageView is still around and set bitmap.
#Override
protected void onPostExecute(Bitmap bitmap) {
if (imageViewReference != null && bitmap != null) {
final ImageView imageView = imageViewReference.get();
if (imageView != null) {
imageView.setImageBitmap(bitmap);
}
}
}
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
}
}
public class ViewHolder {
TextView id;
TextView activationStatus;
ImageView thumbnailImage;
ImageView arrow;
}
I did a few things to make it faster in loading the app.
I am not sure which one is the solution.
(1) I load all data from sql database including text and thumbnail images using AsyncTask.
(2) I change thumbnail image format from png to jpg.
(3) Then I clear the cache manually.
The app looks like faster in loading, but sometimes it is still slow. Most of the times, it is faster than before.
I am still making improvement to my app.
Thanks
I have one ListView which can hold an image. It depends if image exists or not in SDCARD.
Here my example code:
public class MainActivity extends Activity {
ListView mListView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mListView = new ListView(this);
setContentView(mListView);
String[] arr = new String[] {
"/example/images/1.jpg", "/example/images/2.jpg",
"/example/images/3.jpg", "/example/images/4.jpg",
"/example/images/5.jpg", "/example/images/6.jpg",
"/example/images/7.jpg", "/example/images/8.jpg",
"/example/images/9.jpg", "/example/images/1.jpg",
"/example/images/2.jpg", "/example/images/3.jpg",
"/example/images/4.jpg", "/example/images/5.jpg",
"/example/images/6.jpg", "/example/images/7.jpg",
"/example/images/8.jpg", "/example/images/9.jpg",
"/example/images/1.jpg", "/example/images/2.jpg",
"/example/images/3.jpg", "/example/images/4.jpg",
"/example/images/5.jpg", "/example/images/6.jpg",
"/example/images/7.jpg", "/example/images/8.jpg",
"/example/images/9.jpg", "/example/images/1.jpg",
"/example/images/2.jpg", "/example/images/3.jpg",
"/example/images/4.jpg", "/example/images/5.jpg",
"/example/images/6.jpg", "/example/images/7.jpg",
"/example/images/8.jpg", "/example/images/9.jpg"};
List<String> list = Arrays.asList(arr);
MyAdapter adapter = new MyAdapter(this, R.layout.listitem_imv, list);
mListView.setAdapter(adapter);
}
class MyAdapter extends ArrayAdapter<String>{
List<String> mList;
LayoutInflater mInflater;
int mResource;
public MyAdapter(Context context, int resource,
List<String> objects) {
super(context, resource, objects);
mResource = resource;
mInflater = getLayoutInflater();
mList = objects;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view;
if(convertView == null){
view = mInflater.inflate(mResource, null);
}else{
view = convertView;
}
ImageView imageView = (ImageView) view.findViewById(R.id.imv);
TextView textView = (TextView) view.findViewById(R.id.txv);
imageView.setTag(mList.get(position));//tag of imageView == path to image
new LoadImage().execute(imageView);
textView.setText(mList.get(position).toString());
return view;
}
}
class LoadImage extends AsyncTask<Object, Void, Bitmap>{
private ImageView imv;
private String path;
#Override
protected Bitmap doInBackground(Object... params) {
imv = (ImageView) params[0];
path = imv.getTag().toString();
Bitmap bitmap = null;
File file = new File(
Environment.getExternalStorageDirectory().getAbsolutePath() + path);
if(file.exists()){
bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
}
return bitmap;
}
#Override
protected void onPostExecute(Bitmap result) {
if(result != null && imv != null){
imv.setVisibility(View.VISIBLE);
imv.setImageBitmap(result);
}else{
imv.setVisibility(View.GONE);
}
}
}
}
The 'sdcard/example/images' directory has the images: 1.jpg, 2.jpg, 3.jpg, 4.jpg, 6.jpg, 7.jpg and 9.jpg.
the expected result is:
But, if I scroll the list quickly, some images are inserted in the wrong items.
It happens due to use of convertView in getView() method.
If I use the following code, the code works fine:
//if(convertView == null){
// view = mInflater.inflate(mResource, null);
//}else{
// view = convertView;
//}
view = mInflater.inflate(mResource, null);
When list scrolled quickly, two asyncTasks can reference one same View, due to use of convertView.
How Can I cancel AsyncTask when the View is no longer visible?(and is useb by another item of ListView)
edit
#Override
protected void onPostExecute(Bitmap result) {
if(result != null && imv != null){
if(imv.getTag().equals(path)){
imv.setVisibility(View.VISIBLE);
imv.setImageBitmap(result);
}else{
imv.setVisibility(View.GONE);
}
}else{
imv.setVisibility(View.GONE);
}
}
You can send in the ImageView to the task constructor and keep a reference to the image path there. Now at onPostExecute, check if the current tag of the ImageView is the same as the one that you started with. If yes, then set the image. If no, don't do anything.
However, this means that the image will be downloaded in any case. You'll just not set the wrong image on the view.
EDIT:
First pass the ImageView to the task constructor:
new LoadImage(imageView).execute()
Then save a reference to the ImageView and image path in LoadImage constructor. It is important to save the path in the constructor and not in doInBackground to ensure that we don't run into multi threading problems. Then at onPostExecute we check the current path.
class LoadImage extends AsyncTask<Object, Void, Bitmap>{
private ImageView imv;
private String path;
public LoadImage(ImageView imv) {
this.imv = imv;
this.path = imv.getTag().toString();
}
#Override
protected Bitmap doInBackground(Object... params) {
Bitmap bitmap = null;
File file = new File(
Environment.getExternalStorageDirectory().getAbsolutePath() + path);
if(file.exists()){
bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
}
return bitmap;
}
#Override
protected void onPostExecute(Bitmap result) {
if (!imv.getTag().toString().equals(path)) {
/* The path is not same. This means that this
image view is handled by some other async task.
We don't do anything and return. */
return;
}
if(result != null && imv != null){
imv.setVisibility(View.VISIBLE);
imv.setImageBitmap(result);
}else{
imv.setVisibility(View.GONE);
}
}
}
This Android Developers Blog post will give you a complete reference project for this complete with caching. Just replace the Http access code with SD card file reads.
I hope this helps.
After lot of search I have this working solution.
public class CustomAdapter extends ArrayAdapter<String>{
/*
public CustomAdapter(Context context , String[] video) {
super(context,R.layout.custom_row, video);
}
*/
private final Activity context;
private final String[] video;
static class ViewHolder {
public TextView videoTitle;
public ImageView videoThumbnail;
public int position;
public String path;
}
public CustomAdapter(Activity context, String[] video) {
super(context, R.layout.custom_row, video);
this.context = context;
this.video = video;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
LayoutInflater videoInflator = LayoutInflater.from(getContext());
View customView = videoInflator.inflate(R.layout.custom_row, parent, false);
ViewHolder viewHolder = new ViewHolder();
viewHolder.position = position;
viewHolder.path = video[position];
viewHolder.videoTitle = (TextView) customView.findViewById(R.id.videoTitle);
viewHolder.videoThumbnail = (ImageView) customView.findViewById(R.id.videoThumbnail);
//rowView.setTag(viewHolder);
//}
customView.setTag(viewHolder);
final String videoItem = video[position];
int index=videoItem.lastIndexOf('/');
String lastString=(videoItem.substring(index +1));
index = lastString.indexOf(".mp4");
lastString=(lastString.substring(0,index));
viewHolder.videoTitle.setText(lastString);
new AsyncTask<ViewHolder, Void, Bitmap>() {
private ViewHolder v;
#Override
protected Bitmap doInBackground(ViewHolder... params) {
v = params[0];
Bitmap thumb = ThumbnailUtils.createVideoThumbnail(videoItem, MediaStore.Images.Thumbnails.MINI_KIND);
return thumb;
}
#Override
protected void onPostExecute(Bitmap result) {
super.onPostExecute(result);
if (v.position == position) {
// If this item hasn't been recycled already, hide the
// progress and set and show the image
v.videoThumbnail.setImageBitmap(result);
}
}
}.execute(viewHolder);
return customView;
}
}
Maybe you should try:
view = mInflater.inflate(mResource,parent,null);
Check this blog it explains the similar issue:
http://www.doubleencore.com/2013/05/layout-inflation-as-intended/
What I would do (unless you have thousands of images):
1. create a data structure - a simple class holding a String name to be displayed and a bitmap
2. create an adapter for it
3. in the getView method assign the correct bitmap to the correct ImageView.
In your case though you can create a similar data structure but holding not a bitmap but an AsyncTask. Anyway you need to bind the asynctask to the string into one item. An array (or arraylist) of such items will be fed to your adapter. Displayed will be an imageview and a textview.
AsyncTask can be cancelled with cancel().
Hey I found the solution to this problem just use following function instead of your function
#Override
protected void onPostExecute(Bitmap result) {
if (!imv.getTag().toString().equals(rec_id)) {
return;
}
if(result != null && imv != null){
int index = id.indexOf(imv.getTag().toString());
if(list.getFirstVisiblePosition()<=index && index<=list.getLastVisiblePosition())
{
imv.setVisibility(View.VISIBLE);
imv.setImageBitmap(result);
}
}else{
imv.setImageBitmap(icon);
imv.setVisibility(View.GONE);
}
}
Here list is the object of listview. Just pass your list view object to your adapter and paste this function instead of your onPostExecute function.