4th Street Bar Hive-Bar

Hive-Bar Powered by Hive beta-fdb5b5b

Community post

Understand and Implement Custom Views in Android - Part 2

Repository:

https://github.com/enyason/custom_view

https://i.imgur.com/puH4Dfh.png

Repository

https://github.com/enyason/custom_view

What Will I Learn?

  • How custom views work
  • How to extend a built-in view
  • How to build a compound control

Requirements

  • System Requirements: Java JDK, Android Studio
  • OS Support for Java: Windows, Mac OS, Linux
  • Required Knowledge: A fair knowledge of Java, OOP and Android Studio

Resources for Java and this tutorial

Difficulty : Beginner

Tutorial Duration 25- 30 Minutes

Tutorial Content

In the first part of this tutorial series, we considered a custom view, what it means and how to implement it on android. We started off by extending a simple text view that displayed a custom icon and a text. For this second part of the series, we are going to implement a compound control.

What is a compound control?

  • A group of views often with additional functionalities
  • Modularize code into custom compound control with this, you can reuse the same functionality

To delve further into compound control, we are going to implement a ListTile. The list tile will have 3 views: A trailing icon, A leading icon, and a title. see image below for clarity.

![ListTile.png](https://cdn.steemitimages.com/DQmTU8GGBBxBViCm6zA8VXYBgHkYbxNudnRC4qNdGC8C1Jd/ListTile.png)

Step 1 : Create an XML layout

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout

    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="wrap_content"
    >

<ImageView
        android:id="@+id/trailing"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_margin="5dp" />

    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_toRightOf="@id/trailing"
        android:textSize="18sp" />

    <ImageView
        android:id="@+id/leading"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_margin="5dp" />


</RelativeLayout>

This XML code creates our ListTile layout as shown below. The image views for displaying the trailing and leading icons

![ListTile.png](https://cdn.steemitimages.com/DQmTU8GGBBxBViCm6zA8VXYBgHkYbxNudnRC4qNdGC8C1Jd/ListTile.png)

Step 2: Define custom attributes for Listile

This attributes will be tied up to the listTile view so it can be referenced from XML

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="ListTile">

        <attr name="title" format="string" />
        <attr name="leading" format="reference" />
        <attr name="trailing" format="reference" />

    </declare-styleable>

</resources>

We add the required attributes in the declare-styleable tag using the attr tag. The name property of the attr tag defines a label for the attribute while the format property defines the Type of the attribute. String format defines a Type for text while reference defines a Type for resource files e.g a drawable. Note: The name property of the declare-styleable tag should have a value equal to the custom view class name created.

Step 3 : Create a Java Class ListTile.java

public class ListTile extends RelativeLayout {

    //define android views
    private ImageView trailing,leading;
    private TextView title;

    public ListTile(Context context) {
        super(context);
        init(context,null);
    }

    public ListTile(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context,attrs);

    }

    public ListTile(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context,attrs);

    }


    void init(Context context,AttributeSet attrs) {


        Drawable leadingIcon,trailingIcon;
        String text;

        //check for if AttributeSet is null
        if (attrs ==null) {

            return;
        }


        //get attributes from attrs.xml into array
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ListTile);

        //extract attributes from array into the variables
        leadingIcon = typedArray.getDrawable(R.styleable.ListTile_leading);
        trailingIcon = typedArray.getDrawable(R.styleable.ListTile_trailing);
        text = typedArray.getString(R.styleable.ListTile_title);


        //inflate layout
        LayoutInflater inflater = LayoutInflater.from(getContext());
        View tile= inflater.inflate(R.layout.list_tile,this);

        tile.setBackground(getResources().getDrawable(R.drawable.background));

        //find views
        trailing = findViewById(R.id.trailing);
        leading = findViewById(R.id.leading);
        title = findViewById(R.id.title);

        //set view properties
        trailing.setImageDrawable(trailingIcon);
        leading.setImageDrawable(leadingIcon);
        title.setText(text);

        invalidate(); // redraw ui


    }

}
  • The ListTile extends a RelativeLayout and overrides 3 constructors, passing the current context and attributes to the init function

  • We get the attributes defined in our attrs.xml file and store in a TypedArray. A Typed array is a Container for an array of values that were retrieved from XML.

  • We inflate the layout we defined for our custom view that's list_tile.xml. This is done using the layout inflater. This is similar to when we call setContentView in onCreate method of an Activity

  • We call setBackground on the tile view and pass a drawable from XML to it. This drawable marks the Tile border with a specified color. see code snippet for code

    <?xml version="1.0" encoding="utf-8"?>
    <shape
        android:shape="rectangle"
        xmlns:android="http://schemas.android.com/apk/res/android">
    
        <solid android:color="#fff"/>
        <stroke android:width="1dp" android:color="#cacaca"/>
        
    </shape>
    
    • Basically, I created an XML file in the drawable folder,
    • Create a basic rectangular shape
    • define the needed properties with the appropriate values
  • Get a reference to the views defined in the list_tile.xml

  • set view properties from the attributes specified in XML

  • call invalidate();. This method call will redraw the UI to reflect changes made in XML immediately

