One of the most important parts is the user interface, as it is the most visible part of an app. When we create apps for Android devices, sometimes we may need to create custom views.
Creating a new UI control involves creating a new type, inheriting either directly or indirectly from one of the View
types. Here's how it's done:
Button
type. We also need to ensure that the namespace is user friendly as it will be used in the layout files:namespace XamarinCookbook.Views { public class TimedButton : Button { } }
public int Interval { get; set; } private async void OnClicked(object sender, EventArgs e) { // disable the button for several seconds if (Interval > 0) { Enabled = false; await Task.Delay(Interval * 1000); Enabled = true; } }
Context
instance:public TimedButton(Context context) : base(context) { Interval = 5; // default Click += OnClicked; }
IAttributeSet
instance:public TimedButton(Context context, IAttributeSet attrs) : base(context, attrs) { Interval = 5; // default Click += OnClicked; }
To make use of the view in our UI layouts, we can either instantiate the control in code or we can add them to our layout resource files.
var button = new TimedButton(this);
<xamarincookbook.views.TimedButton
android:id="@+id/myButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/hello"/>
Although we can add properties to custom controls, we cannot set them in the layout resource files. To do this, we must create an attributes file:
value
resource (Resources/values/TimedButtonAttr.xml
):<?xml version="1.0" encoding="UTF-8" ?> <resources> <declare-styleable name="TimedButton"> <attr name="interval" format="integer" /> </declare-styleable> </resources>
IAttributeSet
parameter:public TimedButton(Context context, IAttributeSet attrs) : base(context, attrs) { // get our custom style TypedArray styled = context.Theme.ObtainStyledAttributes( attrs, Resource.Styleable.TimedButton, 0, 0); try { // set the property from the attribute Interval = styled.GetInt( Resource.Styleable.TimedButton_interval, 5); } finally { // clean up styled.Recycle(); styled.Dispose(); } Click += OnClicked; }
view
instance in our layout resource file:<xamarincookbook.views.TimedButton
...
xmlns:app="http://schemas.android.com/apk/res-auto"
app:interval="3"/>
Creating a custom view is no different from subclassing an existing type and adding functionality. However, to use this new type, we have to add support for it so that it can be used in layouts that will be inflated. This is just an extra, optional constructor, but it is recommended to add an attributes resource file.
Using this view in a layout resource is no different from adding the standard Android views to the layout. But we can see that we have to specify the full namespace of the button, and this is lowercase. This is exactly the same as adding views from the Android support libraries.
All Android views require at least a constructor that accepts an instance of Context
. This allows the view to properly handle the various lifecycle events.
The additional constructor takes the usual Context
property, but additionally, it also takes the IAttributeSet
parameter. This is used by the layout inflator to pass the attributes from the XML to our custom view, which in turn we can use to initialize any members.
The attributes are declared in a resource, <declare-styleable>
, so that they can be used in code to retrieve the attribute values. This element will contain all the <attr>
elements that represent each available XML attribute that can be used in the layout.
Custom attributes are not prefixed with android:
but with a local, app-specific namespace from http://schemas.android.com/apk/res-auto.
When the view is inflated from an XML layout, all the attributes from that element are passed to the constructor via IAttributeSet
. Although it's possible to read values from IAttributeSet
directly, doing so does not allow resource values to be resolved, such as when using string resources.
We can obtain the resolved and styled attributes from the result of the ObtainStyledAttributes()
method on the Context
themes. This method returns a TypedArray
object, from which we can easily retrieve the values that we can use to initialize members.
Sometimes we wish to create a new view that is actually just a collection of other views, with some relationship between them. This is very easily done by simply subclassing a layout type, such as LinearLayout
, instead of a button. This allows us to add subviews that will make up our new compound view.