• After 15+ years, we've made a big change: Android Forums is now Early Bird Club. Learn more here.

Apps Changing color of ListView item

yotam2010

Newbie
I try to change the color of list item on long click. for some reason it color the item and every 4th item.
(color the item i pressed and every 4th item which is out side the screen when i scroll to it)
the code:

Java:
mQuestionListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
                @Override
                public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
                    TextView textView = (TextView)view.findViewById(R.id.list_item_Question);
                        if(!mListItemPicked.contains(position)) {
                            mMenu.findItem(R.id.delete_Question_menu_item).setVisible(true);
                            mMenu.findItem(R.id.edit_Question_menu_item).setVisible(true);

                            textView.setBackgroundColor(Color.RED);
                            mListItemPicked.add(position);
                        }else{
                            textView.setBackgroundColor(Color.WHITE);
                            mListItemPicked.remove(mListItemPicked.indexOf(position));
                            if(mListItemPicked.size()==0){
                                mMenu.findItem(R.id.delete_Question_menu_item).setVisible(false);
                                mMenu.findItem(R.id.edit_Question_menu_item).setVisible(false);
                            }
                        }

                    return false;
                }
            });

the resault is something like:
View
View
View
View
View
View
View
 
It's happening because ListView uses View recycling when you scroll. For efficiency reasons, once a list item scrolls off the screen, it can be reused in an item coming on to the screen. If you've coloured the TextView (which is a child View of the row) red, then it will also show as red in the line which has been allocated the re-used View.

You need to implement a custom ListAdapter to have much more control over how your Views are reused, and set the properties as required.
 
it is custom adapter that inherent from ArrayAdapter.
from which class should i inherent / how should i change it?
Its list of Cards that contains texts btw.

P.S. you already helped me with other post, your help is much apreacaited =)
 
Yes your custom adapter will extend from ArrayAdapter.
The method getView() is called on your adapter, which should return the correct view for a row.

You should also consider using the 'Viewholder' pattern, because inflating a View and calling findViewById() is expensive and will slow down your list scrolling performance. Basically, a ViewHolder structure is created, to contain only those elements of the row View which need to change e.g. the TextView. The row View can be 'tagged' with this. So when the row View is reused, you set the properties of the ViewHolder elements with the correct data, or properties e.g. colour.

http://www.vogella.com/tutorials/AndroidListView/article.html#adapterown

In particular

http://www.vogella.com/tutorials/AndroidListView/article.html#adapterperformance_holder

So the getView() method of your custom adapter should contain logic to determine whether the row at a certain position should be coloured red or white, and set the background colour of the TextView accordingly.
 
Last edited by a moderator:
Yes your custom adapter will extend from ArrayAdapter.
The method getView() is called on your adapter, which should return the correct view for a row.

You should also consider using the 'Viewholder' pattern, because inflating a View and calling findViewById() is expensive and will slow down your list scrolling performance. Basically, a ViewHolder structure is created, to contain only those elements of the row View which need to change e.g. the TextView. The row View can be 'tagged' with this. So when the row View is reused, you set the properties of the ViewHolder elements with the correct data, or properties e.g. colour.

http://www.vogella.com/tutorials/AndroidListView/article.html#adapterown

In particular

http://www.vogella.com/tutorials/AndroidListView/article.html#adapterperformance_holder

So the getView() method of your custom adapter should contain logic to determine whether the row at a certain position should be coloured red or white, and set the background colour of the TextView accordingly.

tried to use viewHolder with set and get tag but still have the same problem.
for now the way i found to get pass it is to always inflate the view rather then use the covertView but from what i know its
wastefull with resources so i wonder if there is better way.

NVM

doesnt work the way i wanted.

anywa my current code:

