Search code examples
androidxamarinspinnerclip

How to make items corners clip inside spinner popup?


I've made a custom popupBackground with a corner radius of 16dp. The problem is, the items inside that popup may have a background color that doesn't go round following its parent popup, and I'm lost about how to do it since I don't see any "ClipToBounds" kind of property. The same happens with the ripple effect when tapping an item. An image representing what I have right now and the code used to generate it:

Popup items

Spinner definition:

<androidx.appcompat.widget.AppCompatSpinner
    android:id="@+id/ItemOptionsSpinner"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:dropDownWidth="wrap_content"
    android:popupBackground="@drawable/spinner_background"
    android:paddingRight="0dp"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintHorizontal_bias="1"
    android:spinnerMode="dropdown"
    android:visibility="invisible" />

spinner_background.xml inside drawable folder:

<?xml version="1.0" encoding="utf-8"?>
<shape 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <corners android:radius="16dp" />
    <solid android:color="?android:colorBackground" />
</shape>

SpinnerItemLineDropLayout.xml, the view used to display each item:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    android:id="@+id/MainLayout"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:paddingHorizontal="@dimen/activityHorizontalPadding"
    android:paddingVertical="@dimen/activityVerticalPadding"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <TextView
        android:id="@+id/ItemText"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@+id/CheckImage"
        style="@style/DefaultTextAppearance.Medium.Big" />
    <ImageView
        android:id="@+id/CheckImage"
        android:paddingLeft="12dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintBottom_toTopOf="@+id/HelpText"
        app:layout_constraintRight_toRightOf="parent"
        android:src="@drawable/ic_check_black_18dp"
        android:tint="@color/colorPrimaryDark"
        tools:ignore="ContentDescription" />
</androidx.constraintlayout.widget.ConstraintLayout>

The code I'm using inside the adapter to populate the items, just to showcase how I'm setting the background color of the item itself, depending on if the item is selected or not by the spinner:

public override View GetDropDownView(Int32 position, View convertView, ViewGroup parent)
{
    var view = convertView ?? (parent?.Context == null ? null : LayoutInflater.FromContext(parent.Context)?.Inflate(Resource.Layout.SpinnerItemLineDropLayout, null));

    var mainLayout = view.FindViewById<ConstraintLayout>(Resource.Id.MainLayout);
    var itemTextView = view.FindViewById<TextView>(Resource.Id.ItemText);
    var checkImageView = view.FindViewById<ImageView>(Resource.Id.CheckImage);

    itemTextView.Text = this._items[position].Text.GetText();

    checkImageView.SetImageResource(this.SelectedPosition == position ? Resource.Drawable.ic_check_black_18dp : 0);

    if (this.SelectedPosition == position)
        mainLayout.SetBackgroundColor(Color.ParseColor(view.Context.GetString(Resource.Color.colorControlHighlight)));

    return view;
}

Solution

  • This reason is BackgroundColor of SelectedItem will affect the style of spinner DropDownView. If you not set selected color for item, you will not see this phenomenon.

    If you need to set BackgroundColor for SelectedItem, there is a way to reduce the effect for DropDownView. But this is not a workaround, because it can not fully solve this phenomenon.

    Sample code as follows:

    if (this.SelectedPosition == position)
            
    GradientDrawable gradientDrawable = new GradientDrawable();
    gradientDrawable.SetShape(ShapeType.Rectangle);//shape
    gradientDrawable.SetCornerRadius(16f);//set circle Radius
    gradientDrawable.SetColor(Resource.Color.ripple_material_dark);//background color
    
    mainLayout.SetBackgroundDrawable(gradientDrawable);//set backgroundcolor
    

    Here we use SetBackgroundDrawable to set color, becasue this can set with a shape.

    In addition, you could modify the spinner_background.xml code to reduce their difference:

    <?xml version="1.0" encoding="utf-8" ?>
    <shape
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
      <corners android:radius="8dp" />
      <solid android:color="?android:colorBackground" />
    </shape>
    

    The effect:

    enter image description here

    ============================Update==================================

    As dbalboa's comment, there is another possible solution for this, and also can use SetBackgroundColor method to set color.That is putting <padding android:top="15dip" android:bottom="15dip" /> inside the shape tag of the spinner_background.xml.

    The full code as follows:

    <?xml version="1.0" encoding="utf-8" ?>
    <shape
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
      <corners android:radius="8dp" />
      <padding android:top="5dip" android:bottom="5dip"/>
      <solid android:color="?android:colorBackground" />
    </shape>
    

    Then the effect as follows:

    enter image description here