Custom ListView doesn't save it's state - android

I was using standart android ListView with simle_list_item_multiple_choice item layout and custom adapter. It saved it's stated on pause and restored on resume. But after few days of working on I've implemented SectionIndexer, StickyListHeadersAdapter interface within my adapter and changed ListView to StickyListHeadersListView from this library. Also there were other changes in application but these are only attached to ListView. However now when my application resumes working listview is coming scrolled to begining and with all items unchecked. I've tryed to remove SectionIndexer interface and sticky headers support but it had no effect. Maybe there is some hidden options of listview which I need to enable?
(tried saveState property - no effect)
public class WordAdapter extends ArrayAdapter<Word> implements SectionIndexer, StickyListHeadersAdapter{
// -----------------------------------------------------------------------
//
// Fields
//
// -----------------------------------------------------------------------
private int mResID;
private List<Word> mList;
private String[] mSectionNames;
private StickyListHeadersListView mListView;
private int[] mSectionIndexes;
private boolean mHeadersEnabled;
// -----------------------------------------------------------------------
//
// Constructor
//
// -----------------------------------------------------------------------
public WordAdapter(Context context, StickyListHeadersListView listView, int resID, List<Word> list, String[] sectionNames, int[] sectionIndexes, boolean enableHeaders) {
super(context, resID, list);
mResID = resID;
mList = list;
mListView = listView;
mSectionIndexes = sectionIndexes;
mSectionNames = sectionNames;
mHeadersEnabled = enableHeaders;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(mResID, parent, false);
holder = new ViewHolder();
holder.root = convertView;
holder.textView = (TextView) convertView.findViewById(android.R.id.text1);
holder.checkBox = (CheckBox) convertView.findViewById(android.R.id.checkbox);
convertView.setTag(holder);
}
holder = (ViewHolder) convertView.getTag();
holder.textView.setText(getItem(position).getValue());
if(mListView.isItemChecked(position))
holder.root.setBackgroundColor(getContext().getResources().getColor(R.color.highlight));
else
holder.root.setBackgroundColor(getContext().getResources().getColor(android.R.color.transparent));
return convertView;
}
#Override
public View getHeaderView(int position, View convertView, ViewGroup parent) {
if(!mHeadersEnabled)
return new View(getContext());
HeaderViewHolder holder;
if (convertView == null) {
holder = new HeaderViewHolder();
convertView = LayoutInflater.from(getContext()).inflate(R.layout.list_item_header, parent, false);
holder.labelView = (TextView) convertView.findViewById(R.id.list_item_header_label);
convertView.setTag(holder);
} else {
holder = (HeaderViewHolder) convertView.getTag();
}
String label;
if(mSectionNames != null){
label = mSectionNames[getSectionForPosition(position)];
holder.labelView.setText(label);
}
return convertView;
}
#Override
public long getHeaderId(int position) {
long result = getSectionForPosition(position);
return result;
}
#Override
public int getPositionForSection(int section) {
return (mSectionIndexes == null || section < 0 ) ? 0 : (section == mSectionIndexes.length ? mList.size():mSectionIndexes[section]);
}
#Override
public int getSectionForPosition(int position) {
if(mSectionIndexes != null && position >= mSectionIndexes[0]) {
for(int i = 0; i < mSectionIndexes.length; ++i)
if(position < mSectionIndexes[i])
return i-1;
return (mSectionIndexes.length - 1);
}
return 0;
}
#Override
public Object[] getSections() {
return mSectionNames == null ? new Object[0] : mSectionNames;
}
// -------------------------------------------------------------------------------
//
// Internal classes
//
// -------------------------------------------------------------------------------
private static class ViewHolder {
public View root;
public TextView textView;
public CheckBox checkBox;
}
private static class HeaderViewHolder {
public TextView labelView;
}
Here is adapter code.

Related

Android Listview is repeating items when scrolled

