Building a Custom Component for Android with Kotlin or QML – A Comparison

When creating UIs it’s a common task to build your own set of reusable UI Components, if you dont want to rely on the built-in set of the framework.
I was very curious how this is done in native Android app-development and how this fancy new language Kotlin works in this context.
A quick search brought me to this excellent tutorial from Eley “Building Custom Component with Kotlin”.

So here is the goal:

Create a reusable component which contains a label, an editable textline and a switch, formatted as you can see in the picture below (original picture slightly adjusted).

I will show the lines of code you need to achieve this goal with Native Android + Kotlin on the one hand and Android -styled QML on the other hand.

Version 1: Native Android with Kotlin

For a detailed description please check the original source.
First you need a layout xml file which contains the needed TextView as Title, an EditView a Switch and a Layout to place everything properly:

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/my_title"
        style="@style/custom_component_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <EditText
            android:id="@+id/my_edit"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1" />

        <android.support.v7.widget.SwitchCompat
            android:id="@+id/my_switch"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="end|center"
            android:layout_marginTop="6dp" />
    </LinearLayout>

</merge>

Next we need a class file, written in Kotlin to initiate the visual elements an bring them to live:

package com.elyeproj.myapplication

import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.LinearLayout
import kotlinx.android.synthetic.main.view_custom_component.view.*

class CustomComponent @JvmOverloads constructor(
        context: Context,
        attrs: AttributeSet? = null,
        defStyle: Int = 0,
        defStyleRes: Int = 0
) : LinearLayout(context, attrs, defStyle, defStyleRes) {

    init {
        LayoutInflater.from(context).inflate(R.layout.view_custom_component, this, true)
        orientation = VERTICAL

        attrs?.let {
            val typedArray = context.obtainStyledAttributes(it, R.styleable.custom_component_attributes, 0, 0)
            val title = resources.getText(typedArray
                    .getResourceId(R.styleable.custom_component_attributes_custom_component_title, R.string.component_one))

            my_title.text = title
            my_edit.hint = "${resources.getString(R.string.hint_text)} $title"

            typedArray.recycle()
        }
    }

}

To expose attributes of the component we need a third file, called attrs.xml which is used inside the class constructor.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="custom_component_attributes">
        <attr name="custom_component_title" format="reference" />
    </declare-styleable>
</resources>

Now we are ready to use it in a layout file:

<com.elyeproj.myapplication.CustomComponent
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:custom_component_title="@string/component_one" />

 

Version 2: QML

The entire component consists of the following singe QML file, called “CustomComponent.qml”.

import QtQuick 2.9
import QtQuick.Controls 2.2

Column {
    id: componentRoot

    property alias labelText: label.text
    property alias editText: textField.text
    property alias checked: switchButton.checked
    signal switchClicked(bool checked)

    width: parent.width -6
    height: 60
    x: 6

    Label {
        id: label
        text: "Custom Component"
        font.pixelSize: 16
        font.weight: Font.DemiBold
        opacity: 0.8
    }

    Item {
        width: parent.width
        height: textField.height

        TextField {
            id: textField
            placeholderText: "Type something for " + label.text
            anchors.left: parent.left
            anchors.right: switchButton.left
        }

        Switch {
            id: switchButton
            anchors.right: parent.right
            onClicked: componentRoot.switchClicked(checked)
        }
    }
}

The root class of the component is a Column to layout the content vertically. It contains the Label and a second Item with contains a TextField and a Switch for which I chose the anchors system to set the layout. Three properties of content-controls are exposed easily by using property alias.
Qt creates automatically “on<property>Changed” signals (=events) to make these properties observable from outside. In this case onLabelTextChanged, onEditTextChanged and onCheckedChanged. In addition I have added a new signal (=event) called switchClicked() which is triggered when the user clicks on the switch. This helps to seperate user-inputs from programmatically changes.

The component can be used instantly when it is in the same directory, the filename “CustomComponent” is the class-name. This is the complete code of the test-application:

import QtQuick 2.9
import QtQuick.Controls 2.2

ApplicationWindow {
    id: app
    visible: true
    width: 400
    height: 620

    header: ToolBar {
        Label {
            text: "My Application"
            x: 14
            anchors.verticalCenter: parent.verticalCenter
            font.pixelSize: 16
            font.weight: Font.DemiBold
        }
    }

    Page {

        anchors.fill: parent

        Column {
            y: 6
             width: parent.width

             CustomComponent {
                 labelText: "Custom Component 1"
                 onEditTextChanged: console.log("User typed: " + editText)
             }

             CustomComponent {
                 labelText: "Custom Component 2"
                 checked: true
                 onCheckedChanged: console.log("checked changed to: " + checked)
             }

             CustomComponent {
                 labelText: "Custom Component 3"
                 onSwitchClicked: console.log("User changed checked to: " + checked)
             }

        }
    }
}

When you run the code you are getting this result:

Result

Native Android with Kotlin

  • We have a total of three files (located in different directories)
  • The code has a total of 96 lines and 1813 characters (without space)

QML Version

  • The complete component resides in one file
  • The code has a total of 41 lines and 594 characters (without space)

The QML version is less than 1/3 ! of the native Android/Kotlin version and offers more functionality. And it is fully reusable for IOS and Windows Applications as well, the style changes automatically. Even more important: The complete code is readable and understandable with a glance. There is no need to check different files.

Leave a Comment

*