Java:
  @NonNull
    @Override
    public View getView(final int position, final View convertView, ViewGroup parent) {
        final Question questionItem = mQuestionList.get(position);
        View viewItem=convertView;
        ViewHolder viewHolder=null;
        if(viewItem==null) {
            viewItem = mLayoutInflater.inflate(R.layout.list_question_item, null);
             viewHolder = new ViewHolder();
            viewHolder.questionTextView = (TextView) viewItem.findViewById(R.id.list_item_Question);
            viewHolder.difficultyTextView = (TextView) viewItem.findViewById(R.id.list_item_Difficulty);
            viewHolder.categoryTextView= (TextView) viewItem.findViewById(R.id.list_item_Category);
            viewItem.setTag(viewHolder);
        }else{
            viewHolder= (ViewHolder)viewItem.getTag();
        }
        viewHolder.questionTextView.setText(mContext.getString(R.string.question)+": "+ questionItem.getQuestion());
        viewHolder.difficultyTextView.setText(mContext.getString(R.string.difficulty)+": "+questionItem.getDifficulty());
        viewHolder.categoryTextView.setText(mContext.getString(R.string.category)+": "+questionItem.getCategory());
        final TextView question=viewHolder.questionTextView;
        viewItem.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {

                    question.setBackgroundColor(Color.RED);

                return false;
            }
        });
       
        return viewItem;
    }
 
Last edited:
tried to make the long click on ListItem, same problem even when i use position.
Java:
mQuestionListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
                @Override
                public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
                    mQuestionListView.getChildAt(position).setBackgroundColor(Color.RED);
                    return false;
                }
            });
 
Your getView() method must decide if the row's TextView has to be white or red. So the logic for setting the colour must be in this method, in addition to the onItemLongClick() method.

So how does getView() know if the row is red or white? Well that is determined by the position value. So you have to maintain a record of which row items have been long-clicked. You can do this by using a simple boolean[] array.
setOnItemLongClickListener() is responsible for setting the values in this array.
 
Your getView() method must decide if the row's TextView has to be white or red. So the logic for setting the colour must be in this method, in addition to the onItemLongClick() method.

So how does getView() know if the row is red or white? Well that is determined by the position value. So you have to maintain a record of which row items have been long-clicked. You can do this by using a simple boolean[] array.
setOnItemLongClickListener() is responsible for setting the values in this array.

i did added a check (something similtar to th list you suggested).
YET
its go into the onItemLongClick and to the:
mQuestionListView.getChildAt(position).setBackgroundColor(Color.RED);
and change it for every view in the same + 4 like:
to the view at position 1,5,9,13 etc....
ALTHOUGH the position is 1

The current code:

Code:
public class QuestionListAdapter extends ArrayAdapter {
    LayoutInflater mLayoutInflater;
    Context mContext;
    List<Question> mQuestionList;
    List<Boolean> mColoredList;
    public QuestionListAdapter(Context context, int resource, List objects) {
        super(context, resource, objects);
        mLayoutInflater= (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        mContext=context;
        mQuestionList=(List<Question>)objects;
        mColoredList=new ArrayList<>();
        for(int i=0;i<mQuestionList.size();i++)
            mColoredList.add(false);
    }
    static class ViewHolder{
        TextView questionTextView;
        TextView difficultyTextView;
        TextView categoryTextView;
    }

    @Override
    public int getCount() {
        return mQuestionList==null ? 0 : mQuestionList.size();
    }
  

    @NonNull
    @Override
    public View getView(final int position, final View convertView, ViewGroup parent) {
        final Question questionItem = mQuestionList.get(position);
        View viewItem=convertView;
        ViewHolder viewHolder=null;
        if(viewItem==null) {
            viewItem = mLayoutInflater.inflate(R.layout.list_question_item, null);
             viewHolder = new ViewHolder();
            viewHolder.questionTextView = (TextView) viewItem.findViewById(R.id.list_item_Question);
            viewHolder.difficultyTextView = (TextView) viewItem.findViewById(R.id.list_item_Difficulty);
            viewHolder.categoryTextView= (TextView) viewItem.findViewById(R.id.list_item_Category);
            viewItem.setTag(viewHolder);
        }else{
            viewHolder= (ViewHolder)viewItem.getTag();

        }
        viewHolder.questionTextView.setText(mContext.getString(R.string.question)+": "+ questionItem.getQuestion());
        viewHolder.difficultyTextView.setText(mContext.getString(R.string.difficulty)+": "+questionItem.getDifficulty());
        viewHolder.categoryTextView.setText(mContext.getString(R.string.category)+": "+questionItem.getCategory());
        final TextView question=viewHolder.questionTextView;
        viewItem.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {

                if(!mColoredList.get(position))
                {
                    question.setBackgroundColor(Color.RED);
                    mColoredList.set(position,true);
                }


                return false;
            }
        });
        return viewItem;
    }
}