I have looked at other threads and I cannot see what is wrong with my listadapter. I have the view holder pattern and I am setting all the values for the list item outside of the convertview null check statement. Im not sure what other things would cause it to repeat the items.
public class ScheduledJobListAdapter extends BaseAdapter {
private static LayoutInflater inflater = null;
private ArrayList<Job> jobList;
private Context context;
public ScheduledJobListAdapter(Context context, ArrayList<Job> jobList) {
this.context = context;
this.jobList = jobList;
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() { return jobList.size(); }
#Override
public Job getItem(int position) { return jobList.get(position); }
#Override
public long getItemId(int position) { return 0; }
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
final ScheduledViewHolder holder;
if (convertView == null) {
convertView = inflater.inflate(R.layout.list_item_scheduled_job,null);
holder = new ScheduledViewHolder();
holder.job = getItem(position);
holder.container = convertView.findViewById(R.id.scheduled_job_layout);
holder.routeOrder = convertView.findViewById(R.id.route_order_textview);
holder.location = convertView.findViewById(R.id.location_textview);
holder.jobRef = convertView.findViewById(R.id.job_ref_textview);
holder.statusIndicator = convertView.findViewById(R.id.status_indicator);
convertView.setTag(holder);
} else {
holder = (ScheduledViewHolder) convertView.getTag();
}
holder.routeOrder.setText(holder.job.getRouteOrder() + "");
holder.location.setText(holder.job.getLocation());
holder.jobRef.setText(holder.job.getJobReference());
return convertView;
}
}
class ScheduledViewHolder {
Job job;
LinearLayout container;
TextView routeOrder;
TextView location;
TextView jobRef;
ImageView statusIndicator;
}
Here's the problem:
holder.job = getItem(position);
Remember the views may be recycled as you scroll, and the job assigned maybe used unintentionally for other positions if you assigned it that way. To fix it, simply assign the job after the if-else condition:
if (convertView == null) {
// ...
} else {
// ...
}
holder.job = getItem(position); // Update the job by position
holder.routeOrder.setText(holder.job.getRouteOrder() + "");
holder.location.setText(holder.job.getLocation());
holder.jobRef.setText(holder.job.getJobReference());

GridView item position changing position dynamically while scrolling

I am using gridview layout for displaying multiple item and items are displaying fine, if I check on the big screen then all work done is fine because no scrolling is required.
But if I user small screen while scrolling it changing position dynamically.
** Example:**
If I display 50 items it working fine if I display 1000 items I need to scroll down, on that time selected items are changing their positions dynamically.
Adapter Code
public class EM_event_total_userseatsAdapter extends ArrayAdapter<EM_event_total_UserSeatsModel> implements View.OnClickListener {
ArrayList<EM_event_total_UserSeatsModel> dataSet;
public ArrayList<Integer> EMeventuserseatslist = new ArrayList<Integer>();
Context mContext;
ViewHolder holder;
String user_seats;
private int[] tagCollection;
private String[] mobileValues;
private String[] mobileValuesD;
private static class ViewHolder {
TextView TvEmUserSeats;
ImageView IvUsreSeats,available,selctedimag;
}
private String[] strings;
List<Integer> selectedPositions = new ArrayList<>();
SparseBooleanArray selectedItems;
public
EM_event_total_userseatsAdapter(ArrayList<EM_event_total_UserSeatsModel> data, Context context) {
super(context, R.layout.list_em_get_seats, data);
this.dataSet = data;
this.mContext=context;
selectedItems = new SparseBooleanArray();
}
public int getTagFromPosition(int position) {
return tagCollection[position];
}
#Override
public void onClick(View v) {
int position=(Integer) v.getTag();
Object object= getItem(tagCollection[position]);
EM_event_total_UserSeatsModel dataModel=(EM_event_total_UserSeatsModel)
object;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// Get the data item for this position
EM_event_total_UserSeatsModel dataModel = getItem(position);
// Check if an existing view is being reused, otherwise inflate the view
ViewHolder viewHolder; // view lookup cache stored in tag
final View result;
if (convertView == null) {
viewHolder = new ViewHolder();
LayoutInflater inflater = LayoutInflater.from(getContext());
convertView = inflater.inflate(R.layout.list_em_get_seats, parent, false);
viewHolder.TvEmUserSeats = (TextView) convertView.findViewById(R.id.TvEmUserSeats);
viewHolder.IvUsreSeats = (ImageView) convertView.findViewById(R.id.IvUsreSeats);
result=convertView;
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
result=convertView;
}
String blue_available = "seat3.png";
String red_booked = "seat1.png";
String get_seat = dataModel.getBookedStatus();
viewHolder.TvEmUserSeats.setText(dataModel.getSeatName());
//holder.imgSeatSelected.setVisibility(isSelected(position) ? View.VISIBLE : View.INVISIBLE);
if(Integer.parseInt(get_seat) == 1){
Picasso.with(mContext).load(main_url+"assets/admin/img/" + red_booked).into(viewHolder.IvUsreSeats);
}else
{
Picasso.with(mContext).load(main_url+"assets/admin/img/" + blue_available).into(viewHolder.IvUsreSeats);
}
return convertView;
}
public void toggleSelection(int position) {
if (selectedItems.get(position, false)) {
selectedItems.delete(position);
} else {
selectedItems.put(position, true);
}
notifyDataSetChanged();
}
public void clearSelections() {
selectedItems.clear();
notifyDataSetChanged();
}
public int getSelectedItemCount() {
return selectedItems.size();
}
public String getSelectedItems() {
String items = "";
for (int i = 0; i < selectedItems.size(); i++) {
items += selectedItems.keyAt(i);
}
return items;
}
}

