I'm working on making a custom ArrayAdapter so that my list has the first element in a different color.
The thing is, when I execute this code in the get view method:
#Override
public View getView(int position, View convertView, ViewGroup parent){
Club club = (Club)getItem(position);
if(convertView == null){
convertView = LayoutInflater.from(getContext()).inflate(R.layout.list_item, parent, false);
}
TextView textView = (TextView)convertView.findViewById(R.id.txtListItem);
if(position == 0 && club.getName().contains("All")){
textView.setTextColor(ContextCompat.getColor(getContext(), R.color.orange));
}
textView.setText(club.getName());
return convertView;
}
The first item is orange, yes, but the 10th one (below the screen) is also when I scroll down :( in another list with more elements than what the screen can hold, I have several that are orange.. I don't understand why, please help!
This is happening because views are recycling/reused. You are setting orange color for the first item but not setting default color for the rest. Just add an else clause to your if statement above, something like this
if(position == 0 && club.getName().contains("All")){
Log.d(ClubAdapter.class.getName(), club.getName());
textView.setTextColor(ContextCompat.getColor(getContext(), R.color.orange));
}else{
textView.setTextColor(ContextCompat.getColor(getContext(), R.color.your_default_color));
}
Most likely this happens because Android is recycling the view which was originally used for the first row. You need an else clause to set the color back to normal.
This happens because the adapter tries to reuse as many views as possible for performance improvement. That's why you should not create a view in getView, but reuse them (only create if it is null):
if(convertView == null){
convertView = LayoutInflater.from(getContext()).inflate(R.layout.list_item, parent, false);
}
Even though you shouldn't create a new view, you have to make sure that you set the parameters that you want for each getView call (also called "binding"), so:
if(position == 0){
//set your first view color and whatever
}else{
//set your common view colors..
}
Because listview recycle views Google suggests to use the ViewHolder pattern. You should also set a default color if the cell is different. It would look something like this.
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if(convertView == null){
convertView = LayoutInflater.from(context).inflate(R.layout.list_item, parent, false);
viewHolder = new ViewHolder();
viewHolder.textView = (TextView) convertView.findViewById(R.id.txtListItem);
convertView.setTag(viewHolder);
}
else {
viewHolder = (ViewHolder) convertView.getTag();
}
if(position == 0 && club.getName().contains("All")){
viewHolder.textView.setTextColor(ContextCompat.getColor(getContext(), R.color.orange));
}else{
//Set default color
}
viewHolder.textView.setText(club.getName());
return convertView;
}
static class ViewHolder {
protected TextView textView;
}
Related
Currently my listview is working fine to return one view but I want it to
check when the currentVerse.getVerseNumber()==1 and return two different views (rows) if the condition is true. I cannot figure it out, Any help will be highly appreciated.
public View getView(int position, View convertView, ViewGroup parent) {
VersesModel currentVerse = verses.get(position);
if (convertView == null) {
convertView = getActivity().getLayoutInflater().inflate(
R.layout.verses_custom_list, parent, false);
viewHolder = new ViewHolder();
font = Typeface.createFromAsset(convertView.getContext().getAssets(), "my_font.ttf");
viewHolder.textView = (TextView) convertView.findViewById(R.id.textView_Verse);
viewHolder.nView = (TextView) convertView.findViewById(R.id.textView_verseNumber);
viewHolder.textView.setTypeface(font);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.textView.setText(currentVerse.getVerseText().toString());
viewHolder.nView.setText(currentVerse.getVerseNumber() + "");
convertView.setTag(viewHolder);
return convertView;
}
Its simple according to me ....
Create a layout with both views in the same layout which you want to show...
And suppose for condition true you want to show LinearLayout-A and for false u want to show LinearLayout-B then its simple use View.Visibility
if(true)
{
LinearLayout-B.setVisibility(View.GONE);
LinearLayout-A.setVisibility(View.VISIBLE);
}
else
{
LinearLayout-A.setVisibility(View.GONE);
LinearLayout-B.setVisibility(View.VISIBLE);
}
btw im using both because rememember listview paints everytime the view is created after its hidden so to make it easy and robust otherwise it wud just mess up alot...
Hope it helps u ...
Thanks
I defined a Adapter which extends BaseAdapter when I use ListView to display something.I overrided View getView (int position, View convertView, ViewGroup parent) method to reuse View component, in the method, I also wrote if(convertView == null) {System.out.println("test");} block. There are 50 rows data in ListView and the screen only can display about 20 rows data. when I ran the application, LogCat printed less than 50 rows of "test" though I slided the screen to make sure all data are loaded.But why ? I think it should print 50 rows data. Here is the key code:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null) {
convertView = inflater.inflate(resource, null);
System.out.println(++count + "convertView == null:" + convertView);
}
}
someone help me please, I am a newbie.... thanks
Android does not inflate a View for every item in your adapter. It reuses inflated views previously used for other items.
The pattern for binding views in a adapter is something like this:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view;
if(convertView == null) {
view = inflater.inflate(resource, null);
} else {
view = convertView;
}
// bind data to view here
return view;
}
In fact you normally would use a ViewHolder class. But first fix your basic adapter before reading about that.
Because convertView will be null only if there isn't a previously returned view that can be recycled (i.e. it's no longer on screen).
The views you return from an adapter's getView() can be recycled in later calls to getView(). The framework passes such recyclable views in via the convertView arg.
So your convertView == null branch only gets run enough times to fill the listview screen once and after that, when scrolling, these old views get recycled.
You missing return view.
ListView recycles view. How ListView's recycling mechanism works
Use a ViewHolder pattern
http://developer.android.com/training/improving-layouts/smooth-scrolling.html
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if(convertView == null) {
convertView = inflater.inflate(resource, null);
holder = new ViewHolder();
holder.tv = (TextView)convertView.findViewById(R.id.textView1);
//initialize views
convertView.setTag(holder);
} else {
holder =(ViewHolder) convertView.getTag();
}
// update your view here
holder.tv.setText("hi");
return convertView;
}
static class ViewHolder {
// YourViews Declaration
TextView tv; // an example
}
I have a listfragment which I am populating with a adapter extending Base adapter.
I want a textview in one particular to be of a different color.
This is my target: (Forgive me for the "Title" spelling, I was in a hurry)
However when I scroll the list up and down, textviews from different rows also change color randomly,like this:
I have tried with and without viewholders, with the same result. I can't seem to find out what the problem is.
This is my getView method
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolderItem viewHolder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.shedrow, null);
viewHolder = new ViewHolderItem();
viewHolder.txt=(TextView) convertView.findViewById(R.id.textview1);
viewHolder.txt2=(TextView) convertView.findViewById(R.id.textview2);
convertView.setTag(viewHolder);
}
else
{
viewHolder = (ViewHolderItem) convertView.getTag();
}
viewHolder.txt.setTypeface(tf);
viewHolder.txt2.setTypeface(tf);
viewHolder.txt.setText(schedStorage.getProgramTitle(position));
if (position==0)
{
viewHolder.txt2.setTextColor(color);
viewHolder.txt2.setTypeface(tf, Typeface.BOLD);
}
viewHolder.txt2.setText(sTimes.get(position));
return convertView;
}
convertView is a reused View (Row). When you scroll up and down and the first row goes off screen, Android may choose to reuse it when new rows show on-screen. If it gets reused, the color of txt2 will still be set to green. To avoid this, you should set the color and Typeface of txt2 when position != 0.
if (position==0) {
viewHolder.txt2.setTextColor(color);
viewHolder.txt2.setTypeface(tf, Typeface.BOLD);
} else {
viewHolder.txt2.setTextColor(Color.BLACK);
viewHolder.txt2.setTypeface(tf, Typeface.NORMAL);
}
I've a custom adapter for my ListView where I send to it a List, and if the position is in the list then the imageview (that is in the custom row) change its src to another.. Here is the GetView method:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.start_row, null); // line
// 47
holder = new ViewHolder();
holder.tv_SuraName = (TextView) convertView.findViewById(R.id.Start_Name);
holder.tv_SuraName.setTypeface(Font);
holder.tv_PageNumber = (TextView) convertView.findViewById(R.id.Start_Numbering);
holder.im_Audio = (ImageView) convertView.findViewById(R.id.Start_ImageView);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.tv_SuraName.setText(SuraList_SuraNamesCode[position]);
holder.tv_PageNumber.setText(Integer.toString(PageNumber[position]));
holder.im_Audio.setOnClickListener(new imageViewClickListener(position));
if (TilawaAvailable.contains(position))
holder.im_Audio.setImageResource(R.drawable.quran_list_audioavailable);
return convertView;
}
I send a List with 1 position.If I scroll the ListView slowly, this works good and only 1 imageview gets changed as it should be. But if I scroll fast, other imagesviews that are close to the correct position also gets changed!
Can anyone tell me why?
You don't ever set the imageResource to something else if position isn't contained in your list. When the view with the custom image leaves the screen it is probably getting placed at a lower position in the list and getting reused.
Try changing this:
if (TilawaAvailable.contains(position))
holder.im_Audio.setImageResource(R.drawable.quran_list_audioavailable);
To this:
if (TilawaAvailable.contains(position))
holder.im_Audio.setImageResource(R.drawable.quran_list_audioavailable);
else
holder.im_audio.setImageResource(r.drawable.SOME_THING_ELSE);
I am confused in changing the background colour of a particular row in an list view, below is the code I tried. Different rows gets highlighted as i scroll the list, I could like to understand the reason behind this. The logic seems to quite simple, but the results are unpredictable. How am I supposed to achieve this.
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.rows_for_layout, null);
holder = new ViewHolder();
holder.name = (TextView)convertView.findViewById(R.id.name);
holder.rated=(ImageView)convertView.findViewById(R.id.rated);
convertView.setTag(holder);
}else {
holder = (ViewHolder) convertView.getTag();
}
//selected_position is the position where the list has to be highlighted
if(position==selected_position){
holder.name.setText(elements.get(position).get("name"));
convertView.setBackgroundResource(R.drawable.highlight_this);
holder.rated.setBackgroundResource(R.drawable.star_image);
}else{
holder.name.setText(elements.get(position).get("name"));
}
return convertView;
}//getView ![alt text][1]
Your else statement does not reset the background color to its original. The getView method can recycle a view that was previously in your list but is not visible anymore. If the background was changed, then it will still be that background color from when it was originally created, which can depend on your state.
So, to "reset" that, add the following in your else:
if(position==selected_position){
holder.name.setText(elements.get(position).get("name"));
convertView.setBackgroundResource(R.drawable.highlight_this);
holder.rated.setBackgroundResource(R.drawable.star_image);
}else{
holder.name.setText(elements.get(position).get("name"));
//Add this
convertView.setBackgroundResource(R.drawable.not_highlighted);
}