ListView is generated from JSON with boolean getExpired.
if getExpired() is TRUE, then textColor should be changed.
but it set on list position even if == FALSE
Why 1st position is changed, if getExpired is FALSE on this item?
this is my getView
public View getView(int position, View convertView, ViewGroup parent) {
ColorMatrix matrix = new ColorMatrix();
matrix.setSaturation(0);
ColorMatrixColorFilter filter = new ColorMatrixColorFilter(matrix);
if (item.getExpired()) {
vh.imageView.setColorFilter(filter);
vh.profilePhoto.setColorFilter(filter);
vh.expired.setVisibility(View.VISIBLE);
vh.username.setTextColor(Color.parseColor("#76808D"));
}
return vh.rootView;
}
If you're using the ViewHolder pattern you will be getting recycled views which have already have their views set to a certain state. In short, when using ViewHolder also use an else condition to set your view states how you want them to be.
Related
I'm using listview custom adapter which with row click i'm changing row color. But when i'm scrolling bot and up again it doesnt have the right position.
It changes color in other rows...
public override View GetView(int position, View convertView, ViewGroup parent)
{
DataViewHolder holder = null;
if (convertView == null)
{
convertView = LayoutInflater.From(mContext).Inflate(Resource.Layout.TableItems, null, false);
holder = new DataViewHolder();
holder.txtDescription = convertView.FindViewById<TextView>(Resource.Id.txtDescription);
holder.txtDescription.Click += delegate
{
holder.txtDescription.SetBackgroundColor(Color.Red);
};
convertView.Tag = holder;
}
else
{
holder = convertView.Tag as DataViewHolder;
}
holder.txtDescription.Text = mitems[position].Description;
return convertView;
}
public class DataViewHolder : Java.Lang.Object
{
public TextView txtDescription { get; set; }
}
It looks like it doesnt keep in memory specific row situation.
Don't change the color in the click handler directly, instead change the data from which the adapter draws from and use that to change the color when GetView is called again.
ListView recycles the views it uses to optimize scrolling, instead it just expects the view to represent the data. If you change a color of one view directly, the view then gets recycled and you'll see "another view" (another part of the data) with a different background color.
So in summary: give each data point a color attribute and use that to set the color of each view in GetView, change the data and notify the adapter about the changes to the data.
Edit
I've never used Xamarin but maybe something like this would work
public override View GetView(int position, View convertView, ViewGroup parent)
{
DataViewHolder holder = null;
if (convertView == null)
{
convertView = LayoutInflater.From(mContext).Inflate(Resource.Layout.TableItems, null, false);
holder = new DataViewHolder();
holder.txtDescription = convertView.FindViewById<TextView>(Resource.Id.txtDescription);
holder.txtDescription.Click += delegate
{
// instead of setting the color directly here, just modify the data
(holder.txtDescription.Tag as ItemType).ItemColor = Color.Red
notifyDataSetChanged();
};
convertView.Tag = holder;
}
else
{
holder = convertView.Tag as DataViewHolder;
}
holder.txtDescription.Text = mitems[position].Description;
holder.txtDescription.Tag = mitems[position]; // this so that the click handler knows which item to modify
holder.txtDescription.SetBackgroundColor(mitems[position].ItemColor);
return convertView;
}
public class DataViewHolder : Java.Lang.Object
{
public TextView txtDescription { get; set; }
}
ListView will reuse the item layout, you can use List and View.Tag to avoid the problem caused by reusing.
I have posted my demo on github.
as in topic, when I use adapter.notifyDataSetChanged() text color in a cell which i have already changed is seting back to default value. I don't know why it happens im putting here me method for changing color:
for(int l=0;l<list.size();l++){
System.out. println("kolorujemy! "+ list.size() );
LinearLayout root = (LinearLayout) getViewByPosition(l,listView);
((TextView) root.findViewById(R.id.wartosc_calosci)).setTextColor(Color.YELLOW);
I would also add that this part of code is in loop in other thread because the vaules of the cells is updating every 30 seconds. Here is method getViewByPosition:
public View getViewByPosition(int pos, ListView listView) {
final int firstListItemPosition = listView.getFirstVisiblePosition();
final int lastListItemPosition = firstListItemPosition + listView.getChildCount();
if (pos < firstListItemPosition || pos > lastListItemPosition ) {
return listView.getAdapter().getView(pos, null, listView);
} else {
final int childIndex = pos - firstListItemPosition+1;
return listView.getChildAt(childIndex);
}
}
getView:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ListViewHolder listViewHolder;
if(convertView == null){
listViewHolder = new ListViewHolder();
convertView = activity.getLayoutInflater().inflate(R.layout.lista_wlasnych_spolek, null);
listViewHolder.txtFirst = (TextView) convertView.findViewById(R.id.nazwa_spolki);
listViewHolder.txtSecond = (TextView) convertView.findViewById(R.id.wartosc_akt);
listViewHolder.txtThird = (TextView) convertView.findViewById(R.id.wartosc_kupna);
listViewHolder.txtFourth = (TextView) convertView.findViewById(R.id.wartosc_calosci);
convertView.setTag(listViewHolder);
} else {
listViewHolder = (ListViewHolder) convertView.getTag();
}
First of all this line
return listView.getAdapter().getView(pos, null, listView);
makes no sense, because with this call by hand you will internally always create and inflate new row for the list view but this view is never used within your ListView. See that you are always passing second parameter convertView null so internally this method will create new view but this view will be never used inside your ListView.
Tip 1. Don't call getView() method yourself
As you may know ListView stores in memory only as many rows/view as they are visible on the screen when you use ViewHolder pattern properly.
So for now you are setting color for every row that is visible and even those not visible that really not exist in ListView.
Tip 2.
Best way to color or change anything about any of your rows, is to do it just inside getView() method implementation depend on your adapter item state. Don't do it from outside because it looks like some kind of a hack or wrong architecture.
I'm creating a custom adapter and using the getView method in attempt to display a default text only ONCE. Now I'm having a problem such that when I click the first Item in the list, the default text is kept but that doesn't hold for any other items? Any suggestions?
Thanks! (My code is a bit messy as I was just trying to debug)
boolean firstTime = true;
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (firstTime) {
firstTime = false;
TextView firstView = new TextView(ForgotPasswordActivity.this);
firstView.setText("Please select School");
return firstView;
}
TextView view = new TextView(ForgotPasswordActivity.this);
view.setText("Hello");
return view;
}
You must play with the getCount function :
#Override
public int getCount() {
return super.getCount() -1; // This makes the trick;
}
this trick will not show last item that you've added inside your spinner(so when you finish adding your text inside the spinner, add the text that will not be shown in the spinner, and by that it will be show as a default value before clicking the spinner).
Good luck
I'm not exactly sure what your trying to do but you could force the top row to always show the select message by checking if the position is 0. Also notice in the code below that I'm reusing the convertView if it is not null. It's faster to reuse the convertView if it is available than to recreate a new view every time.
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null) {
convertView = new TextView(ForgotPasswordActivity.this);
}
if(position == 0) {
convertView.setText("Please select School");
} else {
convertView.setText("Hello");
}
return convertView;
}
Also remember that by forcing position zero to show the select message you are not showing the actual data in the adapter at position 0. Make sure this is what you want to do or insert a dummy piece of data in the first position of the backing data array.
By default, when you select an item in spinner, it highlights briefly before disappearing.
I changed the color of my spinner rows to alternate color with the following code, and the highlight disappears. R.layout.textviewinside and R.layout.textview don't cause this, just the #Override for getDropDownView, because everything works if I comment out that block.
How can I restore that functionality but keep the row colors?
products = new ArrayAdapter<String>(this, R.layout.textview, thedata){
#Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
View v = super.getDropDownView(position, convertView, parent);
if (position % 2 == 0) { // we're on an even row
v.setBackgroundColor(0xffEBF4FA);//Color.BLUE)
} else {
v.setBackgroundColor(Color.WHITE);
}
((TextView) v).setGravity(Gravity.CENTER);
return v;
}
};
products.setDropDownViewResource(R.layout.textviewinside);
spitem.setAdapter(products);
Instead of using setBackgroundColor, you'll need to use setBackgroundDrawable, and use an xml state list drawable file with pressed/default states.
http://developer.android.com/guide/topics/resources/drawable-resource.html#StateList
I'm working on an application which will display some expandable lists. The fact is sometimes the object won't have any child. So I would like to display in my expandable list only elements which have childs.
I tried to put an if inside the getGroupView function and if the object has no child, I return null but I get a nullPointerException error...
Here is the function:
public View getGroupView(int i, boolean b, View view, ViewGroup viewGroup) {
// Check if the object as child
System.out.println("Result list :: " + mParent.get(i).getListe());
if(mParent.get(i).getListe().isEmpty()){
return null;
}
if (view == null) {
view = inflater.inflate(R.layout.list_item_parent, viewGroup,false);
}
TextView textView = (TextView) view.findViewById(R.id.list_item_text_view);
//"i" is the position of the parent/group in the list
textView.setText(getGroup(i).toString());
// Set the Image View with the brand logo
ImageView logo = (ImageView) view.findViewById(R.id.brandlogo);
// Set image Source
logo.setImageResource(mParent.get(i).getLogoPath());
// Set Image size
logo.getLayoutParams().height = 42;
logo.getLayoutParams().width = 226;
//return the entire view
return view;
}
Is there a way do skip this function?
Thanks
In getGroupCount() function return the number of items with childes (number of items has childes).
And in getGroubView() return the correct view (never return a null here).