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 
		( p => p.CornderRadius, 3.0F);   

		

        public new static readonly BindableProperty BackgroundColorProperty =
            BindableProperty.Create
        (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 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.

  4/7/2015 - 7:52 AM