0

I want to update a previous TextView item in my RecyclerView when the user clicks on another item:

For example, if the user clicks on "lemanju" TextView it will change the font color to orange, and when the user clicks on "Lightning" TextView, lemanju's font color should go back to black and Lighning's font color must become orange, as you can see below.

lemanju being selected

Lightning being selected (How I expect it to work)

Lightning being selected (What is happening now -> not updating lemanju's font color back to black)

I tried the following code in my adapter class to achieve this result:

// set previous item TextColor to black.
 holder.itemView.setTextColor(ContextCompat.getColor(inflater.getContext(), R.color.BaseColor_1));
 holder.adapter.notifyItemChanged(previousSamplePosition);

 // set actual item TextColor to orange.
 holder.itemView.setTextColor(ContextCompat.getColor(inflater.getContext(), R.color.PrimaryColor_1));

Unfortunately, I wasn't able to change lemanju's font color back to black when I have clicked on Lightning...

Does anyone know how to change a specific TextView using the notifyItemChanged? Or is there another way to achieve it?

My onCreateViewHolder method inside my adapter class you can see below:

/**
     * Sets the contents of an item at a given position in the RecyclerView.
     * Called by RecyclerView to display the data at a specificed position.
     *
     * @param holder         The view holder for that position in the RecyclerView.
     * @param samplePosition The position of the item in the RecyclerView.
     *                       <p>
     */
    @Override
    public void onBindViewHolder(@NonNull SampleViewHolder holder, int samplePosition) {

        if (sampleList  != null) {
            Sample currentSample = sampleListFiltered.getCurrentList().get(samplePosition);
            // Add the data to the view holder.
            holder.sampleView.setText(currentSample.getName());
            holder.bind(currentSample);

            if (sampleNameSearched != null) {

                String sampleName = Normalizer.normalize(sampleListFiltered.getCurrentList().get(samplePosition).getName(), Normalizer.Form.NFD).replaceAll("[^\\p{ASCII}]", "").toLowerCase();
                Pattern sampleQuery = Pattern.compile(sampleNameSearched);
                Matcher sampleMatcher = sampleQuery.matcher(sampleName);
                SpannableStringBuilder spanString = new SpannableStringBuilder(sampleListFiltered.getCurrentList().get(samplePosition).getName());

                while (sampleMatcher.find()) {
                    spanString.setSpan(new android.text.style.StyleSpan(android.graphics.Typeface.BOLD), sampleMatcher.start(), sampleMatcher.end(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
                }
                holder.sampleView.setText(spanString);
            } else {
                // This solved the problem
                holder.sampleView.setTextColor(ContextCompat.getColor(inflater.getContext(), R.color.BaseColor_1));
            }
        }

        Bundle sampleBundle = new Bundle();

        holder.sampleView.setOnClickListener(view -> {
            sampleBundle.putLong("Sample", sampleListFiltered.getCurrentList().get(samplePosition).getId());
            SampleCollectorListNavigatorFragment.addScreenToStack(0);
            SampleCollectorListNavigatorFragment.setLastSurveyIdAccessed(sampleListFiltered.getCurrentList().get(samplePosition).getSurveyId());
            Navigation.findNavController(view).navigate(R.id.action_sampleCollectorListNavigator_to_sampleCollectorInspectSampleFragment, sampleBundle);
        });

        holder.sampleView.setOnLongClickListener(view -> {

            sampleBundle.putLong("Sample", sampleListFiltered.getCurrentList().get(samplePosition).getId());

            if (sampleID == 0) {
                // Access the view of the previous screen
                ((Activity) inflater.getContext()).findViewById(R.id.ButtonPanel).setVisibility(View.VISIBLE);
                holder.sampleView.setTextColor(ContextCompat.getColor(inflater.getContext(), R.color.PrimaryColor_1));
            } else if (sampleID == sampleListFiltered.getCurrentList().get(samplePosition).getId()) {
                if (((Activity) inflater.getContext()).findViewById(R.id.ButtonPanel).getVisibility() == View.VISIBLE) {
                    ((Activity) inflater.getContext()).findViewById(R.id.ButtonPanel).setVisibility(View.GONE);
                    holder.sampleView.setTextColor(ContextCompat.getColor(inflater.getContext(), R.color.BaseColor_1));
                } else {
                    ((Activity) inflater.getContext()).findViewById(R.id.ButtonPanel).setVisibility(View.VISIBLE);
                    holder.sampleView.setTextColor(ContextCompat.getColor(inflater.getContext(), R.color.PrimaryColor_1));
                }
            } else if (sampleID != sampleListFiltered.getCurrentList().get(samplePosition).getId()) {
                holder.sampleView.setTextColor(ContextCompat.getColor(inflater.getContext(), R.color.BaseColor_1));
                notifyItemChanged(previousSamplePosition);
                holder.sampleView.setTextColor(ContextCompat.getColor(inflater.getContext(), R.color.PrimaryColor_1));
                ((Activity) inflater.getContext()).findViewById(R.id.ButtonPanel).setVisibility(View.VISIBLE);
            }

            sampleID = sampleListFiltered.getCurrentList().get(samplePosition).getId();
            previousSamplePosition = samplePosition;

            return true;
        });
    }
}

Update -> How to solve this problem?

To solve the problem in my code I had to update the string back to black in my search filter:

// This solved the problem         
holder.sampleView.setTextColor(ContextCompat.getColor(inflater.getContext(), R.color.BaseColor_1));

But if you don't have the same structure as mine or you just want to update a previous TextView item in the recycler you can use this part of the code:

holder.sampleView.setTextColor(ContextCompat.getColor(inflater.getContext(), R.color.BaseColor_1));
notifyItemChanged(previousSamplePosition);

The notifyItemChanged(previousSamplePosition) will do the trick.

1 Answers1

1

This is a basic example and down below I've added your code as well

Your adapter

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder>{
    
    
    private String[] mDataset;
    List<TextView> textViews = new ArrayList<>();
    
    
    // Provide a reference to the views for each data item
    // Complex data items may need more than one view per item, and
    // you provide access to all the views for a data item in a view holder
    public static class MyViewHolder extends RecyclerView.ViewHolder {
        // each data item is just a string in this case
        public TextView textView;
        public MyViewHolder(TextView v) {
            super(v);
            textView = v.findViewById(R.id.textView);
        }
    }
    
    public MyAdapter(String[] myDataset) {
        mDataset = myDataset;
    }

    @Override
    public MyAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent,
                                                   int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.my_text_view, parent, false);
       
        MyViewHolder vh = new MyViewHolder(v);
        return vh;
    }

    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        holder.textView.setText(mDataset[position]);
        views.add(holder.textView);

    }

    @Override
    public int getItemCount() {
        return mDataset.length;
    }
    
    public List<TextView> getTextViews(){
        return textViews;
    }
}

