Making Custom View Components in Android
February 26, 2014Making your own custom view in an Android app isnât too hard to do but it requires knowledge of some sparsely documented features of Android development.
Weâll walk through how to create an image button with a caption.
Creating the Layout
A captioned button is made up of two components, an ImageButton and a TextView below it. Start by making a layout file in â/res/layout/â called âcaption_button.xmlâ with a vertical LinearLayout that contains these two components:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/linearLayout"> <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/imageButton_icon"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Caption" android:id="@+id/textView_caption" android:gravity="center_horizontal"/> </LinearLayout
Alternatively, if you wanted to make a captioned image, use an ImageView instead of an ImageButton.
Custom Component Class
Now weâll make a class called âCaptionButtonâ that will handle the functionality and customization of the custom component. Since the top-level view of our custom component is a LinearLayout, CaptionButton will be a child class of LinearLayout.
public class CaptionButton extends LinearLayout { ImageButton imageButton_icon; TextView textView_caption; public CaptionButton(Context context) { super(context); } public CaptionButton(Context context, AttributeSet attrs) { super(context, attrs); LayoutInflater inflater = LayoutInflater.from(context); inflater.inflate(R.layout.caption_button, this); imageButton_icon = (ImageButton) findViewById(R.id.imageButton_icon); textView_caption = (TextView) findViewById(R.id.textView_caption); } }
What have we done here? Weâve inflated the caption_button.xml layout and declared and assigned our ImageButton and TextView from our caption_button.xml layout.
Custom Attributes
When creating an XML layout with our custom component, weâre going to want to declare two custom attributes: the Drawable icon for the button and the String for the caption. When using our custom component in some other XML layout, it will look something like this:
<com.yourpackagename.CaptionButton android:layout_width="wrap_content" android:layout_height="wrap_content" my_app:button_icon="@drawable/message_icon" my_app:caption="@string/message_caption"/>
Note that custom attributes will need to use a custom namespace, so in a parent element, remember to declare
xmlns:my_app="http://schemas.android.com/apk/res-auto"
If your project doesnât already have one, create a file called âattrs.xmlâ in your â/res/values/â folder, and add this to the file:
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="CaptionButton"> <attr name="button_icon" format="reference" /> <attr name="caption" format="reference|string" localization="suggested" /> </declare-styleable> </resources>
Here weâve created a custom styleable named âCaptionButtonâ with two attributes, âbutton_iconâ and âcaptionâ. Letâs break down whatâs happening on each of these lines.
<declare-styleable name="CaptionButton">
The name of our custom styleable doesnât need to be âCaptionButtonâ. It can be anything, but âCaptionButtonâ easily lets us know what custom component these attributes will be applied to. If we were to reuse these attributes on other custom components, we would select a more general name.
<attr name="button_icon" format="reference" />
For the âbutton_iconâ attribute, when we want an attribute to refer to a resource in our project, such as a Drawable resource in this case, we use âformat=âreferenceââ.
<attr name="caption" format="reference|string" localization="suggested" />
For the âcaptionâ attribute, we can use either a reference to a String resource in our project, such as â@string/message_captionâ or a literal String, such as âMessageâ, hence the use of âformat=âreference | stringââ. Use of âlocalization=âsuggestedââ indicates that if a resource is retrieved, it should use the properly localized resource. |
Retrieving Custom Attributes and Applying them to the Custom Component
Now that weâve declared our custom attributes, hereâs how we retrieve them and apply them to our custom component.
public CaptionButton(Context context, AttributeSet attrs) { super(context, attrs); LayoutInflater inflater = LayoutInflater.from(context); inflater.inflate(R.layout.caption_button, this); imageButton_icon = (ImageButton) findViewById(R.id.imageButton_icon); textView_caption = (TextView) findViewById(R.id.textView_caption); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CaptionButton); Drawable icon = a.getDrawable(R.styleable.CaptionButton_button_icon); String caption = a.getString(R.styleable.CaptionButton_caption); a.recycle(); setIcon(icon); setCaption(caption); } public void setIcon(Drawable icon) { imageButton_icon.setImageDrawable(icon); } public void setCaption(String caption) { textView_caption.setText(caption); }
We use a TypedArray in the CaptionButton constructor that uses an AttributeSet to retrieve what values were given to the two custom attributes in a CaptionButton XML declaration. The Drawable reference from âmy_app:button_icon=â@drawable/message_iconââ is retrieved and assigned to the icon field, and the String from âmy_app:caption=â@string/message_captionââ is retrieved and assigned the caption field.
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CaptionButton); Drawable icon = a.getDrawable(R.styleable.CaptionButton_button_icon); String caption = a.getString(R.styleable.CaptionButton_caption);
After retrieving the attributes from the TypedArray, donât forget to call
a.recycle();
Once the attributes have been retrieved, we apply each of them to individual Views in our CaptionButton, the ImageButton and the TextView.
setIcon(icon); setCaption(caption); public void setIcon(Drawable icon) { imageButton_icon.setImageDrawable(icon); } public void setCaption(String caption) { textView_caption.setText(caption); }
We will also want to override the click listener for this custom component and apply it to the ImageButton.
@Override public void setOnClickListener(OnClickListener onClickListener) { imageButton_icon.setOnClickListener(onClickListener); }
And there we have it, our very own custom component.
The code from this post can be found in this Github Gist.
Looking for more like this?
Sign up for our monthly newsletter to receive helpful articles, case studies, and stories from our team.
![Three principles for creating user-friendly products](https://gratis-creeper.transforms.svdcdn.com/staging/blog/Three-principles.jpg?w=696&h=320&q=75&auto=format&fit=crop&dm=1708017686&s=4b6b726c71c183e97216b75f1ab9e0ec)
Three principles for creating user-friendly products
January 25, 2023Grayson discusses three guiding principles he utilizes when designing user experiences for products.
Read more![MichiganLabsâ approach to product strategy: Driving software success](https://gratis-creeper.transforms.svdcdn.com/staging/blog/approach-to-product-strategy-thumbnail.png?w=1200&h=1200&auto=compress%2Cformat&fit=crop&dm=1708017425&s=fcad451dff0b676c36748aae877710cc)
MichiganLabsâ approach to product strategy: Driving software success
February 12, 2024 Read more![Quickly Prototyping a Ktor HTTP API](https://gratis-creeper.transforms.svdcdn.com/staging/blog/f3f5fd00-c59e-11e9-8284-cb297d193133.png?w=696&h=320&q=75&auto=format&fit=crop&dm=1708017498&s=388f0d632321397de9cdceab45e2cba7)
Quickly Prototyping a Ktor HTTP API
August 18, 2022Whether itâs needing a quick REST API for a personal project, or quickly prototyping a mockup for a client, I like to look for web server frameworks that help me get up and running with minimal configuration and are easy to use. I recently converted a personal projectâs API from an Express web server to a Ktor web server and it felt like a breeze. Iâll share below an example of what I found and how easy it is to get a Ktor server up and running.
Read more