Week 2: Custom Views in Kotlin — Material Chips with Picasso

Aaron Vontell
365 Days of Coding
Published in
12 min readJan 30, 2018

Today we will talk about creating custom views in Kotlin for Android. More specifically, we will talk about creating a material chip class. In Android, a chip is defined as a “complex entity in the form of a small block, such as a contact.”

Photo credits to Hootsuite

You have likely seen chips before; the image here shows how chips are used as finished entries within an EditText. We won’t get into the intricacies of incorporating our custom ChipView into an EditText, but this functionality can certainly be added to this project.

Requirements / Background

Below are a few topics with which you should be familiar with before completing this tutorial:

  • Familiarity with basic Kotlin code
  • Knowledge in adding libraries to Gradle projects

Motivation

Why would you want to create a custom ChipView in Kotlin? Here are a few reasons why:

  • Display small pieces of information, such as tags, in a stylized by simple way.
  • Get accustomed to creating custom Views in Android, with custom attributes
  • Obtain a small introduction to extensions in Kotlin by incorporating Picasso into our library

Design Principles

There are two principles and guidelines that we will follow in building this module. Note that these principles guide the decisions that we make in the following code, so if the principles of your application are not aligned, make sure to change the respective parts of the code!

  • The API for the ChipView should be incredibly easy to use, with in-xml attribute setting, click listeners, and image loading.
  • The ChipView should be customizable, with the ability to show or hide a profile image, change the text that is displayed, and provide the ability to delete the ChipView.

Coding Time!

Let’s finally get down to business! The code for this lesson will be written in Kotlin, which you can learn more about here.

If you would like to follow along, the full source code can be found here:

1. Creating the Layout

The first step is to create the layout file that we will inflate to display this view. We will create the ChipView as defined by the Material Guidelines specification set by Google.

Dimension spec for chip with remove button
Dimension spec for chips with image and no remove button

We will define one layout which will support all four types of possible chips (just text, text with image, text with remove button, and text with image and remove button). The layout will be defined in the standard XML format, but in a later post we may discuss the use of the Anko library to make these layouts.

First, we should obtain the required remove icon for the chips with delete abilities. The process that I use to obtain these icons usually entails the following steps:

  • Visit this awesome site for Material icons
  • Search “close” and click on the close-circle icon
  • Click the code drop-down (</>) and View Vector Drawable
  • In Android Studio, go to the res/drawable resource folder, right click, and go to New > Drawable resource file. Name it ic_close
  • Copy the Vector Drawable code from the previous steps into the ic_close.xml file. If you click the Preview option in Android Studio, you will notice that you now have a vector image for the close icon! The code is also reproduced below:

Note that we also need to change the fillColor property for this drawable from black (#000) to 54% black (as required from the material specs). 54% for hex is 0x98 (use this site for conversions, as we are essentially looking for 54% of 255 since there are 255 bit options for the alpha color). Using the format #AARRGGBB for our hex color, our final color is #98000000.

Another drawable that we will need to create is a background for the chip. We know that the chip has a height of 32dp, so we will create a custom drawable shape in the form of a rectangle with rounded edges of radius 16dp. In your res/drawable folder, create a file named chip_background.xml (once again, a Drawable resource file), with the following contents:

This drawable is pretty self-explanatory; we create a rectangle with a solid fill (the color is given from the specs), with rounded corners.

A last resource that we will need for this project is a circular ImageView for the left side of the chip. Since creating a circular ImageView from scratch is not as simple as it seems, we will use an awesome library called CircleImageView. In your project’s build.gradle file, add the following dependency and resync your project:

dependencies {
...
compile 'de.hdodenhof:circleimageview:2.2.0'
...
}

We can finally create the layout file! In your res/layout folder, create a new file called view_chip.xml . Paste the following code into the file:

At this point your design preview should show what looks to be a pretty simple chip view:

A preview of the basic ChipView

Note that I set my image src to my app icon; this will be overridden later, so feel free to link in your own image for viewing and debugging purposes for now.

2. Creating the ChipView Class

Now that we have a layout file to inflate the ChipView (within res/layout/view_chip.xml), we can create a custom subclass of View that uses this layout file and provides methods for customizing the information and properties of this ChipView.

Before moving on, make sure that the module that includes your ChipView class has Kotlin Extensions enabled. More specifically, make sure that the following plugin line is included in your module’s Gradle file (build.gradle).

apply plugin: 'kotlin-android-extensions'

2a. Using Custom Attributes

In order to make this ChipView legit, we will want to allow the developer to set attributes for the view directly in XML. For instance, they should be able to do the following:

Essentially we allow them to set the drawable image and text for the ChipView directly within their layout. For the purpose of this tutorial and example, we will add support for three properties:

  • imageSrc: An integer representing the ID of a Drawable resource, to be used as the image on the left of the chip.
  • text: A String or integer representing the ID of a String resource, to be used as the label of the chip.
  • imageURL: A String or integer representing the ID of a String resource, to be used as a URL to load the image from. This will be covered in section 4, Picasso Extensions.

We can add support for these properties through the Custom Attributes system in Android. In your res/values folder, create a attrs.xml file if it does not already exist. Inside this file, you will want to add the following declared style.

Just as it reads, this defines a set of attributes under the ChipView name: an integer for a Drawable resource, a String for text, and a String for a URL.

2b. Creating ChipView.kt

We will not create the actual ChipView class! In your Java folder, make a file called ChipView.kt. To start, we will have this custom View extend LinearLayout (since this is what our custom XML layout file is inherently), and we will implement the constructors that any Android View requires:

Now let’s walk through this code. Note that there are a few variables and methods that I reference in this code that you cannot see yet; bear with me!

First you will notice that we have two constructors that seem to be empty / useless. These two constructors (lines 7 and 8) are just simpler versions of the constructor on line 9, and so we have these constructors call the third constructor.

In the main constructor, the first thing we do is inflate the view_chip.xml file (line 12) that we defined in section 1. It loads the contents of that XML layout file into this View, making the ImageView, TextView, and remove icon available to us through code.

Next, we grab any attributes that were set statically within the instance of the ChipView. We do this by obtaining an object that manages the styled attributes (in other words, this object can dereference ids such as @string/my_string ), and then use the respective attributes.get... in lines 23, 27, and 28 to obtain these set properties. For example, if we had the following view declaration in a layout file:

then line 27 will return a Drawable object with the image referenced by my_chip_image. Note that if we do not define a property in the View instance (such as not defining an imageURL above, then that property is returned as null). We also activate the display of the remove icon, which we will get to soon.

Finally, we recycle the object that we used to grab these attributes, as it is used across multiple services and must be remove properly from our grasp.

Our next step is to define the variables that we introduced above. More specifically, we need to define the text, imageResource, and imageURL variables, as well as a removeListener variable that will hold the listener for when the remove icon is clicked.

Keeping with the spirit of Kotlin, we define public variables, and override their set method so that changes to the variable get properly displayed. The field variable is a special variable that simply refers that specific field that you are setting. The displayText() and displayImage() methods are then called, which properly updates the displayed information within the ChipView. We also have a private variable called removeListener which holds a reference to an interface which we talk about later. If this variable is set, then the listener gets executed when the remove button is clicked.

We will now discuss the methods that provide the logic for displaying the various information that is provided to the ChipView:

You will notice that we use synthetic properties to reference the Views that are contained within the view_chip.xml file (for instance, we use chip_image instead of findViewById(R.id.chip_image) ). In displayText(), we set the TextView label within our ChipView to whatever String is stored within text. Similarly, in displayImage(), we first check if an imageURL is provided (since a URL will take precedence over a set Drawable). If a URL is provided, we call a load method (this method is covered in section 3, so feel free to ignore this for now!). Otherwise, if a Drawable is provided, we set the Drawable for the image within the ChipView. Finally, if neither is provided, we hide the CircleImageView and reset any image that was included.

Note that we always call invalidate() and requestLayout() each time we change parts of the View. This tells the layout/drawing system in Android that a change occurred to the View, and that whatever has been displayed should be updated.

Our third method is displayRemoveIcon(), which shows the icon if a listener is provided. If the listener is provided, the icon also sets it’s onClickListener to call the onRemove() method of the listener. Otherwise, the icon is simply hidden and the onClickListener is set to do nothing.

Now, we talked about this listener many times, but what does it looked like? See below:

An interface is defined which has one method: onRemove(v: View). As seen from the displayRemoveIcon() function earlier, this onRemove function is passed the ChipView when the icon is clicked.

You will notice that the listener can be set in two ways. In the first method, the developer can define an OnChipRemoveListener, which is pretty straightforward (if null is passed, then the listener is removed). In the second method, the developer can instead pass a lambda function or anonymous function, which is called by placing it inside an OnChipRemovedListener. For instance, the following code shows how both would be used on two different instances of a ChipView:

Note that each of these does take in the View representing the ViewChip; however, the lambda function ignores that View (although you can still incorporate it by preceding the code block with { v -> ... )

3. Picasso Extensions - Easy Image Loading

Our current ChipView supports setting a Drawable to display as the image on the left of the chip. However, more often than not it is useful to simply load an image from a URL, rather than a drawable. The Picasso library provides fantastic functionality for loading an image from a URL into an ImageView, and thanks to Pawegio in his blog, Kotlin makes this even easier with extensions! Modifying his code slightly, take a look at the value and extensions below (which you can add right in your ChipView.kt file). Also make sure to add the dependency for Picasso into your project.

dependencies {
...
compile 'com.squareup.picasso:picasso:2.5.2'
...
}

The first line simply defines a picasso object that is attached to the context of this View, which increases efficiency since we won’t have to create a new Picasso object each time we would like to load an image. The function on line 4 is the extension, which essentially adds a function called load to the CircleImageView class. This extension / function takes in a path (the URL to an image), and loads it into that CircleImageView using Picasso. With only 5 or 6 lines of code, we have added functionality to load images from URLs! Our displayImage() method from earlier should now be fully functional, as it uses this load method.

What does the final result look like? Well, putting it all together, you can see the full ChipView.kt class implemented here. If you build an example activity, you can also see the different results of using this class. An example Activity is included here, and the corresponding layout file for that activity is here. This example activity also shows how to add a click listener for the whole ChipView as well! Note that in order for your app to display images loaded online from Picasso, you will need to add internet permissions to your application. In your AndroidManifest.xml file, add the following to your <manifest>:

<uses-permission android:name="android.permission.INTERNET" />
The final result!

4. Last-minute Considerations

There are a few last minute considerations (and some open challenges!) that have not been discussed here, considering that they would add unneeded complexity to a tutorial that is mostly aimed at introducing you to the idea of creating custom views in Kotlin.

  1. What’s up with those margins? You may notice that the margins and padding for the chips are slightly off when not including images or the remove icon. In order to fix this, you can set margins and padding at run-time using a technique show here. Simply add a method such as updateLayout that gets called at the end of each display method which determines which views are being displayed and sets the spacing values accordingly. Feel free to make a pull request with these changes!
  2. What about accessibility? Our chip views are not very accessible, as we don’t do much with describing the content for screen readers. One solution is to set contentDescriptions for our images, possibly by using the ChipView text label as a reference. Feel free to make a pull request with this change!
  3. Support right-to-left displays? Some locales, such as Arabic, have text right to left. In order to handle this, we should swap the positions of the remove icon and chip image. There is actually an easy way to do this; can you find it? Feel free to make a pull request with this change!
  4. MethodBinding for setting listeners? As with most custom views in Android, the listener for the remove icon is added programmatically in Kotlin / Java. However, using a technique called Data Binding, one can actually add these listeners right within the XML file as well. Feel free to make a pull request with this change!

Conclusion

In this tutorial, you learned how to create a custom view in Kotlin, which closely matched the specification of the Material Design Chip. This included the creation of custom view attributes, listeners, and dynamic displays. Along the way, you also learned a little bit about extensions and using Picasso to load images.

I hope that you enjoyed this tutorial, and leave comments with suggestions, questions, and concerns. See you next week!

--

--

Aaron Vontell
365 Days of Coding

Software Engineer @ Instabase, Founder of Vontech Software, LLC | Developer, Aspiring Entrepreneur, MIT MEng ‘18