Android listview row changing values

I have a ListView that contains 3 types of elements. Rows and QueueRows that are a bit complicated sets of subcontrols, and HeaderRow containing only textView.
What I do is adding HeaderRow, few QueueRows, headerRow and few Rows.
QueueRow needs to be updated every second. That is why I wrote a code that calls notifyDataSetChanged on adapter. It updates queuerow every second, but there is also a problem - My two headers switch places for half a second.
After every 0,5s they swap. Do you have any ideas how to prevent them?
That is my code:
class BuildingsAdapter extends ArrayAdapter<BaseRow> {
private static Bitmap SOURCE_BITMAP;
private MainActivity mainActivity_;
private Handler handler_;
private Runnable runnable_;
public BuildingsAdapter(MainActivity mainActivity) {
super(mainActivity, R.layout.row);
this.mainActivity_ = mainActivity;
SOURCE_BITMAP = BitmapFactory.decodeResource(
mainActivity_.getResources(), R.drawable.images);
handler_ = new Handler();
runnable_ = new Runnable() {
#Override
public void run() {
notifyDataSetChanged();
if (hasQueueItems())
handler_.postDelayed(runnable_, 1000);
}
};
}
#Override
public void add(BaseRow object) {
if (!hasQueueItems() && object.getClass().equals(QueueRow.class))
handler_.postDelayed(runnable_, 1000);
super.add(object);
}
private boolean hasQueueItems() {
for (int i = 0; i < getCount(); ++i) {
if (getItem(i).getClass().equals(QueueRow.class))
return true;
}
return false;
}
#Override
public int getItemViewType(int position) {
return getItem(position).getType();
}
#Override
public int getViewTypeCount() {
return 3;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
int type = getItemViewType(position);
View row = convertView;
switch (type) {
case BaseRow.QUEUE_ROW: {
QueueRowViewHolder holder = new QueueRowViewHolder();
//whatever
return convertView;
}
case BaseRow.ROW: {
RowViewHolder holder = new RowViewHolder();
//whatever
return convertView;
}
case BaseRow.HEADER_ROW: {
HeaderViewHolder holder = new HeaderViewHolder();
if (row == null) {
LayoutInflater inflater = (LayoutInflater) getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
row = inflater.inflate(R.layout.header_row, parent, false);
HeaderRow headerRow = (HeaderRow) getItem(position);
holder.nameTextView_ = (TextView) row
.findViewById(R.id.categoryNameTextView);
holder.nameTextView_.setText(headerRow.getName());
row.setTag(holder);
} else {
holder = (HeaderViewHolder) row.getTag();
}
return row;
}
}
return null;
}
static class QueueRowViewHolder {
ImageView qItemImageView_;
TextView qItemNameTextView_;
ProgressBar qProgressBar_;
TextView qLevelTextView_;
TextView qTimeTextView_;
int position_;
}
static class RowViewHolder {
ImageView itemImageView_;
TextView nameTextView_;
TextView levelTextView_;
TextView woodTextView_;
TextView ironTextView_;
TextView stoneTextView_;
TextView goldTextView_;
Button actionButton_;
TextView timeTextView_;
}
static class HeaderViewHolder {
TextView nameTextView_;
}
Ok! I've found it out - I didn't set the values of HeaderRow ;)

How can i inflate three layouts in ListView?

