Xamarin.Forms Android CardView

When Google introduced Material Design for Android they introduced a new view called CardView. Xamarin.Forms doesn’t have support for CardView by default but you can easily create your own view that renderers a CardView on Android.

First step is to create a Xamarin.Forms control in your shared project.

public class CardContentView : ContentView
{
 
 
		public static readonly BindableProperty CornerRadiusProperty = 
			BindableProperty.Create<CardContentView,float> 
		( p => p.CornderRadius, 3.0F);   
 
 
 
        public new static readonly BindableProperty BackgroundColorProperty =
            BindableProperty.Create<CardContentView, Color>
        (p => p.BackgroundColor, Color.White);
 
        public float CornderRadius
        {
            get { return (float)GetValue(CornerRadiusProperty); }
            set { SetValue(CornerRadiusProperty, value); }
        }
 
        public new Color BackgroundColor
        {
            get { return (Color)GetValue(BackgroundColorProperty); }
            set { SetValue(BackgroundColorProperty, value); }
        }
 
		protected override SizeRequest OnSizeRequest (double widthConstraint, double heightConstraint)
		{
			if (Content == null)
				return new SizeRequest(new Size(100, 100));
 
			return Content.GetSizeRequest (widthConstraint, heightConstraint);
		}
	}

Next step is to create a custom renderer for Android, on iOS and Windows it will be rendered as a regular ContentView.
You need to add a NuGet package named Xamarin.Android.Support.v7.CardView to get the Android CardView. The renderer will inherit form CardView and implement the IVisaulElementRenderer interface. To get card shadow on both Android KitKat and Android Lollipop you have to set UseCompatPadding to true. You have to remove all view from the ViewGroup, if you don’t do that you will get problems when you using the control in a ListView. The Load method on the VisualElementPackager will load content in to the Card.

[assembly:ExportRendererAttribute(typeof(CardContentView), typeof(CardViewRenderer))]
namespace CardViewFormsAndroid
{
	public class CardViewRenderer : CardView, 
		IVisualElementRenderer
	{
		public CardViewRenderer () : base (Forms.Context)
		{
		}
 
		public event EventHandler<VisualElementChangedEventArgs> ElementChanged;
 
		bool init;
		ViewGroup packed;
		public void SetElement (VisualElement element)
		{
			var oldElement = this.Element;
 
			if (oldElement != null)
				oldElement.PropertyChanged -= HandlePropertyChanged;
 
			this.Element = element;
			if (this.Element != null) {
 
				this.Element.PropertyChanged += HandlePropertyChanged;
			}
 
            ViewGroup.RemoveAllViews();
				//sizes to match the forms view
				//updates properties, handles visual element properties
				Tracker = new VisualElementTracker (this);
 
            Packager = new VisualElementPackager(this);
            Packager.Load();
 
            UseCompatPadding = true;
 
            SetContentPadding((int)TheView.Padding.Left, (int)TheView.Padding.Top,
                   (int)TheView.Padding.Right, (int)TheView.Padding.Bottom);
 
                Radius = TheView.CornderRadius;
                SetCardBackgroundColor(TheView.BackgroundColor.ToAndroid());
 
			if(ElementChanged != null)
				ElementChanged (this, new VisualElementChangedEventArgs (oldElement, this.Element));
		}
 
 
 
 
		public CardContentView TheView
		{
			get { return this.Element == null ? null : (CardContentView)Element; }
		}
 
 
		void HandlePropertyChanged (object sender, System.ComponentModel.PropertyChangedEventArgs e)
		{
			if (e.PropertyName == "Content") {
 
                //Packager.Load();
 
                Tracker.UpdateLayout();
            } else if (e.PropertyName == CardContentView.PaddingProperty.PropertyName) {
				SetContentPadding ((int)TheView.Padding.Left, (int)TheView.Padding.Top,
					(int)TheView.Padding.Right, (int)TheView.Padding.Bottom);
			} else if (e.PropertyName == CardContentView.CornerRadiusProperty.PropertyName) {
				this.Radius = TheView.CornderRadius;
			} else if (e.PropertyName == CardContentView.BackgroundColorProperty.PropertyName) {
				if(TheView.BackgroundColor != null)
				SetCardBackgroundColor (TheView.BackgroundColor.ToAndroid ());
 
			}
		}
 
		public SizeRequest GetDesiredSize (int widthConstraint, int heightConstraint)
		{
			packed.Measure (widthConstraint, heightConstraint);
 
			//Measure child here and determine size
			return new SizeRequest (new Size (packed.MeasuredWidth, packed.MeasuredHeight));
		}
 
		public void UpdateLayout ()
		{
			if (Tracker == null)
				return;
 
			Tracker.UpdateLayout ();
		}
 
		public VisualElementTracker Tracker {
			get;
			private set;
		}
 
        public VisualElementPackager Packager
        {
            get;
            private set;
        }
 
        public Android.Views.ViewGroup ViewGroup {
			get{ return this; }
		}
 
		public VisualElement Element {
			get;
			private set;
		}
 
 
    }
}

You can browse the complete code with a working example on GitHub, https://github.com/dhindrik/Xamarin.Forms-Awesome-Controls
The repository is a fork from James Montemagno’s repository for awesome Xamarin.Forms controls. What I have done is that I have changed a few things so can have layouts as content, a StackLayout for example.

6 thoughts on “Xamarin.Forms Android CardView

  1. Ivana says:

    Why did you implement IVisualElementRenderer, but not the generic ViewRenderer?
    I’ve tried to subclass my custom card view renderer with ViewRenderer, but it does not load controls inside the card.

    • Daniel Hindrikes says:

      When I create a VisualElementTracker I need to pass an object of type IVisualElementRenderer to it.

  2. Bahram says:

    Hi Daniel
    I am trying to use CardContentView.cs class in Xaml. I have added ” xmlns:CardContentView=”clr-namespace:Articap.ProjectW.MobileApp.Views;assembly=Articap.ProjectW.MobileApp” ” but I get error
    ” type CardContentView .CardContentView not found in xmlns http://xamarin.com/schemas/2014/forms

    I have added CardContentView in my Xamarin.forms shared project and also added CardViewRenderer for Android .
    could you please help me to solve this problem?

    • Daniel Hindrikes says:

      You don’t have to use it anymore while the Frame control will render a CardView now if you are running the latest version of Xamarin.Forms. So I guess that is the best way to use CardView.

      I hard to help you to get it working when I don’t see your complete code.

  3. Faizal Saidali says:

    Hi Daniel Hindrikes,
    Thank you for your awesome post. I have implemented this custom render. It pretty awesome.

    Still, I have an issue.
    In my homepage, I have use Custom Card View. When I navigate to another page, then return homepage. When popup an error message as “‘jobject’ must not be IntPtr.Zero.
    Parameter name: jobject”.
    Please help me.
    Thank you Faizal

Leave a Reply

Your email address will not be published. Required fields are marked *