Android Native - Extending a Framework View

Updated dimitrilc 2 Tallied Votes 105 Views Share

Introduction

Among the many ways to create your own View, extending an existing View is the simplest option. This is because you are inheriting pre-made attributes, drawing, and accessibility features.

In this tutorial, we will learn how to create our own custom View by extending an existing View class from the Android framework.

Goals

At the end of the tutorial, you would have learned:

  1. How to extend an Android View.

Tools Required

  1. Android Studio. The version used in this tutorial is Bumblebee 2021.1.1 Patch 3.

Prerequisite Knowledge

  1. Basic Android.

Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.
  2. Remove the default TextView from activity_main.xml.

Extend a View in Kotlin/Java

The first thing that we need to do in order to extend a framework View is to declare it in source code.

The class that we want to extend for this tutorial is the Switch class (android.widget.Switch), so follow the steps below to extend Switch.

  1. Create a new class called MySwitch using the code below.

     class MySwitch(context: Context) : Switch(context)
  2. The compiler will complain that you should use SwitchCompat instead, which is what you should actually do in real code. It does not really matter which Switch class to use for this tutorial, so you can simply suppress the warning by adding the @SuppressLint("UseSwitchCompatOrMaterialCode") annotation to MySwitch.

     @SuppressLint("UseSwitchCompatOrMaterialCode")
     class MySwitch(context: Context) : Switch(context)
  3. The code may look correct for now, but if we try to use MySwitch now, the program will crash. This is because at the minimum, we need to call the super(Switch) constructor that takes both a Context and an AttributeSet. Modify MySwitch to call the super constructor with both context and attrs (AttributeSet).

     @SuppressLint("UseSwitchCompatOrMaterialCode")
     class MySwitch(context: Context, attrs: AttributeSet) : Switch(context, attrs)

Use MySwitch in a layout file

To be able to use MySwitch in an xml file, we can use its FQCN as the XML element tag. There are two ways to add MySwitch to an XML, creating it by hand or with the Palette in the Design surface. Assuming that we want to add MySwitch via the Palette, follow the steps below.

  1. Open activity_main.xml in the Design surface.

  2. Under Palette, find Project.

  3. Under Project, drag MySwitch under ConstraintLayout.
    1.png

  4. And then simply constraint MySwitch to the center of the screen.

  5. activity_main.xml should now look like the code below.

     <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <com.example.daniwebandroidnativeextendingframeworkview.MySwitch
            android:id="@+id/mySwitch"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.5"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
     </androidx.constraintlayout.widget.ConstraintLayout>

Even though we have not provided any custom behavior for the switch, this is basically all you need to do to extend a framework View.

Add Custom Attributes

Now that we have our custom View, we can also add custom attributes and access them as well. Follow the steps below to add a custom attribute.

  1. Create a new file called attrs.xml under values.

  2. Add a <declare-stylable> element inside of <resources> with the name of MySwitch.

     <?xml version="1.0" encoding="utf-8"?>
     <resources>
        <declare-styleable name="MySwitch">
        </declare-styleable>
     </resources>
  3. Your custom attributes will go inside of the <declare-stylable> element.

  4. Each custom attribute is created using the <attr> element. You can provide it any name you like. A name is required.
    Add a new customAttribute like the code below.

     <attr name="customAttribute" format="string"/>
  5. There does not seem to be any documentation available for the type of the format, but you should be fine to use any of the suggested types in the screenshot below.

2.png

Use the custom attribute in XML

To use the custom attribute in XML, we can just use the xmlns:app="http://schemas.android.com/apk/res-auto" namespace. In the MySwitch element in activity_main.xml, add the customAttribute like below.

<com.example.daniwebandroidnativeextendingframeworkview.MySwitch
   android:id="@+id/mySwitch"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   app:customAttribute="test"
   app:layout_constraintBottom_toBottomOf="parent"
   app:layout_constraintEnd_toEndOf="parent"
   app:layout_constraintHorizontal_bias="0.5"
   app:layout_constraintStart_toStartOf="parent"
   app:layout_constraintTop_toTopOf="parent" />

Now, you can access the value of the attribute in the MySwitch like below.

init {
   context.theme.obtainStyledAttributes(
       attrs,
       R.styleable.MySwitch,
       0, 0
   ).use {
       it.getString(R.styleable.MySwitch_customAttribute)
   }
}

Keep in mind that the TypedArray object must be closed after accessing. Fortunately, it implements AutoClosable, so we can just open it with the use function.

Solution Code

MySwitch.kt

@SuppressLint("UseSwitchCompatOrMaterialCode")
class MySwitch(context: Context, attrs: AttributeSet) : Switch(context, attrs){
   init {
       context.theme.obtainStyledAttributes(
           attrs,
           R.styleable.MySwitch,
           0, 0
       ).use {
           it.getString(R.styleable.MySwitch_customAttribute)
       }
   }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   tools:context=".MainActivity">

   <com.example.daniwebandroidnativeextendingframeworkview.MySwitch
       android:id="@+id/mySwitch"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       app:customAttribute="test"
       app:layout_constraintBottom_toBottomOf="parent"
       app:layout_constraintEnd_toEndOf="parent"
       app:layout_constraintHorizontal_bias="0.5"
       app:layout_constraintStart_toStartOf="parent"
       app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
   <declare-styleable name="MySwitch">
       <attr name="customAttribute" format="string"/>
   </declare-styleable>
</resources>

Summary

We have learned how to extend an existing Android View in this tutorial. The full project code can be found at https://github.com/dmitrilc/DaniwebAndroidNativeExtendingFrameworkView

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.