I have a custom listView in which i want to inflate three different layouts.
I have seen many questions but they are all for odd and even position but in my listView the layout that should be inflated to the listView depends on other condition and its dynamic like
if (i == 0) i get the first Layout to be inflated and if (i==1) second one and so on,
The variable "i" is equal to the value I would be getting from my main Activity.
public class SocialListAdapter extends ArrayAdapter<Item> {
private Activity activity;
private List<Item> items;
private Item objBean;
private int row;
private int i;
public SocialListAdapter(Activity act, int resource, List<Item> arrayList) {
super(act, resource, arrayList);
// TODO Auto-generated constructor stub
this.activity= act;
this.items = arrayList;
this.row = resource;
}
#Override
public int getViewTypeCount() {
return 2;
}
#Override
public int getItemViewType(int position) {
return position;
}
#Override
public int getCount() {
return items.size();
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
View view = convertView;
ViewHolder holder;
objBean = items.get(position);
i = objBean.getI();
if (view == null) {
LayoutInflater inflater = (LayoutInflater) activity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if(i == 0){
view = inflater.inflate(R.layout.list, null);
} else if (i == 1){
view = inflater.inflate(R.layout.row1, null);
}
holder = new ViewHolder();
view.setTag(holder);
} else {
holder = (ViewHolder) view.getTag();
}
I tried this code but dint work...
I was getting an error when i scroll through the listView arrayoutofbondexception
getItemViewType has to return a number from 0 to getViewTypeCount.
Try this :
public class CustomAdapter extends BaseAdapter {
private ArrayList<String> comments;
Context mContext;
public CustomAdapter(Context context, ArrayList<String> comments) {
this.mContext = context;
this.comments = comments;
}
public View getView(final int position, View convertView, ViewGroup parent){
String item = comments.get(position);
if (getItemViewType(position) == 0) {
View v = convertView;
if (v == null) {
//GET View 1
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
ViewGroup viewGroup = (ViewGroup)inflater.inflate(R.layout.item_comment2, null);
v = viewGroup;
}
//Fill Data for Ist view
TextView comm = (TextView) v.findViewById(R.id.comment);
comm.setText(item);
return v;
} else if (getItemViewType(position) == 1) {
View v = convertView;
if (v == null) {
//GET View 2
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
ViewGroup viewGroup = (ViewGroup)inflater.inflate(R.layout.item_comment1, null);
v = viewGroup;
}
//Fill Data for IInd view
TextView comm = (TextView) v.findViewById(R.id.comment);
comm.setText(item);
return v;
} else {
//GET View 3
View v = convertView;
if (v == null) {
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
ViewGroup viewGroup = (ViewGroup)inflater.inflate(R.layout.item_comment3, null);
v = viewGroup;
}
//Fill Data for IInd view
TextView comm = (TextView) v.findViewById(R.id.comment);
comm.setText(item);
return v;
}
}
#Override
public int getCount() {
return comments.size();
}
#Override
public Object getItem(int position) {
return comments.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public int getViewTypeCount() {
return 2;
}
#Override
public int getItemViewType(int position) {
if(position == 0)
return 0;
else if (position == 1)
return 1;
else
return 2;
}
}
You can optimize and make your scrolling smooth by defining view holder :
Try this also
in getItemViewType you can set your condition where do you want to show which type of view.
use "if(position==0)" inside getView method
You can check the condition and inflate the layout that you want.
Here is the example. I have three category depending on it I inflate different layouts.
public class ApplicationAdapter extends BaseAdapter
{
private Context cntx;
/**
* 0 - featured 1- Top rated 2- other
*/
private int whichCategory;
/**
* Constructor
*
* #param context
* #param iwhichCategory
* 0 - featured 1- Top rated 2- other
*/
public ApplicationAdapter(Context context, int iwhichCategory)
{
cntx = context;
whichCategory = iwhichCategory;
}
#Override
public int getCount()
{
return entApplications.size();
}
#Override
public Object getItem(int position)
{
return null;
}
#Override
public long getItemId(int position)
{
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
if (convertView == null)
{
// This a new view we inflate the new layout
LayoutInflater inflater = (LayoutInflater) cntx.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (whichCategory == 0)
convertView = inflater.inflate(R.layout.featured_app_grid_item, parent, false);
if (whichCategory == 1)
convertView = inflater.inflate(R.layout.other_app_grid_item, parent, false);
if (whichCategory == 2)
convertView = inflater.inflate(R.layout.other_app_grid_item1, parent, false);
}
}
}

How to add a dynamic view to a ListView item at runtime?

My problem is that I don't know whether I should use multiple list view or a custom listview item adapter which can grows dynamically. For example, for a particular user, they can have multiple activities:
- Take a picture
- Say something
- Checking in
- ...
Apparently, this list can grows as the user has done more activities. Most of the time, I often create a custom item adapter which extends from BaseAdapter and use the ItemHolder pattern as follows:
public class PlaceItemAdapter extends BaseAdapter {
private Activity context;
private List<Place> places;
private boolean notifyChanged = false;
public PlaceItemAdapter(Activity context, List<Place> places) {
super();
this.context = context;
this.places = places;
}
public int getCount() {
return places.size();
}
public Object getItem(int position) {
return places.get(position);
}
public long getItemId(int position) {
return position;
}
public static class ItemViewHolder {
TextView nameTextView;
TextView typesTextView;
TextView ratingTextView;
ImageView mapIconImageView;
}
public View getView(int position, View convertView, ViewGroup parent) {
ItemViewHolder holder;
LayoutInflater inflater = context.getLayoutInflater();
if (convertView == null) {
convertView = inflater.inflate(R.layout.place_item, null);
holder = new ItemViewHolder();
holder.nameTextView = (TextView) convertView.findViewById(R.id.place_item_xml_textview_name);
holder.typesTextView = (TextView) convertView.findViewById(R.id.place_item_xml_textview_address);
holder.ratingTextView = (TextView) convertView.findViewById(R.id.place_item_xml_textview_rating);
holder.mapIconImageView = (ImageView) convertView.findViewById(R.id.place_item_xml_imageview_location_icon);
convertView.setTag(holder);
}
else {
holder = (ItemViewHolder) convertView.getTag();
}
holder.nameTextView.setText(places.get(position).getName());
holder.typesTextView.setText(places.get(position).getAddress());
holder.ratingTextView.setText(Integer.toString(places.get(position).getRating()));
/*
* This task is time consuming!
* TODO: find a workaround to handle the image
*/
// holder.mapIconImageView.setImageBitmap(DownloadImageHelper.downloadImage(places.get(position).getIconUrl()));
holder.mapIconImageView.setImageResource(R.drawable.adium);
return convertView;
}
public void notifyDataSetChanged() {
super.notifyDataSetChanged();
notifyChanged = true;
}
}
Using this method, the number GUI widgets is fixed which means I can't make my listview item look like the picture below.
public static class ItemViewHolder {
TextView nameTextView;
TextView typesTextView;
TextView ratingTextView;
ImageView mapIconImageView;
}
My initial approach was to create a dynamic view nested inside an adapter item, however it will produce duplicate views. To avoid duplicate view, I have set convertView to null which means each time it loads, it will create a new ItemViewHolder which eventually eats up all my memory. :( So how could I handle this situation? A minimal working example would be greatly appreciated.
Duplicate View
public class FriendFeedItemAdapter extends BaseAdapter {
private List<FriendFeedItem> items;
private Activity context;
private static LayoutInflater inflater;
public ImageLoader imageLoader;
private ItemViewHolder viewHolder;
public FriendFeedItemAdapter(Activity context, List<FriendFeedItem> items) {
this.context = context;
this.items = items;
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
imageLoader = new ImageLoader(context.getApplicationContext());
}
public int getCount() {
return items.size();
}
public Object getItem(int position) {
return items.get(position);
}
public long getItemId(int position) {
return position;
}
public static class ItemViewHolder {
TableLayout table;
ImageView imageViewUserPicture;
TextView textViewUsername;
TextView textViewWhatUserDo;
TextView textViewWhere;
TextView textViewTime;
ImageView imageViewSnapPictureBox;
TextView textViewWriteOnWallMessageBox;
}
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = inflater.inflate(R.layout.friend_list_feed_item, null);
viewHolder = new ItemViewHolder();
viewHolder.table = (TableLayout) convertView.findViewById(R.id.friend_list_feed_item_xml_tablelayout_table);
viewHolder.imageViewUserPicture = (ImageView) convertView.findViewById(R.id.friend_list_feed_item_xml_imageview_user_picture);
viewHolder.textViewUsername = (TextView) convertView.findViewById(R.id.friend_list_feed_item_xml_textview_username);
viewHolder.textViewWhatUserDo = (TextView) convertView.findViewById(R.id.friend_list_feed_item_xml_textview_what_user_do);
viewHolder.textViewWhere = (TextView) convertView.findViewById(R.id.friend_list_feed_item_xml_textview_where);
viewHolder.textViewTime = (TextView) convertView.findViewById(R.id.friend_list_feed_item_xml_textview_at_what_time);
convertView.setTag(viewHolder);
}
else {
viewHolder = (ItemViewHolder) convertView.getTag();
}
imageLoader.displayImage(items.get(position).getFriendPictureUrl(), viewHolder.imageViewUserPicture);
viewHolder.textViewUsername.setText(items.get(position).getFriendName());
viewHolder.textViewWhere.setText("at " + items.get(position).getPlaceName());
viewHolder.textViewTime.setText("#" + items.get(position).getActivityTime());
if (items.get(position).getChallengeType() == Challenge.Type.CHECK_IN) {
viewHolder.textViewWhatUserDo.setText("has checked in.");
}
else if (items.get(position).getChallengeType() == Challenge.Type.SNAP_PICTURE) {
viewHolder.textViewWhatUserDo.setText("has snap a picture.");
// add picture box
View rowView = inflater.inflate(R.layout.snap_picture_row_item, null);
viewHolder.imageViewSnapPictureBox = (ImageView) rowView.findViewById(R.id.snap_picture_row_item_xml_imageview_picture);
imageLoader.displayImage(items.get(position).getActivitySnapPictureUrl(), viewHolder.imageViewSnapPictureBox);
viewHolder.table.addView(rowView);
}
else if (items.get(position).getChallengeType() == Challenge.Type.WRITE_ON_WALL) {
viewHolder.textViewWhatUserDo.setText("has written a message on wall.");
// add message box
View rowView = inflater.inflate(R.layout.write_on_wall_row_item, null);
viewHolder.textViewWriteOnWallMessageBox = (TextView) rowView.findViewById(R.id.write_on_wall_row_item_xml_textview_wall_message);
viewHolder.textViewWriteOnWallMessageBox.setText(items.get(position).getActivityComment());
viewHolder.table.addView(rowView);
}
else if (items.get(position).getChallengeType() == Challenge.Type.QUESTION_ANSWER) {
viewHolder.textViewWhatUserDo.setText("has answered a question.");
}
else { // Challenge.Type.OTHER
viewHolder.textViewWhatUserDo.setText("has done some other challenges.");
}
return convertView;
}
}
Extensive Memory Usage
public View getView(int position, View convertView, ViewGroup parent) {
ItemViewHolder holder = null;
LayoutInflater inflater = context.getLayoutInflater();
convertView = inflater.inflate(R.layout.friend_list_feed_item, null);
// create holder
holder = new ItemViewHolder();
// default field
holder.table = (TableLayout) convertView.findViewById(R.id.friend_list_feed_item_xml_tablelayout_table);
holder.imageViewUserPicture = (ImageView) convertView.findViewById(R.id.friend_list_feed_item_xml_imageview_user_picture);
holder.textViewUsername = (TextView) convertView.findViewById(R.id.friend_list_feed_item_xml_textview_username);
holder.textViewWhatUserDo = (TextView) convertView.findViewById(R.id.friend_list_feed_item_xml_textview_what_user_do);
holder.textViewWhere = (TextView) convertView.findViewById(R.id.friend_list_feed_item_xml_textview_where);
holder.textViewTime = (TextView) convertView.findViewById(R.id.friend_list_feed_item_xml_textview_at_what_time);
convertView.setTag(holder);
holder.imageViewUserPicture.setImageURI(items.get(position).getFriendPictureUri());
holder.textViewUsername.setText(items.get(position).getFriendName());
holder.textViewWhere.setText("at " + items.get(position).getPlaceName());
holder.textViewTime.setText("#" + items.get(position).getActivityTime());
if (items.get(position).getChallengeType() == Challenge.Type.CHECK_IN) {
holder.textViewWhatUserDo.setText("has checked in.");
}
else if (items.get(position).getChallengeType() == Challenge.Type.SNAP_PICTURE) {
holder.textViewWhatUserDo.setText("has snap a picture.");
// add picture box
View rowView = inflater.inflate(R.layout.snap_picture_row_item, null);
holder.imageViewSnapPictureBox = (ImageView) rowView.findViewById(R.id.snap_picture_row_item_xml_imageview_picture);
holder.imageViewSnapPictureBox.setImageURI(items.get(position).getActivitySnapPictureUri());
holder.table.addView(rowView);
}
else if (items.get(position).getChallengeType() == Challenge.Type.WRITE_ON_WALL) {
holder.textViewWhatUserDo.setText("has written a message on wall.");
// add message box
View rowView = inflater.inflate(R.layout.write_on_wall_row_item, null);
holder.textViewWriteOnWallMessageBox = (TextView) rowView.findViewById(R.id.write_on_wall_row_item_xml_textview_wall_message);
holder.textViewWriteOnWallMessageBox.setText(items.get(position).getActivityComment());
holder.table.addView(rowView);
}
else if (items.get(position).getChallengeType() == Challenge.Type.QUESTION_ANSWER) {
holder.textViewWhatUserDo.setText("has answered a question.");
}
else { // Challenge.Type.OTHER
holder.textViewWhatUserDo.setText("has done some other challenges.");
}
return convertView;
}
If you have small number of possible variants (on your screenshots I can see 2 different list items) You have two possible variants:
Setup count of different types by this method, and provide type for every item - and you can use convertView.
Create "full" list item view and set visibility for elements, that you don't want to see in particular item.
Some code for #2:
public class ListTestActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
List<Element> list = new ArrayList<Element>();
list.add(new Element(0));
list.add(new Element(0));
list.add(new Element(1));
list.add(new Element(0));
list.add(new Element(1));
list.add(new Element(1));
list.add(new Element(0));
list.add(new Element(0));
list.add(new Element(1));
list.add(new Element(1));
list.add(new Element(1));
list.add(new Element(0));
list.add(new Element(0));
list.add(new Element(1));
list.add(new Element(0));
list.add(new Element(0));
((ListView) findViewById(android.R.id.list)).setAdapter(new SampleAdapter(this, list));
}
private class SampleAdapter extends BaseAdapter {
private List<Element> list;
private Context context;
public SampleAdapter(Context context, List<Element> list) {
this.list = list;
this.context = context;
}
#Override
public int getCount() {
return list.size();
}
#Override
public Element getItem(int position) {
return list.get(position);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null)
switch (getItemViewType(position)) {
case 0:
convertView = new CheckBox(context);
break;
default:
convertView = new Button(context);
break;
}
// Output here shows that you can lay on getItemViewType(position) as indicator of convertView type or structure
Log.e("test", getItemViewType(position) + ": " + convertView.getClass().getSimpleName());
return convertView;
}
#Override
public int getItemViewType(int position) {
return getItem(position).type;
}
#Override
public int getViewTypeCount() {
return 2;
}
#Override
public long getItemId(int position) {
return position;
}
}
private class Element {
public int type;
public Element(int type) {
this.type = type;
}
}
}
A custom adapter would solve your problem. This is because you can change the views that are being added to each row in the Listview, because you can change the content via logic that you implement in the custom adapter.
When the getView() method returns a view that is not null, this means for that particular row there is a view that was already there. As such if this is the case, you may or may not want to change content in that specific view. Or you could build a brand new view with dynamic content for that particular row.
One thing to note is that getView() will be called as many times as there are items found in your adapter.
Here's an idea that'll probably enable you to introduce as many item types as you like without having to modify adapter every time you do:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
AbstractItem abstractItem = ((AbstractItem)getItem(position));
// if null or new item type is different than the one already there
if (convertView == null || (convertView.getTag() != null
&& ((AbstractItem)convertView.getTag()).getType().equals(abstractItem.getType())) {
convertView = abstractItem.inflateSelf(getContext());
}
abstractItem.fillViewWithData(convertView);
convertView.setTag(abstractItem);
return convertView;
}
public class AbstractItem {
public abstract View inflateSelf(Context context);
public abstract String getType();
public abstract void fillViewWithData(View view);
}
public class PictureSnapItem extends AbstractItem {
// data fields
WeakReference<Bitmap> wBitmap;
String pictureComment;
...
public abstract View inflateSelf(Context context) {
// get layout inflater, inflate a layout resource, return
return ((Activity)context).getLayoutInflater.inflate(R.layout.picture_snap_item);
}
public abstract String getType() {
// return something class-specific, like this
return getClass().getCanonicalName();
}
public abstract void fillViewWithData(View view) {
// fill the view with data from fields, assuming view has been
// inflated by this very class's inflateSelf(), maybe throw exception if
// required views can't be found
ImageView img = (ImageView) view.findViewById(R.id.picture);
TextView comment = (TextView) view.findViewById(R.id.picture_comment)
}
}
... and then extend AbstractItem and add instances to adapter, no need to add any if clauses to getView() any more.

Categories

Resources