Reputation: 60061
I create a custom view in Kotlin, and would like to access it's Attributes Resource.
Below is my code
class CustomCardView : FrameLayout {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
init {
LayoutInflater.from(context).inflate(R.layout.view_custom_card, this, true)
if (attrs != null) {
val a = context.obtainStyledAttributes(attrs, R.styleable.custom_card_view)
if (a.hasValue(R.styleable.custom_card_view_command)) {
var myString = a.getString(R.styleable.custom_card_view_command)
}
}
}
}
Note that this will error in the attrs
in the init function. I'm wondering how to access the attrs
?
Upvotes: 25
Views: 21218
Reputation: 1541
You can use core-ktx extension function withStyledAttributes
(https://android.github.io/android-ktx/core-ktx/androidx.content/android.content.-context/with-styled-attributes.html)
context.withStyledAttributes(set, R.styleable.custom_card_view) {
val myString = getString(R.styleable.custom_card_view_command)
}
Upvotes: 7
Reputation: 174
Why don't you simply skip these verbose constructors with default values and do it like that:
class CustomCardView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {
init {
inflate(context, R.layout.view_custom_card, this)
attrs?.let {
val typedArray = context.obtainStyledAttributes(it, R.styleable.custom_card_view)
val myString = typedArray.getString(R.styleable.custom_card_view_command)
}
}
Upvotes: 6
Reputation: 4392
This is a bit verbose, but should work as expected under all conditions:
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.FrameLayout
class CustomCardView: FrameLayout {
constructor(context: Context) : super(context) {
initialize(context, null)
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
initialize(context, attrs)
}
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
initialize(context, attrs)
}
private fun initialize(context: Context, attrs: AttributeSet?) {
LayoutInflater.from(context).inflate(R.layout.view_custom_card, this, true)
attrs?.let {
val a = context.obtainStyledAttributes(it, R.styleable.custom_card_view)
if (a.hasValue(R.styleable.custom_card_view_command)) {
var myString = a.getString(R.styleable.custom_card_view_command)
}
}
}
}
Upvotes: 1
Reputation: 1780
Adapting your code, I think you can also do something like this:
class CustomCardView(context: Context, attrs: AttributeSet?, defStyleAttr: Int) :
FrameLayout(context, attrs, defStyleAttr) {
constructor(context: Context) : this(context, null)
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
init {
LayoutInflater.from(context).inflate(R.layout.view_custom_card, this, true)
if (attrs != null) {
val a = context.obtainStyledAttributes(attrs, R.styleable.custom_card_view)
if (a.hasValue(R.styleable.custom_card_view_command)) {
var myString = a.getString(R.styleable.custom_card_view_command)
}
a.recycle()
}
}
}
Upvotes: 1
Reputation: 54705
You cannot access a secondary constructor parameter from an init
block. But there're at least two ways how you can implement similar functionality.
The first approach is using a single primary constructor with default parameters instead of multiple secondary constructors. In this case you have to apply the @JvmOverloads
annotation to the constructor in order to make Kotlin generate three different constructors.
class CustomCardView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : FrameLayout {
init {
LayoutInflater.from(context).inflate(R.layout.view_custom_card, this, true)
if (attrs != null) {
val a = context.obtainStyledAttributes(attrs, R.styleable.custom_card_view)
if (a.hasValue(R.styleable.custom_card_view_command)) {
var myString = a.getString(R.styleable.custom_card_view_command)
}
}
}
}
An the seconds approach is two chain constructors and move the init block content into the constructor with three arguments.
class CustomCardView : FrameLayout {
constructor(context: Context) :
this(context, null)
constructor(context: Context, attrs: AttributeSet) :
this(context, attrs, 0)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) :
super(context, attrs, defStyleAttr) {
LayoutInflater.from(context).inflate(R.layout.view_custom_card, this, true)
if (attrs != null) {
val a = context.obtainStyledAttributes(attrs, R.styleable.custom_card_view)
if (a.hasValue(R.styleable.custom_card_view_command)) {
var myString = a.getString(R.styleable.custom_card_view_command)
}
}
}
}
Upvotes: 23