androidandroid-layoutandroid-custom-viewandroid-attributes

How do you create an XML-friendly control based on a layout?


I've created a custom control which is a subclass of LinearLayout. I have also created a layout file on which this control is based. Finally I have defined attributes which I parse in the constructor to set my custom properties on. As an example, one of those properties is called 'text'.

Here's a simplified version of my code (I've stripped out a lot of the other properties and such so we can just focus on the one property 'text'):

First, the class (our custom version of a RadioButton)...

public class RadioButton extends LinearLayout
{
    private TextView textView;

    public RadioButton(Context context, AttributeSet attrs)
    {
        super(context, attrs);
        initAttributes(attrs, 0);
    }

    private void initAttributes(AttributeSet attrs, int defStyle)
    {
        final TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.CheckBoxView, defStyle, 0);

        text = a.getString(R.styleable.RadioButton_text);
        if(text == null)
            text = "Not set";

        a.recycle();
    }

    @Override
    protected void onFinishInflate()
    {
        super.onFinishInflate();

        textView = (TextView)findViewById(R.id.textView);
        textView.setText(text);
    }

    private String text;
    public String getText() { return text; }
    public void setText(String newValue)
    {
        text = newValue;

        if(textView != null)
            textView.setText(text);
    }
}

Here's the attrs.xml file...

<resources>

    <attr name="text" format="string" />

    <declare-styleable name="RadioButton">
        <attr name="text" />
    </declare-styleable>

</resources>

And here's the 'reusable_radiobutton.xml' layout file (Note: the internal RadioButtonView is a custom-rendered View and works fine):

<?xml version="1.0" encoding="utf-8"?>
<com.somedomain.reusable.ui.RadioButton
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center_vertical">

    <com.somedomain.reusable.ui.RadioButtonView
        android:id="@+id/radioButtonView"
        style="@style/DefaultRadioButtonView" />

    <TextView
        android:id="@+id/textView"
        style="@style/DefaultRadioButtonText" />

</com.somedomain.reusable.ui.RadioButton>

With the above, users of my control can simply include it in their own layout files, like so...

<include android:id="@+id/someRadioButton"
    layout="@layout/reusable_radiobutton" />

The in their code, using the following, they can get that instance and do with it what they wish, like so...

RadioButton someRadioButton = (RadioButton)findViewById(R.id.someRadioButton);
someRadioButton.text = "Woot!";

This works as expected.

However, this doesn't...

<include android:id="@+id/someRadioButton"
    layout="@layout/reusable_radiobutton"
    app:text="Hello World!" />

It gives me a warning, but otherwise ignores it.

I then tried this...

<com.somedomain.reusable.ui.RadioButton
    app:text="Hello World!" />

While this does instantiate my control and does pass 'Hello World!' to my property via the attributes, nothing actually loads or even associates the layout file to my class so nothing appears on the screen!

So how can I create a custom view, based on a layout, which other developers can simply reference in their own layout files while also allowing them to set custom attributes?

Hope that all made sense! :)

Note: The Android documentation talks about exactly what I'm after, 'Compound Controls' as referenced here, but they don't give an example of using a layout to define the compounded control. However, I feel that was pretty close.


Solution

  • Found it here on SO. I didn't realize you could inflate something into yourself. That and using the Merge tag took care of it for me.