Create Android Custom View

Keywords: Android xml SDK

Any development should not be simply the stacking of interface views, even the stacking should be orderly and reusable. Let's show you how to implement a custom view.

Inherit a View

All view classes defined in Android are inherited from View. Your custom view can also directly inherit View, but in this case you need to implement a lot of code already implemented in Android SDK. Time is precious, so it's most appropriate to implement what we need on the basis of others'implementation. For example, if we want to change a button, we can inherit Button directly. But as an illustration, we inherit View to implement a simple pie chart.

class PieChart : View {
  private var _context: Context ? = null

  constructor(context: Context) : super(context) {

  }

  constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
    this._context = context 
  }

  constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {

  }
}

This is just a simple example. We need to define a PieChart view, which inherits View. Three constructors are given.

Add custom attributes

To add a built-in view to the interface, you only need to define the corresponding interface and behavior attributes in the layout of XML. A well-defined view can also be defined and style added in XML. To achieve this effect, you need:

  1. Add the properties of the custom view in <declare-styleable>.

  2. Define the values of these attributes in the XML layout file.

  3. Get the property value at run time.

  4. Use these properties in custom views.

Add a resource file to store the properties of the custom view. The path is res/values/attrs.xml. The attrs.xml file is as follows:

<resources>
    <declare-styleable name="PieChart">
        <attr name="showText" format="boolean" />
        <attr name="labelPosition" format="enum">
            <enum name="left" value="0" />
            <enum name="right" value="1" />
        </attr>
    </declare-styleable>
</resources>

The above code defines two custom attributes: showText and labelPosition. And both attributes belong to nodes named PicChart. Normally the name of this node is the name of your custom view, but you can also do otherwise.

Let's look at how the defined attributes can be used.

<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"
    tools:context="test.demo.myapplication.customViews.CustoViewmActivity">

    <test.demo.myapplication.customViews.PieChart
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:labelPosition="left"
        app:showText="true"/>

</LinearLayout>

Using a custom View directly in an XML layout is such a test.demo.myapplication.customViews.PieChart. If there is an internal class PieView to use in PieChart, that's it: test.demo.myapplication.customViews.PieChart$PieView. Divide it with $

Use custom properties

The attributes we used in the previous XML layout file are read from the bundle of the resource file and passed into the View constructor as an AttributeSet instance.

Although AttributeSet data can be read directly, this is not recommended. Typically, AttributeSet is passed into the obtainStyledAttributes() method and the return value TypedArray of this method is used. Such as:

  constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
    this._context = context
    var a = context.theme.obtainStyledAttributes(attrs, R.styleable.PieChart, 0, 0)
    try {
      _showText = a.getBoolean(R.styleable.PieChart_showText, false) as Boolean
    } finally {
      a.recycle()
    }
  }

Note: TypedArray is a shared resource and must be recycled after use.

Add attributes and events

Attributes are an effective way to control view behavior and view surroundings, but after view initialization, it can only be read. To provide dynamic behavior, expose a getter and a setter for attributes. The following code shows how PieChart exposes showText attributes:

  fun isShowText():Boolean {
    return _showText
  }
  
  fun setShowText(showText:Boolean) {
    _showText = showText
    invalidate()
    requestLayout()
  }

Notice that setShowText calls invalidate() and requestLayout(). These calls are the guarantees of view dynamic behavior. After changing the attributes of a view, you have to invalidate the view to change its appearance. These methods are used to notify the system view that it needs to be redrawn. Similarly, if changes in attributes affect the appearance of the view, such as size or shape, you also need to request a new layout. If these methods are not invoked, there will be bug s that are hard to find.

Custom views also support adding events. For example, PieChart exposes a custom event, OnCurrentItemChanged. This event can be used to notify the listener user that the block of interest has changed. See what the code looks like:

class PieChart : View {
  public interface OnCurrentItemChangedListener {
    fun OnCurrentItemChanged(source: PieChart, currentItem: Int)
  }

  fun setCurrentItem(currentItem: Int, scrollIntoView: Boolean) {
    _currentItem = currentItem
    if (_currentItemChangedListener != null) {
      _currentItemChangedListener!!.OnCurrentItemChanged(this, currentItem)
    }
    if (scrollIntoView) {
      centerOnCurrentItem()
    }
    invalidate()
  }

  fun setCurrentItemChangedListener(listener: OnCurrentItemChangedListener) {
    _currentItemChangedListener = listener
  }
}

First, we customize an interface OnCurrentItemChangedListener for future use. Set listener in the method setCurrentItemChangedListener. The listener is called when the event occurs and the listener is notified.

Such a custom view is complete.

Posted by lettie on Fri, 19 Apr 2019 16:39:33 -0700