Closed Leland-Takamine closed 7 years ago
Anko has a style()
function, though traditional Android XML styles are unsupported now.
Android SDK does not provide a good way to set View
styles in code out of the box, and we are trying to find a way to support it.
Inspired by the default Anko textView
extension methods, and after doing some research on how to set a theme to a view by code, I came up with the following extension methods, which work very well so far (can be applied to any view class):
public inline fun ViewManager.textView(text: CharSequence?, styleRes: Int = 0, init: TextView.() -> Unit): TextView {
return ankoView({ if (styleRes == 0) TextView(it) else TextView(ContextThemeWrapper(it, styleRes), null, 0) }) {
init()
setText(text)
}
}
public fun ViewManager.textView(text: CharSequence?, styleRes: Int = 0): TextView = textView(text, styleRes) {}
Key is, you have to use ContextThemeWrapper, and the 3 argument constructor of the passed view, see this link: http://stackoverflow.com/a/28613069/1128600
That allows to pass an XML style, and have it fully applied to the newly created view (not comparable to the crappy setTextAppearance
method)
@yanex Would it make sense to consider this for potential adoption into the standard library?
@sfunke, does this only work for styling the text in a TextView, or is it generalized to applying any style resource to any view (including things like padding, backgrounds, borders, etc.?)
Nevermind. I created this shim:
public inline fun ViewManager.styledButton(text: CharSequence?, styleRes: Int = 0, init: Button.() -> Unit): Button {
return ankoView({ if (styleRes == 0) Button(it) else Button(ContextThemeWrapper(it, styleRes), null, 0) }) {
init()
setText(text)
}
}
public fun ViewManager.styledButton(text: CharSequence?, styleRes: Int = 0): Button = styledButton(text, styleRes) {}
Works perfectly, I just call styledButton("text", R.style.MyStyle)
. Thanks @sfunke!
@sfunke I think
public inline fun ViewManager.AnyView(theme: Int = 0, init: TextView.() -> Unit): AnyView
is enough.
@fboldog +1
Is there any way to set style? So now you can set theme for each widget, like AppTheme, AppTheme.Light, but what if you need to set individual styles, eg. Widget.Button, Widget.Button.Colored?
@simophin depends on what version you're targeting. on API 21+, Google added a 4th parameter to all Views constructors allowing you to set the style resource: https://developer.android.com/reference/android/view/View.html#View(android.content.Context,%20android.util.AttributeSet,%20int,%20int)
So if you're only targeting devices running 21+, it's easy to make custom Anko views calling that constructor. If you want to target devices running below 21, you're out of luck. There's no (easy) way to set the style programmatically because it's all private APIs used by the XML layout inflater.
What we ended up doing was to create very small XML layout files containing just <Button style="@style/WhiteButton" />
and then instantiating that layout with Anko's include<Button>(R.layout.white_button)
Works great, thanks! If you wish to resolve attr value from Activity theme to style resource, just use this code:
val View.contextThemeWrapper: ContextThemeWrapper
get() = context.contextThemeWrapper
val Context.contextThemeWrapper: ContextThemeWrapper
get() = when (this) {
is ContextThemeWrapper -> this
is ContextWrapper -> baseContext.contextThemeWrapper
else -> throw IllegalStateException("Context is not an Activity, can't get theme: $this")
}
@StyleRes
fun View.attrStyle(@AttrRes attrColor: Int): Int = contextThemeWrapper.attrStyle(attrColor)
@StyleRes
private fun ContextThemeWrapper.attrStyle(@AttrRes attrRes: Int): Int =
attr(attrRes) {
it.getResourceId(0, 0)
}
private fun <R> ContextThemeWrapper.attr(@AttrRes attrRes: Int, block: (TypedArray)->R): R {
val typedValue = TypedValue();
if (!theme.resolveAttribute(attrRes, typedValue, true)) throw IllegalArgumentException("$attrRes is not resolvable")
val a = obtainStyledAttributes(typedValue.data, intArrayOf(attrRes));
val result = block(a)
a.recycle()
return result
}
then,
textView(attrStyle(R.attr.my_title)) {
}
Resolving the attr does not actually work in all cases. Example:
button("My Button", theme = attrStyle(R.attr.buttonBarButtonStyle))
will not apply the correct background, since the one-argument constructor of Button
that Anko uses calls through to the three-argument constructor with a default value for int defStyleAttr
which overrides the button background with the default.
While working in some cases, the theme support of Anko 0.9 is not a full replacement for the style
XML attribute. Can we reopen this issue?
@hannesstruss I've created the new issue for this: #361.
I also mark this issue as "closed". If you have other problems with themed views, please feel free to create the new issues.
now you can use horizontalProgressBar { }
so no need to set style horizontal to progressBar
I can't seem to find a good way of setting a style for a view. For example if I wanted a large ProgressBar in XML I would write:
How do you do this in anko?