I have some custom listview adapter, in adapter I use ViewHolder pattern,it is work correctly for anything but not for Bitmaps.
That code is work but plase that work with bitmap don't use viewholder. I commented it for test, so now I see that, viewholder not work with bitmaps.
If someone know why or have any idea please told me how to fix.
There is my code:
public class FrontListAdapter extends ArrayAdapter<GoodObject> {
private Context context;
private AdapterLoadNotifier notifier;
private List<GoodObject> list;
public HashMap<String, Bitmap> avatars = new HashMap<String, Bitmap>();
public FrontListAdapter(MainActivity activity, List<GoodObject> objects) {
super(activity, R.layout.row_front_layout, objects);
this.context = activity;
this.list = objects;
this.notifier = activity;
}
static class ViewHolder {
static TextView shopTitle;
static TextView time;
static TextView description;
static ImageView goodsImage;
static ProgressBar loadingGoodsImageDialogue;
static ImageView shopAvatar;
static ImageView loadingAvatarHolder;
static ProgressBar loadingAvatarDialogue;
static TextView commentAuthor;
static TextView commentText;
static Button likeButton;
static Button commentButton;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if (convertView == null) {
LayoutInflater inflater = LayoutInflater.from(context);
convertView = inflater.inflate(R.layout.row_front_layout, null);
viewHolder = new ViewHolder();
viewHolder.shopTitle = (TextView) convertView.findViewById(R.id.row_front_layout_shop_title);
viewHolder.time = (TextView) convertView.findViewById(R.id.row_front_layout_time);
viewHolder.description= (TextView) convertView.findViewById(R.id.row_front_layout_description_label);
viewHolder.goodsImage = (ImageView) convertView.findViewById(R.id.row_front_layout_good_image);
viewHolder.loadingGoodsImageDialogue = (ProgressBar) convertView.findViewById(R.id.row_front_layout_good_image_progress);
viewHolder.shopAvatar = (ImageView) convertView.findViewById(R.id.row_front_layout_logo);
viewHolder.loadingAvatarDialogue = (ProgressBar) convertView.findViewById(R.id.row_front_layout_avatar_image_progress);
viewHolder.loadingAvatarHolder = (ImageView) convertView.findViewById(R.id.row_front_layout_logo_back);
viewHolder.commentAuthor = (TextView) convertView.findViewById(R.id.row_front_layout_first_comment_author);
viewHolder.commentText = (TextView) convertView.findViewById(R.id.row_front_layout_first_comment_text);
viewHolder.likeButton = (Button) convertView.findViewById(R.id.row_front_layout_like_button);
viewHolder.commentButton = (Button) convertView.findViewById(R.id.row_front_layout_comment_button);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
GoodObject goodObject = list.get(position);
String shopTitle = goodObject.getShopTitle();
Bitmap avatar = goodObject.getAvatar();
Bitmap image = goodObject.getGoods_logo();
viewHolder.shopTitle.setText(shopTitle);
viewHolder.time.setText(goodObject.getTime());
viewHolder.description.setText(goodObject.getDescription());
if (image != null) {
//viewHolder.goodsImage.setImageBitmap(image);
//viewHolder.loadingGoodsImageDialogue.setVisibility(View.GONE);
((ImageView) convertView.findViewById(R.id.row_front_layout_good_image)).setImageBitmap(image);
convertView.findViewById(R.id.row_front_layout_good_image_progress).setVisibility(View.GONE);
}
if (avatar != null) {
convertView.findViewById(R.id.row_front_layout_avatar_image_progress).setVisibility(View.GONE);
convertView.findViewById(R.id.row_front_layout_logo_back).setVisibility(View.GONE);
//viewHolder.loadingAvatarDialogue.setVisibility(View.GONE);
//viewHolder.loadingAvatarHolder.setVisibility(View.GONE);
}
((ImageView) convertView.findViewById(R.id.row_front_layout_logo)).setImageBitmap(avatars.get(shopTitle));
//viewHolder.shopAvatar.setImageBitmap(avatars.get(shopTitle));
viewHolder.commentAuthor.setText(goodObject.getLast_comment().getUser() + ":");
viewHolder.commentText.setText(goodObject.getLast_comment().getComment());
viewHolder.likeButton.setText(Integer.toString(goodObject.getLikesAmount()));
viewHolder.commentButton.setText(Integer.toString(goodObject.getCommentsAmount()));
if (position == list.size() - 2 && !notifier.isEnded()) {
notifier.loadNextPage();
}
return convertView;
}
}
For fix bug the fields in the ViewHolder class musn't be static:
static class ViewHolder {
TextView shopTitle;
TextView time;
TextView description;
ImageView goodsImage;
ProgressBar loadingGoodsImageDialogue;
ImageView shopAvatar;
ImageView loadingAvatarHolder;
ProgressBar loadingAvatarDialogue;
TextView commentAuthor;
TextView commentText;
Button likeButton;
Button commentButton;
}
In that case all work great and correctly.
Related
I'm working on a project that has a list view should update automatically with database changes, I'm working on it for a long time and find a solution that using ViewHolder pattern, and I tried this way and it's work really! but now I try manyyyy solution to do it again, it's not working!! pls help me or tell me if there is a tip in using this pattern.
Thanks really!
public class DownloadItem_ViewHolder {
private boolean hasInited = false;
public DownloadItem_ViewHolder() {
}
public DownloadItem_ViewHolder(View convertView) {
if (convertView != null) {
App_icon = (ImageView) convertView
.findViewById(R.id.Download_app_icon);
App_name = (TextView) convertView
.findViewById(R.id.Downlaod_app_name);
Downloaded_percent = (TextView) convertView
.findViewById(R.id.Downloaded_app_percent);
Downloaded_progress = (ProgressBar) convertView
.findViewById(R.id.Downloaded_app_progress);
Pause_btn = (ImageView) convertView
.findViewById(R.id.Download_pause_btn);
Resume_btn = (ImageView) convertView
.findViewById(R.id.Download_Resume_btn);
hasInited = true;
}
}
public void setPorgress(int progress) {
Downloaded_progress.setProgress(progress);
// Downloaded_percent.setText(String.valueOf(progress) + "%");
}
public void setData(DownloadManagerItem_Cls dlItem) {
if (hasInited) {
// this.App_icon.setBackground(newDownloadManager.getDownloadItemApp(
// dlItem.getItemID()).getAppIcon());
// App_name.setText(newDownloadManager.getDownloadItemApp(
// dlItem.getItemID()).getAppName()
// + "-->" + dlItem.getItemID());
this.Downloaded_progress
.setProgress(dlItem.getDownloadPercentage());
// this.Downloaded_percent.setText(String.valueOf(dlItem
// .getDownloadPercentage()) + "%");
// DownloadStatus downloadItemStatus = dlItem.getDownloadStatus();
// // PAUSE
// if (downloadItemStatus.equals(DownloadStatus.Pause)) {
// // Resume
// Resume_btn.setVisibility(View.VISIBLE);
// // Pause
// Pause_btn.setVisibility(View.GONE);
// } else if (downloadItemStatus.equals(DownloadStatus.Downloading))
// {// Resume
// Resume_btn.setVisibility(View.GONE);
// // Pause
// Pause_btn.setVisibility(View.VISIBLE);
// } else if (downloadItemStatus.equals(DownloadStatus.Complete)) {
// // Resume
// Resume_btn.setVisibility(View.GONE);
// // Pause
// Pause_btn.setVisibility(View.GONE);
// // Install or Run
// }
}
}
ImageView App_icon;
public TextView App_name;
ProgressBar Downloaded_progress;
TextView Downloaded_percent;
public ImageView Resume_btn;
public ImageView Pause_btn;
}
public class DownloadItemAdpater extends ArrayAdapter<DownloadManagerItem_Cls> {
int _rowResourceID;
public DownloadItemAdpater(Context context, int rowResourceID,
List<DownloadManagerItem_Cls> DownloadedItems) {
super(context, rowResourceID, DownloadedItems);
_rowResourceID = rowResourceID;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
// final int AppID = DownloadManager.DownloadedList.get(position).ApkID;
if (convertView == null) {
LayoutInflater vi = (LayoutInflater) getContext().getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
convertView = vi.inflate(R.layout.download_history_item, null);
// store the holder with the view.
// convertView.setTag(1, viewHolder);
}
convertView.setTag(getItem(position).getItemID());
DownloadItem_ViewHolder viewHolder = new DownloadItem_ViewHolder(
convertView);
viewHolder.setData(getItem(position));
viewHolder.App_name.setText(String.valueOf(getItem(position)
.getItemID()));
// set data
// viewHolder.Pause_btn.setOnClickListener(new DownloadBtnListener(
// position, viewHolder));
// viewHolder.Resume_btn.setOnClickListener(new DownloadBtnListener(
// position, viewHolder));
return convertView;
}
public class DownloadManager_Act extends ListActivity {
DownloadItemAdpater dlAdapter;
ArrayList<DownloadManagerItem_Cls> listHistory;
// #Override
// protected void onDestroy() {
// unregisterReceiver(mReceiver);
// super.onDestroy();
// }
MyReceiver mReceiver;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.dl_manager);
listHistory = CP_Connector.GDM_QueueTable_Connector.getDownloadList();
dlAdapter = new DownloadItemAdpater(getApplicationContext(),
R.layout.download_history_item, listHistory);
setListAdapter(dlAdapter);
mReceiver = new MyReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction("com.glx.appscenter.dlmanager");
registerReceiver(mReceiver, filter);
}
public class MyReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent != null
&& intent.getAction()
.equals("com.glx.appscenter.dlmanager")) {
try {
int dlItemID = Integer.valueOf(intent
.getStringExtra("itemID"));
if (dlItemID >= 0) {
DownloadManagerItem_Cls dlTempItem = CP_Connector.GDM_QueueTable_Connector
.getDownloadItemByID(dlItemID);
View v = getListView().findViewWithTag(
dlTempItem.getItemID());
TextView tv = (TextView) v
.findViewById(R.id.Downlaod_app_name);
Utility.appUtility.ShowToast(String.valueOf(tv
.getText() + ":" + dlItemID));
DownloadItem_ViewHolder viewHolder = new DownloadItem_ViewHolder(
v);
viewHolder.setPorgress(dlTempItem
.getDownloadPercentage());
dlAdapter.notifyDataSetChanged();
}
} catch (Exception e) {
}
}
}
}
}
if you want to set adapter then follow this code.
adapter=new AuctionListAdapter(getActivity(), listAuctionBeans,progressBar,1);
setListAdapter(adapter);
if you wan to refresh list with new data change follow this
adapter.addData(getActivity(), listAuctionBeans);
adapter.notifyDataSetChanged();
and adapter is you your d1Adapter.
I've been reading about ViewHolder Pattern and it's effects on ListView scrolling performance lately.
For a smooth scrolling, fast ListView should i avoid using OnClickListener registerations inside adapter getView() method such as:
#Override
public View getView(final int position, View convertView, ViewGroup parent)
{
ViewHolder holder;
if (convertView == null)
{
holder = new ViewHolder();
LayoutInflater vi = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = vi.inflate(mResourceId, null);
holder.btn1 = (TextView) convertView.findViewById(R.id.btn1);
holder.img1 = (TextView) convertView.findViewById(R.id.img1);
convertView.setTag(holder);
} else { holder = (ViewHolder) convertView.getTag(); }
final Items item = getItem(position);
holder.btn1.setText(item.btnText);
holder.img1.setBackgroundResource(item.imgSource);
holder.img1.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) { /* .. my code block USING POSITION ARG .. */ }
}
holder.btn1.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) { /* .. My Code Block USING POSITION ARG .. */ }
}
return convertView;
}
If so, is registering a OnItemClickListener to ListView instance as following sample does a good practice:
myListView.setOnItemClickListener(new OnItemClickListener()
{
#Override
public void onItemClick(AdapterView<?> arg0, View view, int position, long id) {
if (view.getId() == R.id.btn1)
{
// my code block USING POSITION ARG
}
else if (view.getId() == R.id.img1)
{
// my code block USING POSITION ARG
}
}
});
It's better to use a Wrapper to access to your View and to define your OnClickListener earlier (and outside the adapter for a better usability).
The following sample show how to handle 2 clickable View on one single item of the ListView with good performance:
public class ItemAdapter extends BaseAdapter {
private List<Item> items;
private ItemWrapper wrapper = null;
private OnClickListener onMyItemClickListener1;
private OnClickListener onMyItemClickListener2;
public ItemAdapter(Context context, List<Item> items, OnClickListener onMyItemClickListener1, OnClickListener onMyItemClickListener2) {
this.inflater = LayoutInflater.from(context);
this.items = items;
this.onMyItemClickListener1 = onMyItemClickListener1;
this.onMyItemClickListener2 = onMyItemClickListener2;
}
#Override
public int getCount() {
return items.size();
}
#Override
public Object getItem(int position) {
return items.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public synchronized View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
if (row == null) {
row = inflater.inflate( R.layout.item, null);
wrapper = new ItemWrapper(row);
row.setTag(wrapper);
} else {
wrapper = (ItemWrapper)row.getTag();
}
Item item = getItem(position);
wrapper.getClickView1().setOnClickListener(onMyItemClickListener1);
wrapper.getClickView2().setOnClickListener(onMyItemClickListener2);
return(row);
}
}
public class ItemWrapper {
private View baseView;
private View clickView1;
private View clickView2;
public ItemWrapper(View baseView) {
this.baseView = baseView;
}
public View getClickView1() {
if ( clickView1 == null) {
clickView1 = (View)baseView.findViewById(R.id.clickView1);
}
return(clickView1);
}
public View getClickView2() {
if ( clickView2 == null) {
clickView2 = (View)baseView.findViewById(R.id.clickView2);
}
return(clickView2);
}
}
I have created custom listview in which i have
textView,imageView.
I want to set id to the each row's imageView so that when i click on that image,can get the id(for the purpose of uniqueness).I have also a click listener in getView.So I want to get that id on click of that particular image but when i click on that image everytime i am getting 3,I don't no why this is happening.Any help would be appreciable.
public View getView(int position, View convertView, ViewGroup parent) {
final CruunNotificationModel cruun = userDetails.get(position);
LayoutInflater mInflater = (LayoutInflater) context
.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
if (convertView == null) {
convertView = mInflater.inflate(R.layout.imagewithlist, parent,
false);
holder = new ViewHolder();
holder.userName = (TextView) convertView
.findViewById(R.id.userNameTextView);
holder.userTask = (TextView) convertView
.findViewById(R.id.userTaskTextView);
holder.dateTime = (TextView) convertView
.findViewById(R.id.dateTimeTextView);
holder.userComments = (TextView) convertView
.findViewById(R.id.userCommentsTextView);
holder.userImage = (ImageView) convertView
.findViewById(R.id.userImageImageView);
holder.expandImage = (ImageView) convertView
.findViewById(R.id.expandTextImageView);
holder.commentPart = (LinearLayout) convertView
.findViewById(R.id.commentlayout);
holder.view = (View) convertView.findViewById(R.id.view);
convertView.setTag(holder);
} else
holder = (ViewHolder) convertView.getTag();
holder.userComments.setText(cruun.getComments());
holder.userName.setText(cruun.getUserName());
holder.userTask.setText(cruun.getUserTask());
holder.dateTime.setText(cruun.getDateAndTime());
holder.userImage.setImageResource(cruun.getImageId());
holder.expandImage.setImageResource(R.drawable.down_arrow);
holder.expandImage.setId(position);
holder.expandImage.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
holder.userComments.setEllipsize(null);
RelativeLayout.LayoutParams lParams = new RelativeLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
lParams.setMargins(2, 10, 2, 0);
holder.userComments.setMaxLines(Integer.MAX_VALUE);
lParams.addRule(RelativeLayout.BELOW, R.id.userImageLayout);
holder.userComments.setLayoutParams(lParams);
holder.expandImage.setImageResource(R.drawable.up_arrow);
notifyDataSetChanged();
if (v.getId() == holder.expandImage.getId()) {
holder.view.setVisibility(View.VISIBLE);
holder.commentPart.setVisibility(View.VISIBLE);
}
}
});
return convertView;
}
I have a ListView that is populate by taking photos. Each time I take a photo, the photo gets added as an ImageView entry to the listview, together with the date etc. When the photo is taken, the Uri is saved into an arraylist in memory and when the listview is displayed, it loads the images for the rows from the arraylist uris into bitmaps (scaled down) and then into drawables for the imageviews. Everything works fine, however, when the listview gets longer, it gets really slow and sometimes I get an out of memory on byte allocation error.
Here is where I populate the view for the listview in my own custom adapter:
public View getView(int position, View convertView, ViewGroup parent)
{
Uri selectedImage2 = Uri.parse(sampleuris.get(position).toString());
Bitmap ThumbImage = null;
if(convertView == null)
{
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.row_list_sample_entry, parent , false);
}
ImageView sampleimage = (ImageView) convertView.findViewById(R.id.sampleimage);
if(ThumbImage == null)
{
try
{
ThumbImage = ThumbnailUtils.extractThumbnail(decodeUri(selectedImage2), 100, 100);
}
catch (FileNotFoundException ex)
{
Logger.getLogger(Screen_View_Package.class.getName()).log(Level.SEVERE, null, ex);
}
Drawable d = new BitmapDrawable(null, ThumbImage);
sampleimage.setImageDrawable(d);
}
else
{
Drawable d = new BitmapDrawable(null, ThumbImage);
sampleimage.setImageDrawable(d);
}
TextView position2 = (TextView) convertView.findViewById(R.id.packageimage2);
TextView datereceived = (TextView) convertView.findViewById(R.id.date);
position2.setText(""+(position+1));
datereceived.setText(sampledates.get(position));
//textSynced.setText("Synced: "+syncs.get(position));
return convertView;
}
//----------------------------------------
I have fixed the problem by converting the bitmap images to byte arrays before the listview populates and then for the listview, I just use the bytearray which is stored in memory to create drawable and display them. No Memory errors. Heres the new code for the listview's getview :
public View getView(int position, View convertView, ViewGroup parent)
{
//Uri selectedImage2 = Uri.parse(sampleuris.get(position).toString());
//Bitmap ThumbImage = null;
if(convertView == null)
{
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.row_list_sample_entry, parent , false);
}
ImageView sampleimage = (ImageView) convertView.findViewById(R.id.sampleimage);
byte[] b = Base64.decode(sampleuris.get(position),Base64.DEFAULT);
ByteArrayInputStream is = new ByteArrayInputStream(b);
Drawable d = Drawable.createFromStream(is, "bloodsample");
sampleimage.setImageDrawable(d);
TextView position2 = (TextView) convertView.findViewById(R.id.packageimage2);
TextView datereceived = (TextView) convertView.findViewById(R.id.date);
position2.setText(""+(position+1));
datereceived.setText(sampledates.get(position));
//textSynced.setText("Synced: "+syncs.get(position));
return convertView;
}
I have a main activity (FragmentDemoActivity) which creates 2 tabs. Fragment A and Fragment B.
I have another class called CategoryList which loads a customlist from an sqlite database.
I am using this example which works great for the TAB parts.
I want to load the CategoryList into the Fragment A.
My CategoryList class is as follows
// All the imports
public class CategoryList extends BaseActivity{
ImageView imgFeedback;
ImageView imgAbout;
ListView listCategories;
ProgressBar prgLoading;
TextView txtAlert;
AdView ads;
static String[] CategoryName ;
ListAdapter la;
static int[] id;
List<String> catList;
static DBHelper dbhelper;
class ListAdapter extends BaseAdapter {
private LayoutInflater inflater;
#SuppressWarnings("unused")
private Context ctx;
public ListAdapter(Context context) {
inflater = LayoutInflater.from(context);
ctx = context;
}
#Override
public int getCount() {
return CategoryName.length;
}
#Override
public Object getItem(int position) {
return position;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if(convertView == null){
convertView = inflater.inflate(R.layout.row, null);
holder = new ViewHolder();
holder.imgPreview = (ImageView) convertView.findViewById(R.id.imgPreview);
holder.txtRecipeName = (TextView) convertView.findViewById(R.id.txtRecipeName);
convertView.setTag(holder);
}else{
holder = (ViewHolder) convertView.getTag();
}
holder.imgPreview.setImageResource(getResources().
getIdentifier(CategoryName[position], "drawable", getPackageName()));
String categoryName = CategoryName[position];
categoryName = categoryName.toLowerCase();
categoryName = Character.toString(categoryName.charAt(0)).toUpperCase()+categoryName.substring(1);
holder.txtRecipeName.setText(categoryName);
return convertView;
}
class ViewHolder {
ImageView imgPreview;
TextView txtRecipeName;
}
}
final Context context = this ;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.categories_list);
AppRater.displayAsk2Rate(this, 1, 1, false);
getSupportActionBar().setHomeButtonEnabled(true);
dbhelper = new DBHelper(this);
try {
dbhelper.createDataBase();
}catch(IOException ioe){
throw new Error("Unable to create database");
}
/** then, the database will be open to use */
try{
dbhelper.openDataBase();
}catch(SQLException sqle){
throw sqle;
}
CategoryName = dbhelper.getAllCategories();
dbhelper.close();
catList = Arrays.asList(CategoryName);
/* imgAbout = (ImageView) findViewById(R.id.imgAbout);
imgAbout.setOnClickListener(new ButtonListner(CategoryList.this));
imgFeedback = (ImageView) findViewById(R.id.imgFeedback);
imgFeedback.setOnClickListener(new ButtonListner(CategoryList.this));*/
listCategories = (ListView) findViewById(R.id.listCategories);
la = new ListAdapter(CategoryList.this);
listCategories.setAdapter(la);
txtAlert = (TextView) findViewById(R.id.txtAlert);
ads = (AdView) findViewById(R.id.ads);
Ads.loadAds(ads);
listCategories.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapter, View view, int position, long id) {
String category = catList.get(position);
Intent i = new Intent(CategoryList.this, RecipesList.class);
i.putExtra("Category_Name", category);
startActivity(i);
}
});
}
}
My FragmentA is as follows
public class FragmentA extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle saved)
{
View view = inflater.inflate(R.layout.categories_list, container, false);
ListView listCategories = (ListView) getActivity().findViewById(R.id.listCategories);
ListAdapter lav = new ListAdapter(CategoryList.this);
listCategories.setAdapter(lav);
return view;
}
#Override
public void onActivityCreated (Bundle savedInstanceState)
{
super.onActivityCreated(savedInstanceState);
}
}
Any pointers ?