I have a TextInputLayout
with a custom Style that needs to be heavily reused so I am trying to turn it into a custom view.
Here is xml to be reused :
<com.google.android.material.textfield.TextInputLayout
style="@style/TextInputLayoutAppearance"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</com.google.android.material.textfield.TextInputLayout>
TextInputLayoutAppearance
is the custom style I have created in the styles.xml
Here is the Class for my custom view :
class OutlinedTextInput @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : TextInputLayout(context, attrs, defStyleAttr) {
init {
LayoutInflater.from(context).inflate(R.layout.view_outlined_textinput, this, true)
}
}
Here is the view_outlined_textinput
I have adapted from the original xml above, for the custom view :
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:parentTag="com.google.android.material.textfield.TextInputLayout">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</merge>
There are 2 things to notice here :
The layout uses a merge tag to avoid redundant view in the view hierarchy.
It is crucial to apply the custom style. The style is applied in the original xml using style= syntax. However, since the merge tag is being used for the custom view it cannot be done that way.
I tried setting the style as follows but it didn't work :
class OutlinedTextInput @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = R.style.TextInputLayoutAppearance
) : TextInputLayout(context, attrs, defStyleAttr) {
I assume the above solution should work as the 3rd parameter in the constructor is to pass in the style.
Another option is to set all the properties programatically in the init {}
of my custom view but that defeats the purpose of having declared a style in the Styles file.
What are my options ?
Currently there are 4 constructor for a view
public View (Context context) -> Simple constructor to use when creating a view from code.
public View (Context context, AttributeSet attrs) -> Constructor that is called when inflating a view from XML
public View (Context context, AttributeSet attrs, int defStyleAttr) -> Perform inflation from XML and apply a class-specific base style from a theme attribute.
public View (Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) Added in API level 21
The 3rd & 4th constructors are called by subclasses if they want to specify an attribute containing a default style, or a default style directly (in the case of the four-argument constructor)
For example, a Button class's constructor would call this version of the super class constructor and supply R.attr.buttonStyle for defStyleAttr; this allows the theme's button style to modify all of the base view attributes (in particular its background) as well as the Button class's attributes. from the docs
So, when you are creating your custom view and adding that view into an XML android will always call the second constructor which look like this under the hood
public TextInputLayout(Context context, AttributeSet attrs) {
this(context, attrs, attr.textInputStyle);
}
The third argument attr.textInputStyle
is getting that specific style directly from the application theme.
So, To achieve the result you are looking for you can do the following.
attrs.xml
add <attr name="attribute_name" format="reference" />
eg. attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="attribute_name" format="reference" />
</resources>
style.xml
<item name="attribute_name">@style/your_style</item>
eg. style.xml
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
...
<item name="attribute_name">@style/your_style</item>
...
</style>
Finally, in your custom view second constructor you pass that attribute as a parameter
constructor(context: Context, attrs: AttributeSet) :
super(
context,
attrs,
R.attr.attribute_name
)
I hope it helps!