Friends,
I am having a problem with Expandable List - for the first time the list groups are drawn correctly, (respective textViews texts get replaced, e.g. 3 of 9) but when any of groups are clicked the textViews of another group (random) are replaced instead of beeing untouched mm.
What should be done to prevent this?
Here is the adapter class:
public class InterestListAdapter extends BaseExpandableListAdapter
{
...
#Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent)
{
if (convertView == null)
{
convertView = LayoutInflater.from(_context).inflate(R.layout.activity_interest_list_group, parent, false);
}
try
{
HashMap<String, String> interest = this._listDataHeader.get(String.valueOf(groupPosition));
String interestId = interest.get("interest_id");
String interestName = interest.get("interest_name");
JSONObject interestsTree = new JSONObject(library.loadString(_context, "userInterestsTree"));
TextView listGroupName = (TextView) convertView.findViewById(R.id.interest_list_group_name);
listGroupName.setText(interestName);
if (this._listDataChild.containsKey(String.valueOf(interestId)))
{
String subinterestAll = String.valueOf(this._listDataChild.get(String.valueOf(interestId)).size());
if (interestsTree.has(interestId))
{
String subinterestChecked = interestsTree.getString(interestId);
TextView subinterestText = (TextView) convertView.findViewById(R.id.interest_list_group_selected);
subinterestText.setText(subinterestChecked + " of " + subinterestAll);
}
}
}
catch (Exception e)
{
Log.e("error", Log.getStackTraceString(e));
}
return convertView;
}
}
I'm rather sure, that you have problems with memory management. When you expand your list some cells become hidden from the screen. After then, when you scroll it, they have to be regenerated. Android OS, to save memory, doesn't generate them from scratch, but instead of that uses the same memory regions, that were used for hidden cells.
It looks like subinterestText is the problematic field. It's being set only if both these ifs are true:
if (this._listDataChild.containsKey(String.valueOf(interestId)))
and
if (interestsTree.has(interestId))
If one of them is false, subinterestText variable won't be set. So, as result, value will be usedfrom previously hidden field - in your case - 2 of 2.
To fix it - always setup all values of your cell in getGroupView method.
Related
I have an Adapter with rows from View, and each row contains some information. I am trying to make a Actioncall, so if someone clicks the row with the phone number, then it starts dialing. This work but the problem is I don't get the phone number from the TextView, I get a random number.
This is the code
The function for when the users clicks on the phone number
private void TelSelected(TextView obj)
{
mTelefonNumar = FindViewById<TextView>(Resource.Id.textView3);
Toast.MakeText(this, mTelefonNumar.ToString(), ToastLength.Long).Show();
string stringtelefon = mTelefonNumar.ToString();
var uri = Android.Net.Uri.Parse("tel:" + stringtelefon);
var intent = new Intent(Intent.ActionCall, uri);
StartActivity(intent);
}
The Toast returns this android.widget.TextView{2e4a768b V.ED..C...... 0, 198-698,264 #7f0b0055 app:id/textView3}
And the Intent.ActionCall, calls this number 247 680 but the number another one/
public override View GetView(int position, View convertView, ViewGroup parent)
{
View row = convertView;
if (row == null)
{
row = LayoutInflater.From(mContext).Inflate(mLayout, parent, false);
}
row.FindViewById<TextView>(Resource.Id.textView3).Text = Linfo[position].TelClient;
row.FindViewById<TextView>(Resource.Id.textView3).Click += InformatiiListAdapter_Click;
return row;
}
void InformatiiListAdapter_Click(object sender, EventArgs e)
{
//He clicked on the Telephone Number
mNrApasat.Invoke((TextView)sender);
}
.ToString() on a TextView will show information about the View, not the text it displays. The following would fix it :
((TextView)sender).Text;
instead of
((TextView)sender).ToString();
An cleaner way (because the displayed text might not always be exactly a usable phone number) would be to store the number in the row 'tag' property (in which you can store anything).
And to set the event on the row.
public override View GetView(int position, View convertView, ViewGroup parent)
{
View row = convertView;
if (row == null)
{
row = LayoutInflater.From(mContext).Inflate(mLayout, parent, false);
}
row.SetTag(Linfo[position].TelClient);
row.Click += InformatiiListAdapter_Click;
row.FindViewById<TextView>(Resource.Id.textView3).Text = (string)row.GetTag();
return row;
}
void InformatiiListAdapter_Click(object row, EventArgs e)
{
//He clicked on the Telephone Number
string phoneNumber = (string)(((View)row).GetTag());
// do stuff with the phone number
}
also, on a side note, FindViewByID is very expensive processing-wise, avoid doing it too much, you might want to look into the ViewHolder pattern, it'll make scrolling smoother
also, it would be better to set the item click listener on the list itself, instead of registering an event on each convertView.
I have an ExpandableListView and I'm using an adapter to inflate groups and children rows from layout XMLs. In my child XML I have 3 text views: name, notes and amount. What I want is:
if child's object amount > 1 display it in the appropriate text view; set notes text view's text to child's details field, but if the details string is empty - make the view disappeared.
So I had this code in my adapter:
public View getChildView(int groupPosition, int childPosition,
boolean isLastChild, View convertView, ViewGroup parent) {
final ChildHolder child_holder;
if(convertView == null)
{
convertView = LayoutInflater.from(context).inflate(R.layout.row_selected_item_packed, null);
child_holder = new ChildHolder();
child_holder.tv_si_name = (TextView) convertView.findViewById(R.id.tv_packed_item_name);
child_holder.tv_si_notes = (TextView) convertView.findViewById(R.id.tv_packed_item_notes);
child_holder.tv_si_amount = (TextView) convertView.findViewById(R.id.tv_packed_item_amount);
child_holder.img_packed = (ImageView) convertView.findViewById(R.id.img_packed);
convertView.setTag(child_holder);
} else {
child_holder = (ChildHolder) convertView.getTag();
}
final Selected_Group selected_group = arr_selected_groups.get(groupPosition);
final Selected_Item selected_item = selected_group.getArr_selected_items().get(childPosition);
if(selected_item!=null)
{
child_holder.tv_si_name.setText(selected_item.getItem_name());
//Here starts the problem
child_holder.tv_si_notes.setText(selected_item.getSelected_item_details());
if(selected_item.getSelected_item_details().equals(""))
{
child_holder.tv_si_notes.setVisibility(View.GONE);
}
if(selected_item.getSelected_item_amount()>1)
{
child_holder.tv_si_amount.setText(String.valueOf(selected_item.getSelected_item_amount()));
}
if(selected_item.isSelected_item_packed())
{
child_holder.img_packed.setImageDrawable(context.getResources().getDrawable(R.drawable.check_box_packed));
} else {
child_holder.img_packed.setImageDrawable(context.getResources().getDrawable(R.drawable.check_box_unchecked));
}
}
return convertView;
}
class ChildHolder{
TextView tv_si_name;
TextView tv_si_notes;
TextView tv_si_amount;
ImageView img_packed;
}
Currently I only have 1 child that has both amount>1 (all the rest have 1) and details. But here's what happens:
All the notes text views are gone. If I remove the if statement - they appear and the text is set in the appropriate child.
First I have the amount text exactly in the right child. However if I close the group and reopen it - suddenly another child has that same amount (and if the if statement from #1 is gone - then the details are also being duplicated to that child. If I open another group - I'll see the amount on one of its children too. So eventually if I keep opening and closing groups I'll have amount in every child's amount text view.
Why is it happening and how can it be fixed?
try like this way..
child_holder.tv_si_notes.setVisibility(View.GONE);
if(selected_item.getSelected_item_details().equals(""))
{
child_holder.tv_si_notes.setVisibility(View.GONE);
}
else
{
child_holder.tv_si_notes.setVisibility(View.VISIBLE);
}
I have a list of orders in SQLite which vary in status: assigned, loaded, delivered. I'd like for each of those orders, when displayed in the list, to have a different colored background. So far, I haven't found a good way to do this.
I've found plenty of discussions on how to change the background color of list items based on the position, but none based on data content. I've also found lots of discussions on how to change the color that's used to highlight an item that is selected. These don't help me.
The only methods I come up with for solving my problem involve running through the entire list, after it's been created by the adapter, and setting the background on each item. It's kludgy and wasteful. I'm hoping there's a more efficient method that would let the background be changed in the adapter as the list is being created from the cursor.
I'm sure there's a better way. I'm just too new to Android to know it.
I really appreciate the responses so far. I'm doing my best to incorporate them, but I'm still not having success. Here's what I've just tried, based on the answers I've gotten and the research I've done.
public class OrderListAdapter extends SimpleCursorAdapter {
private static final String TAG = "SimpleCursorAdapter";
Context _context = null;
int layoutResourceId = 0;
public OrderListAdapter(Context context, int layout, Cursor c,
String[] from, int[] to, int flags) {
super(context, layout, c, from, to, flags);
_context = context;
}
public View getView(int position, View convertView, ViewGroup parent) {
View view = super.getView(position, convertView, parent);
String tag = TAG + ".getView()";
Log.d(tag,"in getView()");
if (view == null) {
LayoutInflater inflater = ((Activity)_context).getLayoutInflater();
view = inflater.inflate(R.layout.fragment_order_list_row, null);
}
setRowColor(view);
return view;
}
private void setRowColor(View view) {
String tag = TAG + ".setRowColor()";
Cursor cursor = getCursor();
int col = cursor
.getColumnIndex(DBContract.DeliveryOrderTable.ENROUTE_FLAG);
String enroute_flag = cursor.getString(col);
Log.d(tag, "enroute_flag = [" + enroute_flag + "]");
col = cursor
.getColumnIndex(DBContract.DeliveryOrderTable.DELIVERED_DATETIME);
String deliveredDateStr = cursor.getString(col);
Log.d(tag, "deliveredDateStr = [" + deliveredDateStr + "]");
int bgColorId = 0;
if (!deliveredDateStr.equals("")) {
bgColorId = R.color.bg_status_delivered_color;
Log.d(tag, "Setting to delivered color");
} else if (enroute_flag.startsWith("T") || enroute_flag.startsWith("Y")) {
Log.d(tag, "Setting to enroute color");
bgColorId = R.color.bg_status_enroute_color;
} else {
Log.d(tag, "Setting to assigned color");
bgColorId = R.color.bg_status_assigned_color;
}
view.setBackgroundColor(_context.getResources().getColor(bgColorId));
}
}
Unfortunately, this doesn't work. If I don't make the call to super.getView(), I wind up with no data in the fields, obviously, since I don't explicitly make the transfers, but I figured I could just modify the returned view.
My traces show me that I am reading the data, but the background color is not changing.
It appears that the view I'm trying to change is the LinearLayout, but changing the background color doesn't seem to work.
Got it! Make sure to make the backgrounds of all the child views transparent.
if you are using any custom adapter for listview then, you will have a method getView(), in that just call a method before returning, and pass the view(which is returning) and data depending on you want to change the color of the row.
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
View view = convertView;
if (view == null) {
LayoutInflater vi = (LayoutInflater) _c.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = vi.inflate(R.layout.list_item_var, null);
}
TextView varView = (TextView) view.findViewById(R.id.var);
TextView valueView = (TextView) view.findViewById(R.id.value);
VarDetails var = _data.get(position);
setRowColor(view, var.getVar());
varView.setText(var.var);
valueView.setText("Value: " + var.value);
return view;
}
private void setRowColor(View view, String var) {
if("assigned".equalsIgnoreCase(var)){
view.setBackgroundColor(Color.rgb(255,0,0));
}else if("loaded".equalsIgnoreCase(var)){
view.setBackgroundColor(Color.rgb(0,255,0));
}else if("delivered".equalsIgnoreCase(var)){
view.setBackgroundColor(Color.rgb(0,0,255));
}
}
please change in method depending on you data type.
I would say try to extend CursorAdapter for binding your database with a ListView. And then you can override ListView.dispatchDraw() to customize your Paint object.
Or maybe it's helpful to check this: Customizing Android ListView Items with Custom ArrayAdapter
It uses different images based on weather status. Porting to your problem, you may use 9-patch or programmatically created Drawables as backgrounds, rather than changing stuff in Paint.
I am currently having an issue with the creation of my ExpandableListView. I'm unsure what is happening at the moment but I'll explain what I'm trying to do.
I programmatically allocate meals that fall on a certain day. Within each meal (the title is the group) is a child that contains a vote and order button. Both of which are changed to "Ordered" when a order is made on a meal. If another order is made the previous order button goes back to the 'order' state and the selected one goes to ordered. This is the same for vote. I go about this by setting an on click listener to my buttons that loop through all the other buttons and set their text to 'order'/'vote' and set the current buttons text to ordered.
This works fine in some extremely rare cases - but most of the time when I order/vote for an item it changes the selected one to 'ordered' and the last element in my expandable list view to ordered as well. Then if I order, lets say the 3rd element, close and reopen the second element it also changes to ordered and visa versa. Sometimes they all change back to order or all change to ordered. I'm finding it difficult to work out why this may be happening.
I believe it may have something to do with the getChildView function. As when I traverse through the list opening each element the view is never set for the last element even though it has been allocated the appropriate data.
Am I misunderstanding a major concept of the ExpandableListView or is there something else.
Here is the getChildView function that I believe there may be a fault. I'll also supply links to the other relevant classes below. If you need further information, please don't hesitate to ask.
//Static class view holder
static class ViewHolder {
protected Button oBut;
protected Button vBut;
protected TextView description;
}
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
//Getting the appropriate child for the group and position
ExpandListChild child = (ExpandListChild) getChild(groupPosition, childPosition);
View view = null;
//Set up if doesn't exist
if (convertView == null) {
Log.d("OnClickListener", "groupPosition: " + groupPosition + " childPosition: " + childPosition);
Log.d("OnClickListener", "View being set up for: " + child.getName() + " desc: " + child.getDescription());
LayoutInflater infalInflater = (LayoutInflater) context.getSystemService(context.LAYOUT_INFLATER_SERVICE);
view = infalInflater.inflate(R.layout.expandlist_child_item_todays, null);
//Attempt at viewholder
final ViewHolder holder = new ViewHolder();
holder.description = (TextView) view.findViewById(R.id.mealDescription);
holder.vBut = (Button)view.findViewById(R.id.vote);
holder.oBut = (Button)view.findViewById(R.id.order);
view.setTag(holder);
}
else {
view = convertView;
}
ViewHolder holder = (ViewHolder) view.getTag();
holder.description.setText(child.getDescription());
holder.description.setTag(child.getTag());
holder.oBut.setOnClickListener(new OrderOnClick(groups, (Activity)context, holder.oBut, view, child));
holder.vBut.setOnClickListener(new VoteOnClick(groups, (Activity)context, holder.vBut, view, child));
return view;
}
ExpandListAdapterTodays (extends and extends off my BaseExpandableListAdapter)
ExpandListAdapter (extends the BaseExpandableListAdapter)
VoteOnClick (Class that implements the changing of button text when a successfull vote has been placed)
the way that listviews on android work to be efficient is that they implement a "view recycling" method where if some view goes off screen, that same view is put back somewhere on the screen with all the necessary bits changed. That keeps things using much less resources, actually reusing the same resources, but wreaks havoc if you need states persisting in a specific order like you do. What you should do is to implement some sort of map or arraylist of which the position on the object corresponds to its position of the listview and then make changes through the made object. A little lame but its kinda like an adapter method for your adapter.
Now i apologize because i can't exactly visualize how the onlick method is supposed to work, but looking through it.. something like:
for (int i = 0; i < groups.size(); i++) {
....
but.setText("Vote");
would become
ArrayList<String> group_string_states = new ArrayList<String> ();
private void fillGroupStringStates () {
for(int i = 0; i < groups.length; i++) {
group_string_states.add("Order");
}
}
....
for (int i = 0; i < group_string_states.size(); i++) {
....
group_string_states.set(i, "Vote");
Then you do a conditional, like
if group_string_states.get[position] says "vote", then do this.
Well, that's how i'd attempt to do it. i hope it helped and i'm really sorry if it didn't.
Tried using the following:
Populate Listview from JSON
To make a listview which uses a JsonArray containing Json Objects. For some reason, the
'public View getView(int position, View convertView, ViewGroup parent)'
code is fired more times than there are contents in the jsonarray.
I made a control test to check up on this and I found that even with just 1 Jsonobject within the jsonarray, I came up with 32 times the getView code was activated.
I am rather confused as to why this is happening, as my friends have managed to make similar codes to mine, but without the huge number of activations I am suffering from. Am I being rather slow, and this is because the individual Jsonobject has, not only the image and text in them, but about 15 other items within it? Or is ther another cause?
I would appreciate any aid towards this, I am posting the adapter code below:
public class ArticleAdapter extends BaseAdapter{
private JSONArray items;
private Context cont;
public ArticleAdapter(Context context, JSONArray array)
{
super();
this.items = array;
this.cont = context;
}
#Override
public int getCount() {
return items.length();
}
#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)
{
View v = convertView;
WebIView sath;
TextView sati;
Log.i("Seiji", "Checking! " + position);
try
{
if(!items.isNull(position))
{
JSONObject item = items.getJSONObject(position);
if (v == null) {
v = LayoutInflater.from(cont).inflate(R.layout.saved_articles_listitem, null);
}
sath = (WebIView) v.findViewById(R.id.sathumbnail);
sati = (TextView) v.findViewById(R.id.satitle);
if(item.has("image") && sath != null)
{
JSONObject thisImage = item.getJSONObject("image");
sath.reset();
sath.setImageUrl(thisImage.getString("thumbnail"));
sath.loadImage();
}
if(sati != null)
{
sati.setText(item.getString("title"));
}
}else{
return null;
}
}
catch(Exception e)
{
Log.e("num", "Saved Art Error! " + e.toString());
}
return v;
}
}
the code which activates this class is the following:
ListView savedArtList = (ListView) sav.findViewById(R.id.savelist);
ArticleAdapter savedadapter = new ArticleAdapter(cont, flip);
ArtList.setAdapter(savedadapter);
EDIT:
Thanks to some very helpful advice I was able to figure out what was going wrong. The Listview was resizing itself every time a new row was added because I had set the views height to be 'wrap_content'. I hadnt realised that this would cause problems, but once I had set it to 'fill_parent' (or a set value in other cases), the issue disappeared and I didnt have this problem any more.
Thank you againfor the helpful advice!
getView will be called many times - per visible cell when the list view is being laid out, per visible cell when the list view is being drawn + more. This is normal behaviour and getView should be efficient. Its possible your images and/or text are making the height of each cell change as they're loaded in, meaning other cells may become visible / go off screen etc.