Your Activity

public class MainActivity extends AppCompatActivity {
    
    private Button changeColorButton;
    private RecyclerView recyclerView;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        changeColorButton = findViewById(R.id.button);
        recyclerView = findViewById(R.id.recyclerView);
        
        MyAdapter adapter = new MyAdapter(new String[]{"Hello", "world"});
        recyclerView.setAdapter(adapter);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        
        changeColorButton.setOnClickListener(new OnClickListener() {
 
            @Override
            public void onClick(View view) {
                int length = adapter.getTextViews().size();
                List<TextView> textViews = adapter.textViews;
                
                for(int i=0; i<length; i++){
                    textViews.get(i).setTextColor(Color.parseColor("#bdbdbd"));
                }
            }
 
        });
    }
}

Now as for your code

public static class SampleViewHolder extends RecyclerView.ViewHolder {

        private final TextView sampleView;

        /**
         * Creates a new custom view holder to hold the view to display in the RecyclerView.
         *
         * @param sampleListView The view in which to display the data.
         * @param adapter        The adapter that manages the the data and views for the RecyclerView.
         */
        public SampleViewHolder(View sampleListView) {
            super(sampleListView);
            sampleView = sampleListView.findViewById(R.id.data_name);
        }
    }

    /**
     * Inflates an item view and returns a new view holder that contains it.
     * Called when the RecyclerView needs a new view holder to represent an item.
     *
     * @param parent   The view group that holds the item views.
     * @param viewType Used to distinguish views, if more than one type of item view is used.
     * @return a view holder.
     */
    @NonNull
    @Override
    public SampleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        // Inflate an item view.
        View sampleListView = inflater.inflate(R.layout.fragment_sample_collector_list_sample, parent, false);
        return new SampleViewHolder(sampleListView);
    }

    /**
     * Sets the contents of an item at a given position in the RecyclerView.
     * Called by RecyclerView to display the data at a specificed position.
     *
     * @param holder         The view holder for that position in the RecyclerView.
     * @param samplePosition The position of the item in the RecyclerView.
     *                       <p>
     *                       TODO: Most important method used
     */
    @Override
    public void onBindViewHolder(@NonNull SampleViewHolder holder, int samplePosition) {

        if (sampleList != null) {
            // Retrieve the data for that position.
            //Sample currentSample = sampleListFiltered.get(samplePosition);
            Sample currentSample = sampleListFiltered.getCurrentList().get(samplePosition);
            // Add the data to the view holder.
            holder.sampleView.setText(currentSample.getName());
            holder.bind(currentSample);
            list.add(holder.sampleView);

            if (sampleNameSearched != null) {

                String sampleName = Normalizer.normalize(sampleListFiltered.getCurrentList().get(samplePosition).getName(), Normalizer.Form.NFD).replaceAll("[^\\p{ASCII}]", "").toLowerCase();
                Pattern sampleQuery = Pattern.compile(sampleNameSearched);
                Matcher sampleMatcher = sampleQuery.matcher(sampleName);
                SpannableStringBuilder spanString = new SpannableStringBuilder(sampleListFiltered.getCurrentList().get(samplePosition).getName());

                while (sampleMatcher.find()) {
                    spanString.setSpan(new android.text.style.StyleSpan(android.graphics.Typeface.BOLD), sampleMatcher.start(), sampleMatcher.end(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
                }
                holder.sampleView.setText(spanString);
            }
        }

        Bundle sampleBundle = new Bundle();
        holder.sampleView.setOnClickListener(view -> {
            sampleBundle.putLong("Sample", sampleListFiltered.getCurrentList().get(samplePosition).getId());
            SampleCollectorListNavigatorFragment.addPreviousScreen(0);
            SampleCollectorListNavigatorFragment.setLastSurveyIdAccessed(sampleListFiltered.getCurrentList().get(samplePosition).getSurveyId());
            Navigation.findNavController(view).navigate(R.id.action_sampleCollectorListNavigator_to_sampleCollectorInspectSampleFragment, sampleBundle);
        });

        holder.sampleView.setOnLongClickListener(view -> {

            sampleBundle.putLong("Sample", sampleListFiltered.getCurrentList().get(samplePosition).getId());

            // TODO: if the same button is clicked close the buttonPanel, else keep it open.
            if (sampleID == 0) {
                // Access the view of the previous screen
                ((Activity) inflater.getContext()).findViewById(R.id.ButtonPanel).setVisibility(View.VISIBLE);
                holder.sampleView.setTextColor(ContextCompat.getColor(inflater.getContext(), R.color.PrimaryColor_1));
            } else if (sampleID == sampleListFiltered.getCurrentList().get(samplePosition).getId()) {
                if (((Activity) inflater.getContext()).findViewById(R.id.ButtonPanel).getVisibility() == View.VISIBLE) {
                    ((Activity) inflater.getContext()).findViewById(R.id.ButtonPanel).setVisibility(View.GONE);
                    holder.sampleView.setTextColor(ContextCompat.getColor(inflater.getContext(), R.color.BaseColor_1));
                } else {
                    ((Activity) inflater.getContext()).findViewById(R.id.ButtonPanel).setVisibility(View.VISIBLE);
                    holder.sampleView.setTextColor(ContextCompat.getColor(inflater.getContext(), R.color.PrimaryColor_1));
                }
            } else if (sampleID != sampleListFiltered.getCurrentList().get(samplePosition).getId()) {
                Toast.makeText(inflater.getContext(), "list size() " + list.size(), Toast.LENGTH_SHORT).show();
                // I CANNOT CHANGE THE COLOR OF THE PREVIOUS SAMPLE RIGHT HERE.
                List<RecyclerView.ViewHolder> vh = getItem();
                changeColor(vh,previousSamplePosition);
                holder.adapter.notifyItemChanged(previousSamplePosition);
                holder.sampleView.setTextColor(ContextCompat.getColor(inflater.getContext(), R.color.PrimaryColor_1));
                ((Activity) inflater.getContext()).findViewById(R.id.ButtonPanel).setVisibility(View.VISIBLE);
            }

            sampleID = sampleListFiltered.getCurrentList().get(samplePosition).getId();
            previousSamplePosition = samplePosition;

            return true;
        });
    }

    public List<RecyclerView.ViewHolder> getItem(){
        return list;
    }

    public void changeColor(List<RecyclerView.ViewHolder> vh, int previousSamplePosition) {
        for (int i = 0; i < list.size(); i++) {
            if(i == previousSamplePosition) {
                // Does not change the previous TextView.
                Toast.makeText(inflater.getContext(), "i -> " + i + " previousSamplePos -> " + previousSamplePosition, Toast.LENGTH_SHORT).show();
                // How can I use the vh here?
                //vh.get(i).setTextColor(ContextCompat.getColor(inflater.getContext(), R.color.BaseColor_1));
                //holder.adapter.notifyItemChanged(i);
            }
        }
    }
}

Let me know if it works

Hamza Abbas
  • 178
  • 9
  • I have all this structure in my adapter, but what should I put in the changeColor function to change the specific TextView item color? I mean... how can I change a specific item of my adapter list? I will add my CreateViewHolder so you can see what I am doing... – HumbleGuyImproving Aug 27 '20 at 17:18
  • TextView text; public ViewHolder(@NonNull View itemView) { super(itemView); text = itemView.findViewById(R.id.theID); } public changeColor(){ text.setTextColor(Color.parseColor("#bdbdbd")); } } Refer to this link to see how to change the color of textVIew https://stackoverflow.com/questions/8472349/how-to-set-text-color-to-a-text-view-programmatically – Hamza Abbas Aug 27 '20 at 17:39
  • Yes, I used it in the code already, but how do I update the previous item that I have clicked? Like, how can I update a previous TextView: // Here... how do a change the color from the previous and update it? holder.sampleView.setTextColor(ContextCompat.getColor(inflater.getContext(), R.color.BaseColor_1)); holder.adapter.notifyItemChanged(previousSamplePosition); – HumbleGuyImproving Aug 27 '20 at 17:50
  • In my answer look the 2 last code snippets, A method to store all the elements and a loop to change the color of every element. – Hamza Abbas Aug 27 '20 at 17:53
  • The **viewHolders** return variable in getItem() is a list(the **list** that you created) or a ViewHolder object? – HumbleGuyImproving Aug 27 '20 at 18:05
  • It's a list, look at the second and the third snippet – Hamza Abbas Aug 27 '20 at 18:13
  • Is it necessary to do the for loop inside the activity or could I do it in a function in my adapter? Because I need to control the change of color inside it as you can see in my `else if (sampleID != sampleListFiltered.getCurrentList().get(samplePosition).getId())` – HumbleGuyImproving Aug 27 '20 at 19:34
  • you can make that loop in the adapter if you'd like but you're going to have to call that loop somewhere. – Hamza Abbas Aug 28 '20 at 05:36
  • I called the loop inside my "else if" condition from the previous answer, but I couldn't change the TextView color. – HumbleGuyImproving Aug 31 '20 at 12:06
  • I added the changes to the code, if you can have a look the problem is under the comment `// I CANNOT CHANGE THE COLOR OF THE PREVIOUS SAMPLE RIGHT HERE.` – HumbleGuyImproving Aug 31 '20 at 14:04
  • I'm so sorry, Upon reviewing you code I realized I posted the wrong code, Please read the 3rd snippet, I've made changes to it, I was adding the viewholder in onCreateViewHolder instead I had to do in onBindViewHolder, Please let me know if it works for you. – Hamza Abbas Aug 31 '20 at 14:26
  • I changed to onBindViewHolder. Could you have a look at my changeColor(vh, previousSamplePosition) function? I am not sure how to use the vh there to set the color of the TextView. – HumbleGuyImproving Aug 31 '20 at 16:32
  • You're doing it wrong, I'm gonna rewrite my answer. – Hamza Abbas Aug 31 '20 at 17:23
  • I can't use my activity to handle it, I have a Fragment class called ListNavigator that handles the adapters that I have. And I also can't use this `changeColorButton = findViewById(R.id.button);`, because the action happens when the user clicks on the data_name (a TextView in my adapter.xml) and the adapter.xml only is inflated in my adapter class, so I don't have data_name (which would be you R.id."button") reference. – HumbleGuyImproving Aug 31 '20 at 19:19
  • Jesus, put the activity code in you fragment and just call this any where you want to int length = adapter.getTextViews().size(); List textViews = adapter.textViews; for(int i=0; i – Hamza Abbas Aug 31 '20 at 19:24
  • In your code this one would be my "button" -> `textView = v.findViewById(R.id.textView);`. As it only gets created in the adapter, I can't access its reference in my Fragment class that controls the buttons listeners. – HumbleGuyImproving Aug 31 '20 at 19:25
  • I will try it out ASAP. – HumbleGuyImproving Aug 31 '20 at 19:27
  • Thanks for your help Hamza Abbas, I found my mistake in the code, I had forgotten to set it back to black in my search filter, so it always maintained the color in orange. I will update the code to the correct answer. Many thanks for your help! I hope that your answer help other people with a similar problem! :) – HumbleGuyImproving Aug 31 '20 at 21:45
  • You can access it in your fragment too(if you made the adapter in the fragment), anyway I'm glad I could help you, took 2 days but we did it, good luck. – Hamza Abbas Sep 01 '20 at 12:22