ImageView not updating in RecyclerView

Nathan Sokalski 4,111 Reputation points
2024-10-18T18:02:52.0833333+00:00

I have a RecyclerView with the following item layout:

<?xml version="1.0" encoding="utf-8"?>
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" android:columnCount="3" android:rowCount="1" android:paddingHorizontal="0dp" android:paddingVertical="2dp" tools:ignore="RequiredSizeWidth,RequiredSizeHeight,MissingInputType,HardcodedText,HardcodedSize,MissingDimension">	<!--paddingLeft = (layout_width - (width of drawable)) / 2-->
	<Button android:id="@+id/btnPiece" style="@style/PieceButton" android:layout_column="0" android:drawableLeft="@drawable/meepledrawable" android:paddingLeft="7.5dp" android:drawableTint="@color/RoyalBlue"/>
	<EditText android:id="@+id/txtPlayerName" style="@style/PlayerNameEditTextStyle" android:layout_column="1" android:layout_columnWeight="1"/>
	<Button android:id="@+id/btnDeletePlayerName" style="@style/BasicDeleteButton" android:layout_column="2" android:layout_marginVertical="0dp" android:layout_marginLeft="0dp" android:layout_marginRight="5dp" android:layout_gravity="center"/>
</GridLayout>

This gives the following in the Visual Studio 2022 Designer:

User's image

Notice that the first Button (btnPiece) uses a drawableLeft, not an image. This is done so that I can change the color using drawableTint. In the Adapter, I define OnBindViewHolder as follows:

public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
	PlayerNameViewHolder vh = holder as PlayerNameViewHolder;
	vh.txtPlayerName.Text = this.Players[position].Name;
	vh.btnDeletePlayerName.Visibility = position == (this.ItemCount - 1) ? ViewStates.Invisible : ViewStates.Visible;
	vh.btnPiece.Visibility = position == (this.ItemCount - 1) ? ViewStates.Invisible : ViewStates.Visible;
	if (position < (this.ItemCount - 1))
	{
		vh.btnPiece.SetCompoundDrawables(this.GetPieceDrawable, null, null, null);
		//vh.btnPiece.SetPadding((int)(((vh.btnPiece.Width - 40) / 2) * vh.btnPiece.Context.Resources.DisplayMetrics.Density), 0, 0, 0);
		vh.btnPiece.BackgroundTintList = ColorStateList.ValueOf(this.Players[position].PlayerColor);
	}
	vh.btnPiece.Click += btnPiece_Click;
	vh.txtPlayerName.TextChanged += this.txtPlayerName_TextChanged;
	vh.txtPlayerName.FocusChange += this.txtPlayerName_FocusChange;
	vh.btnDeletePlayerName.Click += this.btnDeletePlayerName_Click;
}

I also define the TextChanged EventHandler as follows:

private void txtPlayerName_TextChanged(object sender, TextChangedEventArgs e)
{
	GridLayout grdRoot = (sender as EditText).Parent as GridLayout;
	RecyclerView rvPlayerNames = grdRoot.Parent as RecyclerView;
	int index = rvPlayerNames.GetChildAdapterPosition(grdRoot);
	PlayerNameViewHolder vh = rvPlayerNames.FindViewHolderForAdapterPosition(index) as PlayerNameViewHolder;

	if (index != -1)
	{
		if (index == (this.Players.Count - 1) && this.Players[this.Players.Count - 1].Name.Trim().Length == 0 && e.Text.ToString().Trim().Length > 0)
		{
			//A new name is being added
			this.Players.Add(new PlayerData());
			vh.btnPiece.Visibility = ViewStates.Visible;

			vh.btnPiece.SetCompoundDrawables(Application.Context.GetDrawable(Resource.Drawable.meepledrawable), null, null, null);
			//vh.btnPiece.SetPadding((int)(((vh.btnPiece.Width - 40) / 2) * vh.btnPiece.Context.Resources.DisplayMetrics.Density), 0, 0, 0);
			vh.btnPiece.BackgroundTintList = ColorStateList.ValueOf(this.Players[index].PlayerColor);

			vh.btnDeletePlayerName.Visibility = ViewStates.Visible;
			this.NotifyItemInserted(index + 1);
			rvPlayerNames.SmoothScrollToPosition(index);
		}
		else if (index == (this.Players.Count - 2) && this.Players[this.Players.Count - 2].Name.Length > 0 && e.Text.ToString().Trim().Length == 0)
		{
			//The last name (2nd last item) is deleted
			this.Players.RemoveAt(index + 1);
			vh.btnPiece.Visibility = ViewStates.Invisible;
			vh.btnDeletePlayerName.Visibility = ViewStates.Invisible;
			this.NotifyItemRemoved(index + 1);
		}

		this.Players[index].Name = e.Text.ToString();
	}
}

My goal is to have another item added to the RecyclerView when the EditText (txtPlayerName) has text added to it, as well as unhide the Buttons (btnPiece & btnDeletePlayerName). I also wish to modify the drawableLeft, paddingLeft & drawableTint of the first Button (btnPiece). This all works fine for the EditText (txtPlayerName) & second Button (btnDeletePlayerName), but if I attempt to set the drawableLeft of the first Button (btnPiece) using SetCompoundDrawables (as shown in the code above), it is hidden. Setting only the drawableTint (using BackgroundTintList as shown above) it is still visible, but the color does not change. Setting the paddingLeft (using SetPadding as shown above), however, does work. I'm sure that all of this can somehow be solved using the Notify methods (such as NotifyItemInserted & NotifyItemChanged), but I can't seem to get it to work. Can someone please help? Thanks!

Xamarin
Xamarin
A Microsoft open-source app platform for building Android and iOS apps with .NET and C#.
5,363 questions
.NET
.NET
Microsoft Technologies based on the .NET software framework.
3,941 questions
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
11,046 questions
{count} votes

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.