Step 4 : Use ListTile in MainActivity Layout file

With our ListTile ready, we can use it now in our XML.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout

    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"
    android:orientation="vertical"
    tools:context=".MainActivity">


    <com.enyason.custom_view_1.ListTile
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:leading="@drawable/ic_navigate_next"
        app:title="User profile"
        app:trailing="@drawable/ic_person" />


</LinearLayout>

The code snippet above shows the use of the custom view created in the activity_main.xml file. Running the app gives the output show in the image below.

![tile_single.png](https://cdn.steemitimages.com/DQmVVsEZ85AinSLvW5Rzdxnp26BXPKqMjhceFbfwhQ8TypU/tile_single.png)

Step 5 : Using the Merge Tag

<merge/> is useful because it can get rid of unneeded ViewGroups. In our list_tile.xml file, we have the root element as RelativeLayout which is inflated in the Listile.java class. With this class extended the Relative layout, we now have a duplicate view group. To see this duplication we'll use the Android Layout Inspector

  • Navigate to Layout Inspector from tools, from the tools bar
![Screenshot 2.png](https://cdn.steemitimages.com/DQmQkpUMbjasQoKsfnt6VYjertddTTsDggXgFRAk8mAqm6i/Screenshot%202.png)
  • Select the process from the device connected
![Screenshot 3.png](https://cdn.steemitimages.com/DQmTLauKeAM21AgirFzbArxhVzupJdhbqK7eHuuHiNBqD4a/Screenshot%203.png)
  • From the image below you see we have a Listile view which is a RelativeLayout and another redundant Relative Layout
![Screenshot 1.png](https://cdn.steemitimages.com/DQmeSUS7oAVjKMAQfShy2KTJBeErngbCTKorfdmDnhJzUbP/Screenshot%201.png)
  • To get rid of the unneeded view group, use the merge tag in list_tile.xml as shown in the code snippet below

    <?xml version="1.0" encoding="utf-8"?>
    <merge
        xmlns:android="http://schemas.android.com/apk/res/android"
           >
    
    <ImageView
            android:id="@+id/trailing"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_margin="5dp" />
    
        <TextView
            android:id="@+id/title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_toRightOf="@id/trailing"
            android:textSize="18sp" />
    
        <ImageView
            android:id="@+id/leading"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_margin="5dp" />
    
    
    </merge>
    
  • Checking the View hierarchy again, the redundant view group has been removed.

![Screenshot with merge tag.png](https://cdn.steemitimages.com/DQmXjrfGHHvLcUk6sfm71E8KtBXGDuB1K6DRJCpWW9s1Wej/Screenshot%20with%20merge%20tag.png)

UseCase

With the Listile created we should be able to make screens such as one below easily

![tile_multiple.png](https://cdn.steemitimages.com/DQmd9FGvvnYATtBUUMk4naG42JMQWhNArQ2DSiSax3svZeD/tile_multiple.png)

Curriculum

Proof of Work

The complete source code can be found on gitHub https://github.com/enyason/custom_view

127 upvotes $7.37

Replies (5)

Review before signing

Posting as . Signing with . Keychain permission: Posting. Hive Keychain will ask you to approve this action next.


  
Technical details

Operation fingerprint: