Skip to content
This repository has been archived by the owner. It is now read-only.

Set style? #16

Closed
Leland-Takamine opened this issue Apr 9, 2015 · 13 comments
Closed

Set style? #16

Leland-Takamine opened this issue Apr 9, 2015 · 13 comments

Comments

@Leland-Takamine
Copy link

@Leland-Takamine Leland-Takamine commented Apr 9, 2015

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:

<ProgressBar
        style="@android:style/Widget.DeviceDefault.Light.ProgressBar.Large"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

How do you do this in anko?

@yanex
Copy link
Member

@yanex yanex commented Apr 29, 2015

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.

@sfunke
Copy link

@sfunke sfunke commented Jan 25, 2016

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?

@dennislysenko
Copy link

@dennislysenko dennislysenko commented Mar 10, 2016

@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.?)

@dennislysenko
Copy link

@dennislysenko dennislysenko commented Mar 10, 2016

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!

@BennyWang
Copy link

@BennyWang BennyWang commented Mar 10, 2016

@sfunke I think

public inline fun ViewManager.AnyView(theme: Int = 0, init: TextView.() -> Unit): AnyView

is enough.

@fboldog
Copy link
Contributor

@fboldog fboldog commented Mar 10, 2016

#143
;)

@BennyWang
Copy link

@BennyWang BennyWang commented Mar 11, 2016

@yanex yanex added this to the Anko 0.9 milestone Mar 25, 2016
yanex added a commit that referenced this issue Apr 19, 2016
yanex added a commit that referenced this issue Apr 21, 2016
@yanex yanex added testing duplicate and removed open labels Apr 29, 2016
@yanex yanex closed this Apr 29, 2016
@simophin
Copy link

@simophin simophin commented Aug 2, 2016

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?

@gregschlom
Copy link

@gregschlom gregschlom commented Aug 2, 2016

@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)

@mvysny
Copy link

@mvysny mvysny commented Aug 8, 2016

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)) {
}
@hannesstruss
Copy link

@hannesstruss hannesstruss commented Feb 3, 2017

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?

@yanex
Copy link
Member

@yanex yanex commented May 3, 2017

@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.

@ayinmursalin
Copy link

@ayinmursalin ayinmursalin commented Jan 26, 2018

now you can use horizontalProgressBar { }

so no need to set style horizontal to progressBar

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
You can’t perform that action at this time.