holder in listview not retaining which flipview layout to show - android

I'm creating an expandable listview that contains a restaurant menu. When the user clicks on one of the items, he sees the details of the item (for example clicking on the row "avocado roll" shows a new layout that states "avocado, crab, rice").
Here is a screenshot of what a standard view is:
(I can't post photos because I am new but here is what the user would see if he saw the listview:
PRODUCT DESCRIPTION
PRODUCT DESCRIPTION
PRODUCT DETAIL (different format)
PRODUCT DESCRIPTION
PRODUCT DESCRIPTION
The idea is that the default features the name of the food item; and if the user clicks (or swipes, eventually - but let's keep it simple for now) he should see the description (in black).. Problem is, when I scroll the black row switches back to the original default format. (the problem will become more acute when I try to get the interface to "remember" where the user clicked and keep those buttons pressed so teh user can know what he ordered - and it clearly has to do with me not understanding how the holder works!)
I use a viewflipper inside each row and it's working great - fast and reliable. Also only a single row gets selected when I click, which is nice.
My only problem is I can't get the holder to remember the status of the viewflipper when I scroll. So it resets every time I scroll.
For example, in the steak example, if I show the detail view in the row, then scroll down and back up, the view in the row returns to the name "avocado roll". Clearly I'm not implementing the holder correctly so it remembers the viewflipper value but for the life of me I can't figure this out after 24 hours of trying!
Here is my adapter code:
ExpandMenu Adapter so you know what the lists are referring to:
public ExpandMenuAdapter(Context passedContext, ArrayList<ExpandOptionsGroup> passedGroupList){
this.context = passedContext;
this.groupList = passedGroupList;
}
getChildView code: this is where I think the holder issue is but I can't ID it!
#Override
public View getChildView(int groupPosition, int childPosition,
boolean isLastChild, View convertView, ViewGroup parent) {
View rowView = null;
String[] child = getChild(groupPosition, childPosition);
if (rowView == null){
LayoutInflater infalInflater = (LayoutInflater) context.getSystemService(context.LAYOUT_INFLATER_SERVICE);
rowView = infalInflater.inflate(R.layout.item_row, null);
final ViewHolder viewHolder = new ViewHolder();
viewHolder.itemName = (TextView) rowView.findViewById(R.id.tvItem);
viewHolder.itemPrice = (TextView) rowView.findViewById(R.id.tvPrice);
viewHolder.itemDetail = (TextView) rowView.findViewById(R.id.tvDetail);
viewHolder.itemLayout = (LinearLayout) rowView.findViewById(R.id.item_layout);
viewHolder.rowFlipper = (ViewFlipper) rowView.findViewById(R.id.item_row_flipper);
viewHolder.itemLayout.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
viewHolder.rowFlipper.showNext();
Toast.makeText(context, "You clicked", Toast.LENGTH_SHORT).show();
}
});
rowView.setTag(viewHolder);
viewHolder.rowFlipper.setTag(groupList.get(groupPosition));
}else{
rowView = convertView;
((ViewHolder) rowView.getTag()).rowFlipper.setTag(groupList.get(groupPosition));
}
ViewHolder holder = (ViewHolder) rowView.getTag();
//holder.rowFlipper.set(groupList.get(groupPosition)); CLEARLY THIS IS WRONG
holder.itemName.setText(child[MenuXMLGettersSetters.ELEMENT_NAME]);
holder.itemDetail.setText(child[MenuXMLGettersSetters.ELEMENT_DETAIL]);
if(!child[MenuXMLGettersSetters.ELEMENT_PRICE].isEmpty()){
holder.itemPrice.setText("$"+child[MenuXMLGettersSetters.ELEMENT_PRICE]);
}else{
holder.itemPrice.setText("");
}
return rowView;
}
private static class ViewHolder {
public TextView itemName;
public TextView itemPrice;
public TextView itemDetail;
public LinearLayout itemLayout;
public ViewFlipper rowFlipper;
private void getViewHolderDetail(){
itemDetail.isShown();
}
}

Related

Android ListView actions repeat in different items

I have a custom layout for a Listview, each row item contains a button that when clicked it shows a small imageview in the item, however the actions i perform in one item, repeats for another item down the list, for example, if i click the button in item 1, the imageview will show up in item 1 and item 10, if i click the button in item 2, the Imageview will show up on item 2 and item 11 and while i scroll it will keep repeating in different items, heres the code for my custom adapter:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
mparent = parent;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.places_item, parent, false);
holder = new ViewHolder();
holder.placeimage = (CircularImageView) convertView.findViewById(R.id.locationimage_pilayout);
holder.addbtn = (TextView) convertView.findViewById(R.id.addbtn_pilayout);
holder.delbtn = (TextView) convertView.findViewById(R.id.delbtn_pilayout);
holder.oribtn = (TextView) convertView.findViewById(R.id.oribtn_pilayout);
holder.placename = (TextView) convertView.findViewById(R.id.locationname_pilayout);
holder.selected = (ImageView) convertView.findViewById(R.id.selected_pilayout);
holder.origin = (ImageView) convertView.findViewById(R.id.origin_pilayout);
holder.swipeLayout = (SwipeRevealLayout) convertView.findViewById(R.id.swipe_pilayout);
holder.mainLayout = (LinearLayout) convertView.findViewById(R.id.main_pilayout);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
final Place item = getItem(position);
/*
my code assigning the button click listener and assigning the views
*/
return convertView;
}
Am i missing something? im sure this could be a simple fix, but i havent found it yet. any help would be kindly appreciated.
In ListView the individual views for rows, get reused. For example, let's say the window can show up to 10 rows inside the ListView. Now when you scroll down, the 1st view gets out of the window from the top, and a new 11th view comes into the window from the bottom. In order to save memory and CPU power, Android uses the same 1st view for the 11th view. That's the purpose of convertView and the if (convertView == null) {} else {} code.
So the reason why the image is being shown in the 1st item and also in the 11th item, is that they are exactly one view object. To tackle this issue, in the getView() method, you need to reset every attribute of every view and don't rely on the defaults.
So adding a line like the one below, will get rid of the mentioned problem:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// all your code ...
holder.placeImage.setImageResource(0); //<-- This clears any previously set image.
return convertView;
}

ExpandableListAdapter with buttons changing an TextView

I have an expandableList Adapter working pretty well, but something in baffling me. On each child lists I have 2 buttons and a textview. I would like to make it some that when I click one button (+) that the textview number goes up and when I click the other button (-) that the textview number goes down.
public View getChildView(final int groupPosition, final int childPosition,
boolean isLastChild, View convertView, ViewGroup parent) {
final String childText = (String) getChild(groupPosition, childPosition);
if (convertView == null) {
LayoutInflater infalInflater = (LayoutInflater) this._context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = infalInflater.inflate(R.layout.list_item, null);
}
final TextView txtListChild = (TextView) convertView
.findViewById(R.id.lblListItem);
Button btnMinus = (Button) convertView
.findViewById(R.id.btnMinus);
btnMinus.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
LinearLayout ll = (LinearLayout) v.getParent();
TextView tv1 = (TextView) ll.getChildAt(1);
String Count = tv1.getText().toString();
int num = Integer.parseInt(Count);
num = num - 1;
tv1.setText(Integer.toString(num));
}
So the above code works as I would expect it, the button is clicked the textview goes down by one and we all is good.
But here is where I am getting confused. If I open another part of the list and look at another list of children one of the TextViews in that list has also changed! So in short clicking a button in one child is not only affecting the text view right next to it, but is also affecting a text view in a completely different header.
I think you are unaware of the view recycling mechanism. You can read about it here:
How ListView's recycling mechanism works
Basically, when you scroll a ListView (or ExpandableListView), the view that just got off the screen on the top will be reused to create a view that needs to be shown on the bottom.

Removing a row from ListView with a button click

I have a ListView whose rows comprise of some TextViews and a button. Upon a user pressing the button, I want to remove that parent that houses the button from the ListView. How do I access my custom ArrayAdapter's fields from within a nested method (onClickListener) though? All I have to work with is the View v. Am I suppose to call v.getParent() multiple times, or is there a better way to do it?
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
Action item = this.getItem(position);
if (convertView == null) {
convertView = inflater.inflate(R.layout.action_holder_layout,
parent, false);
holder = new ViewHolder();
holder.title = (TextView) convertView
.findViewById(R.id.action_holder_title);
holder.finishBtn = (Button) convertView
.findViewById(R.id.finish_action_button);
convertView.setTag(holder);
} else
holder = (ViewHolder) convertView.getTag();
holder.title.setText(item.getActionName());
holder.finishBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//REMOVE THE ACTION FROM THE ADAPTER'S ARRAYLIST
}
});
return convertView;
}
static class ViewHolder {
private TextView title;
private Button finishBtn;
}
Make item final:
final Action item = this.getItem(position);
and you can access it from onClick. Not a beautiful solution IMO, but will work:
remove(item);
If you need access to your ListView (and consequently its Adapter), a better way than using getParent().getParent()... would be to use the setTag() method on your View. Since you are already using it to insert a ViewHolder as a Tag of your view, why not add another field
ListView parentListView;
to your ViewHolder and retrieve it later in the onClick?
You could also set the ListView directly as a tag on your button.
You could also just access the adapter or other methods and fields directly from your onClick code since you're using an anonymous class.

attractive listview with images

i want to build an application with a listview or whatever.. that looks very attractive and have some images and many more.But i cant find a good way to have that. I exactly want my application's UI like this images:
(source: coenraets.org)
(source: coenraets.org)
i want to display my app like this images please suggest me how can i do this? please if u know some tutorials then give the links.
Yes. Just place one ImageView and TextView in one xml layout. And, inflate this layout into one layout which is having the ListView And, do the process there for getting images from webservice or locally stored
Here i provide some example links that may very useful to you -
Lazy load of images in ListView
ListView with images
How to display a list of images in a ListView in Android?
You need to build the layout you want in a XML file, the same way you would do for an Activity. Then just inflate the XML layout for each row in your ListView and set its values and images.
Example of one ArrayAdapter that I've inflated with my own view (picture, text and checkbox):
private class FriendListAdapter extends ArrayAdapter<User> {
public FriendListAdapter(Activity a, int textViewResourceId, List<User> items) {
super(a, textViewResourceId, items);
}
public class ViewHolder{
public TextView username;
public ImageView image;
public CheckedTextView ctv;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
View v = convertView;
ViewHolder holder;
if (v == null) {
LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.invite_friend_row, null);
holder = new ViewHolder();
holder.username = (TextView) v.findViewById(R.id.username);
holder.username.setTypeface(tf);
holder.image = (ImageView) v.findViewById(R.id.image);
holder.ctv = (CheckedTextView) v.findViewById(R.id.checked);
v.setTag(holder);
}
else{
holder = (ViewHolder) v.getTag();
}
final User user = getItem(position);
if(user != null){
holder.username.setText(user.getName());
holder.ctv.setChecked(user.isChecked());
holder.image.setImageView(user.getImage());
}
return v;
}
}
You get the idea!

Deleting items from a ListView using a custom BaseAdapter

I am using a customised BaseAdapter to display items on a ListView. The items are just strings held in an ArrayList.
The list items have a delete button on them (big red X), and I'd like to remove the item from the ArrayList, and notify the ListView to update itself.
However, every implementation I've tried gets mysterious position numbers given to it, so for example clicking item 2's delete button will delete item 5's. It seems to be almost entirely random.
One thing to note is that elements may be repeated, but must be kept in the same order. For example, I can have "Irish" twice, as elements 3 and 7.
My code is below:
private static class ViewHolder {
TextView lang;
int position;
}
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.language_link_row, null);
holder = new ViewHolder();
holder.lang = (TextView)convertView.findViewById(R.id.language_link_text);
holder.position = position;
final ImageView deleteButton = (ImageView)
convertView.findViewById(R.id.language_link_cross_delete);
deleteButton.setOnClickListener(this);
convertView.setTag(holder);
deleteButton.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.lang.setText(mLanguages.get(position));
return convertView;
}
I later attempt to retrieve the deleted element's position by grabbing the tag, but it's always the wrong position in the list. There is no noticeable pattern to the position given here, it always seems random.
// The delete button's listener
public void onClick(View v) {
ViewHolder deleteHolder = (ViewHolder) v.getTag();
int pos = deleteHolder.position;
...
...
...
}
I would be quite happy to just delete the item from the ArrayList and have the ListView update itself, but the position I'm getting is incorrect so I can't do that.
Please note that I did, at first, have the deleteButton clickListener inside the getView method, and used 'position' to delete the value, but I had the same problem.
Any suggestions appreciated, this is really irritating me.
You have to set the position each time. Your implementation only sets the position on the creation of the view. However when the view is recycled (when convertView is not null), the position will not be set to the correct value.
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.language_link_row, null);
holder = new ViewHolder();
holder.lang = (TextView)convertView.findViewById(R.id.language_link_text);
final ImageView deleteButton = (ImageView)
convertView.findViewById(R.id.language_link_cross_delete);
deleteButton.setOnClickListener(this);
convertView.setTag(holder);
deleteButton.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.lang.setText(mLanguages.get(position));
holder.position = position;
return convertView;
}
you need to implement OnItemClickListener interface, and delete the item in the onItemClick method, one parameter of the method is the position.
My final solution was to use the accepted answer by Greg and the following:
Store the holders in a HashMap, with the item positions as the keys (this is initialised as empty in the constructor)
private HashMap mHolders;
Use this as the onClickListener method:
public void onClick(View v) {
ViewHolder deleteHolder = (ViewHolder) v.getTag();
int pos = deleteHolder.position;
mHolders.remove(pos);
ViewHolder currentHolder;
// Shift 'position' of remaining languages
// down since 'pos' was deleted
for(int i=pos+1; i<getCount(); i++){
currentHolder = mHolders.get(i);
currentHolder.position = i-1;
}
mLanguages.remove(pos);
notifyDataSetChanged();
}
[Please excuse the weird formatting. The code embedding isn't working properly]

Categories

Resources