P.S.

every time i scroll the views that are colored in red change.
like if it was
1,5,9
its change to
2,6,10
and then back to 1,5,9
(the text stays the same only the color change)
 
Last edited:
Your getView() method still doesn't contain any code to set the colour, depending on the position requested.
 
To be fair, I think that View recycling is just about the clunkiest thing that Google have put into the Android framework. It leads to no end of trouble, and complicates your code. When you have row specific information, and it's very common to want to select or somehow highlight specific rows. To require the user's code to keep track of this is quite bad.
 
To be fair, I think that View recycling is just about the clunkiest thing that Google have put into the Android framework. It leads to no end of trouble, and complicates your code. When you have row specific information, and it's very common to want to select or somehow highlight specific rows. To require the user's code to keep track of this is quite bad.

well maybe i missunderstood / dont understand whati suppose to do exactly.

Code:
if(!mColoredList.get(position))
                {
                    question.setBackgroundColor(Color.RED);
                    mColoredList.set(position,true);
                }

^ in this part i check if this view wasnt painted yet and in this case its set the TextView backgorund color.
The TextView represent ViewHolder TextView

i thought the get / set Tag suppose to handle the "unique" row.
so after i set the tag and then use get tag i suppose to Holder that represent a single row.
was i wrong?

BTW

when i tried to use the longClick liek this:

Code:
mQuestionListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
                @Override
                public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
                    mQuestionListView.getChildAt(position).setBackgroundColor(Color.RED);
                    return false;
                }
            });

the app got crushed when i pressed on 5th item and above. mQuestionListView.getChildAt(position) returned null.

From what i understood getChildAt(position) check only the the views that are on screen so even if there are 20 items in the list only 4 show on screen so the index can only be 1-4.
is there other way to get specific position item?
 
Last edited:
You have a few misunderstandings, and unfortunately it's very difficult to explain in the back and forth manner of a forum.

The code you quoted above has nothing at all to do with the getView() method. It's in the setOnLongClickListener() method. So it only gets called when you do a long click on the row.

The purpose of getView() is to return the row View which is being rendered at any time. So if you're scrolling the list, it will be continually called to get rows as they appear on the screen.

The complication here is View recycling. Because the 'convertView' parameter that gets passed in to this method is one that was used on a different row (now off the screen).
So the main purpose of getView() is to set the correct state of the View (including all child Views), according to the position.

It took me a while to actually properly get this concept, and until I did, I was very confused. Try looking at some examples, but it's important that you do fully understand what's going on, because this is fundamental.
Take some time to think it over, examples do really help, and there are a lot around.
 
You have a few misunderstandings, and unfortunately it's very difficult to explain in the back and forth manner of a forum.

The code you quoted above has nothing at all to do with the getView() method. It's in the setOnLongClickListener() method. So it only gets called when you do a long click on the row.

The purpose of getView() is to return the row View which is being rendered at any time. So if you're scrolling the list, it will be continually called to get rows as they appear on the screen.

The complication here is View recycling. Because the 'convertView' parameter that gets passed in to this method is one that was used on a different row (now off the screen).
So the main purpose of getView() is to set the correct state of the View (including all child Views), according to the position.

It took me a while to actually properly get this concept, and until I did, I was very confused. Try looking at some examples, but it's important that you do fully understand what's going on, because this is fundamental.
Take some time to think it over, examples do really help, and there are a lot around.

Took me awhile but i finally understood.
I solved the problem just want to say thanks for all the patience and help.
Too bad there are no more people like you in this community (well i am new here but you are the only 1 responding)
Have a great day!
 
Thanks for your kind words. Very pleased you got it working!
 
Back
Top Bottom