If you have been doing layouts for a while now, you must be familiar with the uneasiness that comes with creating layouts, more specifically creating the same layout over and over again for different application components. The general idea for eliminating such a situation is to create a layout template that could be used for multiple components with smaller modifications, which in most of the cases, is the data.
In this post, we will cover a similar situation -- "How to make cards for different components". However, instead of discussing the problem, we will be focusing on its solution -- CardView. We will assume you have at least basic knowledge of the how-tos of Android development.
CardView was announced by Google during its yearly I/O conference in 2014 as a part of Material Design components. The main purpose CardView serves in the Material Design universe is to provide a unified look to all of the card based UIs making it easier for developers to create seamless interfaces.
In this post, we will see what opportunities CardView provides on Android platform and how to use it properly in our application.
If you look closely at the image above, you will notice a white card like view container holding an ImageView, a TextView and an ImageButton. This is our CardView. As I already mentioned, a CardView is a view container or ViewGroup that inherits its nature from FrameLayout which further inherits from ViewGroup itself. The only thing that separates a CardView from any other ViewGroups is its nature to behave like a card, more specifically the shadow and rounded corners. One could argue that such properties can be added to other viewgroups as well, but you will be surprised to know how well CardView behaves on Android versions before Lollipop, something difficult to pull off with custom VGs.
The basic customizability that a CardView provides includes CornerRadius, Elevation, MaxElevation, ContentPadding, CompatPadding, PreventCornerOverlap, and a dedicated CardBackgroundColor. The CardView in the image above with all of its customizability enabled can be created using this piece of XML code. Take a look, try to figure out what happens with each of the properties, why is there a ViewGroup inside our CardView viewgroup or simply continue reading to find out.
|android:text="Joshua Sortino - Via Unsplash"|
With the release of Android Lollipop, Google started separating Android Components like RecyclerView and CardView from support library with their own repositories. CardView library can be added to a project by adding the following dependency to dependencies in build.gradle (module: app),
If using Android Studio 3.0 and above, then this needs to be added instead,
I know I did say a CardView is a ViewGroup but it inherits from FrameLayout which is the simplest form of ViewGroup created to handle a single frame at a time. That's why we need a RelativeLayout to hold all of our views together while acting like a single frame to the CardView.
The most straightforward property of all. By default, a CardView comes with white card background color which can be modified as per our need to any other color using this property. Sample declaration of cardBackgroundColor (white) in XML and Java looks like this,
Card Corner Radius gives the CardView a rounded corner of radius equal to the value provided to it. On Android Lollipop and above, it simply clips the child views that may be intersecting CardView's rounded corners. It also ensures that a padding is maintained between corners and child views on previous versions to avoid expensive clipping of views. We will soon see how we can enable it. Sample code for 0 corner radius:
Card Elevation property simply elevates the cardview on the z-axis to a height equal to the passed value. Since elevation came with Android Lollipop, Card Elevation on previous versions uses shadows to create a similar effect. It adds padding on its content's sides using
maxCardElevation + (1 - cos45) * cornerRadius
and on top and bottom using
maxCardElevation * 1.5 + (1 - cos45) * cornerRadius
and then applies shadows to that space. MaxCardElevation property, if not set manually, will be equal to CardElevation. In our example, we have set elevation to 0.7dp and maxElevation to 1dp. If you look at the Java part, you'll notice the values passed to the methods are different than the values to xml properties. This is because we can specify nature of value with xml but not with java methods which expect the value to be in pixels (px) only and thus we have to pass number of pixels that make up the same dp as xml values.
As previously mentioned, the normal CardView padding is used to create a space for drawing shadows and thus we use ContentPadding to add padding between the edges and child views. As it's with normal view padding, ContentPadding can be used to selectively add padding on any of the four edges. In our example, we have used content padding to set padding on all the edges to 10dp and then content padding bottom to 0dp to make sure there is no padding at the bottom. As usual, the Java method needs values for all edges to be passed in pixels. The first parameter of setContentPadding represents left edge, with the last representing bottom.
cardView.setContentPadding(30, 30, 30, 0);
One more attribute related to padding. Since CardViews use padding for drawing shadows on platforms before Lollipop, the content area will change on platforms before and after Lollipop. To make sure the content area remains the same on all the platforms, Compat Padding is used which simply adds inner padding on platforms Lollipop and after.
As mentioned in Corner Radius, the CardView avoids clipping the child views on platforms before Lollipop due to expensive nature of clipping and rather gives an option to distance content from rounded corners with Prevent Corner Overlap. The definition in XML and Java looks like this,
Okay, now that we have discussed about attributes and their method counterparts, let's see what attribute namespaces we get with CardView. There are actually 2 namespaces that hold the attributes of CardView. The first is the normal android namespace which holds all of the inherited attributes for CardView like width or height or scale. The other one is the app namespace, formerly card and card_view, which holds all the exclusive CardView attributes we discussed just now. It's often easier to look for special attributes using app namespace.
The complete Java representation of the CardView's XML layout above looks like this,
|CardView cardView = (CardView) findViewById(R.id.cardView);|
|cardView.setContentPadding(30, 30, 30, 0);|
The following code does the same as the XML does, same elevation and stuff. The only difference with code is that the methods want the values to be passed in pixels (px) instead of dp or px or mm. This causes a problem on phones with different pixel density and the required value has to be calculated for every device and then converted to px using dpToPx(...) method using device density. Here
will give the same result as
on a Nexus 5 where the pixel density is 3.
That's it. We now know what a CardView is, what's the advantage using it, how to correctly use it on different Android versions without making it look different on Lollipop or above and v20 or below. We now also have the idea of different attributes we are supposed to use with a CardView and how they perform on different platforms. Also, knowing the namespaces will help us get better at identifying attributes for our CardView.
If we missed out on any of the CardView features or there's anything to correct, share it with us in comments below.