Custom view

View is base class for widgets, which are used to create interactive UI components (buttons, text fields, etc.)

A ViewGroup is a special view that can contain other views (called children.) The view group is the base class for layouts and views containers.

You can modify existing widget like button by overriding or adding new functional. Usually you need override some onXXXX() method.

You can compound existing widgets into one view group. This can be done by extending one of layout manager.

You can extend View or ViewGroup class to implement totally custom widget.

appearance

Override the onDraw() method to change or define appearance of view. It has Canvas object as parameter.

The Canvas class defines methods for drawing text, lines, bitmaps, and many other graphics primitives. You can use these methods in onDraw() to create your custom user interface (UI).

You need to create one or more Paint objects, that define the color, style, font for drawing methods.

Simple round ImageView

Call invalidate() method to redraw view after changing some properties that affect on appearance. For example, at end of animation step.

attributes

You can define additional attributes to use them in xml layouts.

1. Define custom attributes for your view in a <declare-styleable> resource element. Typical name is name of view class.

<!-- usually save it in "res/values/attrs.xml" file -->
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="CustomImageView">
        <attr name="borderWidth" format="dimension" />
        <attr name="borderColor" format="color" />
    </declare-styleable>
</resources>

2. Add additional names space in your layout where custom view is used:

http://schemas.android.com/apk/res-auto
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:custom="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.darkraha.androidsand.ui.views.CustomImageView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:adjustViewBounds="true"
        android:src="@drawable/flower_n_2"
        android:scaleType="centerCrop"
        custom:borderWidth="20dp"
        custom:borderColor="@color/black"
        >

    </com.darkraha.androidsand.ui.views.CustomImageView>

</FrameLayout>

3. Apply attributes in the constructor.

Apply atributes assigned in xml

measurement

The View.onMeasure() method allows you to set width and height of view. If we customize View to support wrap_content, we must override onMeasure to specify the size of wrap_content, i.e. set at least the view's minimum height and width (getSuggestedMinimumHeight() and getSuggestedMinimumWidth() methods).

When overriding this method, you must call setMeasuredDimension() to store the measured width and height of this view.

A MeasureSpec encapsulates the layout requirements passed from parent to child. It is comprised of a size and a mode. There are three possible modes:

  • EXACTLY - the parent has determined an exact size for the child. The child is going to be given those bounds regardless of how big it wants to be.
  • AT_MOST - the child can be as large as it wants up to the specified size.
  • UNSPECIFIED - the child can be as large as it wants up to the specified size.

It is implemented as ints to reduce object allocation. This class is provided to pack and unpack the <size, mode> tuple into the int.

Simple onMeasure example

Parent view must calculte sizes of children by calling the ViewGroup.measureChild() method. See more about custom layouts.