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.
Related
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;
}
I'm developing an app, using expandableListView, whith a custom adapter. the problem is getChildView is called many times per item. for example for a group with one child, the method is called 9 times.
I'm not sure what code to attach, I'll attach getChildView(), write in the comments if you need another one
#Override
public View getChildView(int groupPosition, int childPosition, boolean b, View convertView, ViewGroup viewGroup) {
Vehicle vehicle = (Vehicle) getChild(groupPosition, childPosition);
if (convertView == null) {
LayoutInflater infalInflater = (LayoutInflater) fragment.getActivity()
.getSystemService(fragment.getActivity().LAYOUT_INFLATER_SERVICE);
convertView = infalInflater.inflate(R.layout.child_item, null);
}
TextView tv = (TextView) convertView.findViewById(R.id.country_name);
ImageView iv = (ImageView) convertView.findViewById(R.id.flag);
tv.setText(vehicle.getUname().toString());
//TODO load image
return convertView;
}
any help will be aprreciated
Is getChilderCount(int groupPosition) overriden properly?
Should probably be something along the lines of return getGroup(groupPosition).size() depending on the type of data you're storing.
After looking for some answers here, I find myself in a disturbing situation where my Listview is really getting on my nerve.
Here are the questions I looked for :
Maintain ListView Item State
How to save state of CheckBox while scrolling in ListView?
I'm using a custom adapter with a custom row as below.
My Listview is simple as it is displaying a custom row made of three elements :
1) an ImageView displaying contact picture cropped in a circle ;
2) a TextViewdisplaying the contact full name as plain text ;
3) and finally an ImageView that holds the purpouse of a CheckBox.
Please focus on the last element. The ImageView CheckBox-like will have its src changed upon click.
When the user click, the ImageView will switch between a check sign and an unchecked sign according to it's previous status. Possible status are : {checked | unchecked}
So far so good.
But as soon as I scroll the ListView, any aforementioned change will disappear as Android recycle unused view.
Here comes the so-called ViewHolder pattern. Unfortunately, this pattern is failling me on two issues :
First, when scrolling, my organized-in-an-alphabetical-order listview gets disorganized.
e.g. somehow, whitout any reason, the first displayed contact name gets displayed again later on the ListView as I scrolled. That can happen with any row ! So it would seem unused view are being wrongly re-used.
Second, and in accordance to the first issue, the checked status do seem to stay, but not always and if it does stay, it may very well stay on the wrong row ... and that can happen randomly, of course. Therefore ViewHoder is not a viable solution.
Before discouvering the ViewHolder pattern, I have been using a HashMap to store the item position upon click as followed :
ContactsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public final void onItemClick(final AdapterView<?> adapterView, final View view,
final int position, final long id) {
final ImageView check = (ImageView) view.findViewById(R.id.checkImage);
final TextView name = (TextView) view.findViewById(R.id.contactName);
final Boolean isChecked = Boolean.valueOf(checkedContactsList.isChecked(position));
if (isChecked != null && isChecked.booleanValue() == true) {
check.setImageDrawable(getActivity().getResources().getDrawable(R.drawable.unchecked_sign));
checkedContactsList.(position);
} else {
check.setImageDrawable(getActivity().getResources().getDrawable(R.drawable.checked_sign));
checkedContactsList.add(position, true);
}
}
});
I tried adding a different value instead of position.
I tried with ContactsListView.getPositionForView(view)
And I also tried with the View's ID, but still it doesn't work.
I wish I could use ContactsListView.getSelectedItemPosition() but it returns -1 as there is no selection event because I'm handling a touch/click event.
And this is how my Custom Adapter looks like :
public final View getView(final int position,
final View convertView, final ViewGroup parent) {
final LayoutInflater inflater = (LayoutInflater) this.context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
final View contactRowView = inflater.inflate(R.layout.contact_row, parent, false);
final ImageView contactPic = (ImageView) contactRowView.findViewById(R.id.contactPic);
final TextView contactName = (TextView) contactRowView.findViewById(R.id.contactName);
final ImageView checkImage = (ImageView) contactRowView.findViewById(R.id.checkImage);
// the list is the same as above and therefore contains the exact same entries
if (this.checkedContactsList.isChecked(position))
checkImage.setImageDrawable(this.context.getResources().getDrawable(R.drawable.checked_sign));
contactPic.setImageBitmap(cropePictureInCircle(this.contacts.get(position).getPicture()));
contactName.setText(this.contacts.get(position).getName());
return contactRowView;
}
Is there a good way to keep the checked row checked and the unchecked row unchecked in the given alphabetical order ?
Thanks !
For the list position change I know the solution but for the second problem I am still searching for a solution, anyway first make a viewHolder class;
public class ViewHolder{
//put all of your textviews and image views and
//all views here like this
TextView contactName;
ImageView checkImage;
ImageView contactImage;
}
Then edit your adapter:
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
final View contactRowView = convertView;
ViewHolder holder;
if (contactRowView == null) {
final LayoutInflater inflater = (LayoutInflater)
this.context.getSystemService(Context.LAYOUT_INFLATER_SERVICE
);
contactRowView =
inflater.inflate(R.layout.contact_row, parent,
false);
holder = new ViewHolder():
holder.contactPic = (ImageView)
contactRowView.findViewById(R.id.contactPic);
holder.contactName = (TextView)
contactRowView.findViewById(R.id.contactName);
holder.checkImage = (ImageView)
contactRowView.findViewById(R.id.checkImage);
contactRowView.setTag(holder);
} else {
holder = contactRowView.getTag();
// the list is the same as above and therefore contains the exact same entries
if (this.checkedContactsList.isChecked(position))
holder.checkImage.setImageDrawable(this.context.get.
Resources().getDrawable(R.drawable.checked_sign));
holder.contactPic.setImageBitmap(cropePictureInCircle(this.contacts.get(position).getPicture()));
holder.contactName.setText(this.contacts.get(position).getName());
return contactRowView;
}
}
Hope this helps and sorry because writing code from my phone is totally awkward.
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();
}
}
I have an ExpandableListView where each parentView has an EditText (and a textview) in it. When i added the EditText to the Layout it would no longer let me click or expand the ExpandableListView, all focus just goes to the EditText. Is there a way to make both the EditText and the ExpandableListView itself be clickable? I want to be able to click the expandableListView to expand the list, and the EditText to be able to enter text.
Thanks
EDIT: added code of my group view
public View getGroupView(int groupPosition, boolean isExpanded,
View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) myContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.group_row, null);
}
TextView playerName = (TextView) convertView.findViewById(R.id.playerName);
playerName.setText(groupElements[groupPosition]);
final EditText holeScore = (EditText) convertView.findViewById(R.id.score);
holeScore.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(scoreCard.this, "On CLick", Toast.LENGTH_SHORT).show();
holeScore.setFocusable(true);
}
});
return convertView;
}
The problem is because of EditText's default focusablitiy. Since they are capable of getting focused by default this will make your parent view to be unfocused. So what you have to do is remove the focus capability of your EditText by using,
andorid:focusable="false".
And similarly if you are having any button in your parent, setting focusable false to it also will help. This is the only way you can make your parent view to get clicked.