Set view style programmatically Kotlin - android

I searched for many answers but didn't find any working solution. I wonder if there is some possibility to set Edittext style programmatically. In my situation, I have one layout which is included and used in two different places and beside the place where it is included need to use a different style.
<style name="TestStyle1" parent="Widget.AppCompat.EditText">
<item name="android:maxLength">1</item>
<item name="android:singleLine">true</item>
</style>
<style name="TestStyle2" parent="CodeTextStyle">
<item name="android:alpha">0.5</item>
</style>
first_activity.xml
<include
layout="#layout/custom_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
second_activity.xml
<include
layout="#layout/custom_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
custom_layout.xml
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<CustomEditText
android:id="#+id/et"
app:style_ref="#style/CustomTextStyle"
style="#style/TestStyle1" <-- Here i need to change style beside of usage
android:layout_height="wrap_content" />
</LinearLayout>
CustomEditText.kt
class CustomEditText #JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : AppCompatEditText(context, attrs) {
init {
val typedArray = context.theme.obtainStyledAttributes(attrs, R.styleable.CustomEditText, 0 , 0)
try {
val style2 = typedArray.getResourceId(R.styleable.CustomEditText_style_ref, 0)
setTypeface(Typeface.DEFAULT, style2)
invalidate()
}
}
}

So the simplest way, if your min API level is 23 would be to get a reference of each CustomEditText instance after inflation of the respective activity layout, then just set
myCustomEditText.setTextAppearance(R.style.TestStyle1)
as explained in this answer (which is using the deprecated version of this)
Another option would be to apply the 2 styles to two respective app theme styles, which you can set to each activity in AndroidManifest.
Or you can go the more complicated path that I suggested in my comment:
Create a custom LinearLayout class and pass the style in the constructor:
class CustomLayout(context: Context) : LinearLayout(context, null, R.style.TestStyle1)
You would just need to figure out a way to let the layout know which activity it is created. As far as I know, that style would then get passed on to each child view, your CustomEditText respectively.

Two useful answers.
RelativeLayout layout = (RelativeLayout)getLayoutInflater().inflate(R.layout.template, null);
or
int buttonStyle = R.style.your_button_style;
Button button = new Button(new ContextThemeWrapper(context, buttonStyle), null, buttonStyle).
See https://stackoverflow.com/a/24438579/5093308 and https://stackoverflow.com/a/5488652/5093308

Related

Extending MaterialCardView doesn't apply themes

According to the material specifications https://material.io/develop/android/components/material-card-view/ colorSurface applies to the card's Background color .
This works when we are specifying the card in our xml like this
<com.google.android.material.card.MaterialCardView
android:layout_width="100dp"
android:layout_height="100dp"></com.google.android.material.card.MaterialCardView>
When I run this I can see color surface properly being applied to the above card.
This also works if I make a card programmatically
addView(MaterialCardView(this).apply {
layoutParams = ViewGroup.LayoutParams(300,300)
})
However once I extend from MaterialCardView to make my own custom view , it appears as if the theme connection to this is lost . The color surface is not applied rather the card defaults to white
class CustomView #JvmOverloads constructor(
context: Context?,
attributeSet: AttributeSet? = null,
defStyleAttr: Int = 0
) : MaterialCardView(context, attributeSet, defStyleAttr){
}
<com.seed.formviewactivity.CustomView
android:layout_width="100dp"
android:layout_height="100dp"></com.seed.formviewactivity.CustomView>
My CustomView now doesn't have the colorSurface applied.
Is this a known issue ?
In your constructor you are using defStyleAttr: Int = 0.
You should apply the R.attr.materialCardViewStyle as default value instead of 0.
In this way your custom CardView will use the style defined in you app theme by materialCardViewStyle attribute.
The default value provided by the Material Components Library is:
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight">
<!-- ... -->
<item name="materialCardViewStyle">#style/Widget.MaterialComponents.CardView</item>
</style>

Adding theme to Seekbar programmatically makes Seekbar disappear

I need to add a variable number of Seekbars depending on configuration.
for(int i=0;i<length;i++){
seeks[i] = new SeekBar(getActivity());
seeks[i].setMax(4);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
layoutParams.setMargins(30,30,30,30);
seeks[i].setLayoutParams(layoutParams);
ll.addView(seeks[i]);
seeks[i].setVisibility(View.VISIBLE);
Works perfectly, but I would like the Seekbar to be discrete. In XML you can just add:
<SeekBar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/Base.Widget.AppCompat.SeekBar.Discrete"/>
However, if I follow the Docs, you should be able to add a Theme in the Seekbar constructor like so:
seeks[i] = new SeekBar(getActivity(), null, R.style.myTheme2 );
where I have set the theme in the styles.xml, OR
seeks[i] = new SeekBar(getActivity(), null, R.style.Widget_AppCompat_SeekBar_Discrete);
However, as soon as I do either of those things, the Seekbar disappears from view completely.
Got it working, leaving up for others who have the same problem, as I couldn't find it anywhere:
First setup a layout for each individual SeekBar:
seekbar_base.xml
<?xml version="1.0" encoding="utf-8"?>
<SeekBar
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:progressBackgroundTint="#color/seekBarBackground"
android:progressTint="#color/seekBarProgress"
android:thumbTint="#color/seekBar"
android:paddingLeft="20sp"
android:paddingTop="3dp"
android:paddingRight="20dp"
android:paddingBottom="30dp"
android:theme="#style/Widget.AppCompat.SeekBar.Discrete" />
Then instantiate it like so:
SeekBar bar = (SeekBar)LayoutInflater.from(getActivity()).inflate(R.layout.seekbar_base, null);
I still have no idea why it didn't work the way the docs said it should, but oh well.
when you use the XML, it actually set some default attribute. it is located in core / res / res / values / styles.xml
<style name="Widget.SeekBar">
<item name="android:indeterminateOnly">false</item>
<item name="android:progressDrawable">#android:drawable/progress_horizontal</item>
<item name="android:indeterminateDrawable">#android:drawable/progress_horizontal</item>
<item name="android:minHeight">20dip</item>
<item name="android:maxHeight">20dip</item>
<item name="android:thumb">#android:drawable/seek_thumb</item>
<item name="android:thumbOffset">8dip</item>
<item name="android:focusable">true</item>
</style>
at the same time, the SeekBar(Context context) also use the defalut style. it relaize that by call the same method you called, but set a defalut style.
public SeekBar(Context context) {
this(context, null);
}
public SeekBar(Context context, AttributeSet attrs) {
this(context, attrs, com.android.internal.R.attr.seekBarStyle);
}
so change your theme extends it.the add your custom item.
seeks[i] = new SeekBar(getActivity(), null, R.style.Widget_AppCompat_SeekBar_Discrete);
here are the document descirbe for three params constructor:
Perform inflation from XML and apply a class-specific base style from a
theme attribute. This constructor of View allows subclasses to use their
own base style when they are inflating. 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.

Button not showing Material Design

I've found out that the reason is that I'm using the Android-Iconics library - I removed the context injection and everything is fine now.
I'm using a combination of XML Layouts and Anko DSL to build my app and I've noticed that the button design is different depending on how it's generated.
In case it's an Anko-generated button, the text is in caps (what I think it should be in Material) and has a ripple effect. If the button is created by XML the text is lowercase and without the effect.
The upper button is the XML one, so here you can see the difference.
I've tried setting a custom style to the button but it doesn't seem to work - I can't even make textAllCaps=true work.
Currently I'm using androidx and extending AppCompat & Theme.AppCompat.Light.NoActionBar and I've tried extending Widget.AppCompat.Button to set a custom style to the view without luck.
This is happening in all API levels (24, 26 and 28). In the XML preview it does show fine.
The current XML is
<Button
android:text="#string/compartir"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/debunkShareButton"
android:textAllCaps="true"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintTop_toBottomOf="#+id/debunkTitle"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
style="#style/Full_Yellow_Button"/>
And the Full_Yellow_Button is
<style name="Full_Yellow_Button" parent="Widget.AppCompat.Button">
<item name="android:background">#drawable/yellow_gradient</item>
</style>
Any ideas? Thanks!
If you are using new material design components make sure your application theme extends from main theme Theme.MaterialComponents or other relavant theme.
<style name="Theme.MyApp" parent="Theme.MaterialComponents.Light">
<!-- ... -->
</style>
Also, instead of using generic Button class to define button, You need to use com.google.android.material.button.MaterialButton in your xml and java both.
<com.google.android.material.button.MaterialButton
android:id="#+id/material_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/button_label_enabled"/>
Your theme should be extended from Theme.MaterialComponents.xxxxx
like this XML block
<style name="AppTheme" parent="Theme.MaterialComponents.Light.DarkActionBar">
You can create your TextView class for set to uppercase
class UppercaseTextView : TextView, ViewTreeObserver.OnPreDrawListener {
constructor(context: Context) : super(context) {}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {}
constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {}
override fun setText(text: CharSequence, type: BufferType) {
super.setText(text.toString().toUpperCase(), type)
}
}

How to maintain layout attributes on merged custom android view?

I'm trying to replace a set of views with a custom composite view that is supposed to do exactly the same. Specifically I frequently repeat the following layout:
<LinearLayout style="#style/customLayoutStyle">
<Button style="#style/customButtonStyle" />
<TextView style="#style/customTextViewStyle" />
</LinearLayout>
My goal is to replace this block by a single <Highlighter />.
To this end I define in res/layout/highlighter.xml something like
<merge xmlns:android="http://schemas.android.com/apk/res/android"
style="#style/customLayoutStyle">
<Button android:id="#+id/btnHighlighter" style="#style/customButtonStyle" />
<TextView android:id="#+id/lblHighlighter" style="#style/customTextViewStyle" />
</merge>
And in my custom view I have something like
public class Highlighter extends LinearLayout {
public Highlighter(Context context, AttributeSet attrs) {
super(context, attrs);
inflate(context, R.layout.highlighter, this);
}
}
This mostly works, but it seems some of the layout parameters of the <merge> tag are ignored. This screenshot illustrates what seems to be wrong. The 3 images on the bottom row are aligned correctly, using 3x the LinearLayout block I'm trying to replace. Only the top-left image uses the custom view. My guess is that the layout parameters for padding and layout_weight are lost. Am I doing something wrong, or do I need a workaround?
You're right about the parameters being lost. To workaround this you can put the style definition for Highlighter in the layout where you define the Highlighter.
E.g.
<yournamespace.Highlighter
style="#style/customLayoutStyle"
/>
Years later this is still something that's complicated on Android, but it can be done.
You can do it by using style attribute in your custom view, then setting the style in you theme.
Don't set the style in res/layout/highlighter.xml:
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<Button android:id="#+id/btnHighlighter" style="#style/customButtonStyle" />
<TextView android:id="#+id/lblHighlighter" style="#style/customTextViewStyle" />
</merge>
Then in your custom view:
public class Highlighter extends LinearLayout {
public Highlighter(Context context, AttributeSet attrs) {
// The third constructor parameter is you friend
super(context, attrs, R.attr.highlighterStyle);
inflate(context, R.layout.highlighter, this);
}
}
Define the attribute in values/attrs_highlighter_view.xml:
<resources>
<attr name="highlighterStyle" format="reference" />
</resources>
Now you can set the style in your theme
<style name="YourAppTheme" parent="Theme.AppCompat">
<item name="preferenceViewStyle">#style/customLayoutStyle</item>
</style>
This way this style will be used by default whenever you use Highlighter.
Usage of your custom view is then like you want:
<com.your.app.Highlighter
android:id="#+id/highlighter"
... />
I have not verified if this works with layout_weight, but it does work with padding, background etc.
Elaborating on muscardinus' answer, starting from API 21 (Lollipop), Views accept a defStyleRes as fourth constructor argument, so you can skip the attr part and just do:
In styles.xml
<style name="CustomView">
// Define your style here
</style>
CustomView.kt
class CustomView #JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
) : LinearLayout(context, attrs, defStyleAttr, R.style.CustomView) {
...
}
Note that you need at least minSdkVersion 21 for this.

Set style for TextView programmatically

I'm trying to use the TextView constructor with style like this:
TextView myText = new TextView(MyActivity.this, null, R.style.my_style);
However, when I do this, the text view does not appear to take the style (I verified the style by setting it on a static object).
I've also tried using myText.setTextAppearance(MyActivity.this, R.style.my_style) but it also doesn't work.
I do not believe you can set the style programatically. To get around this you can create a template layout xml file with the style assigned, for example in res/layout create tvtemplate.xml as with the following content:
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="This is a template"
style="#style/my_style" />
then inflate this to instantiate your new TextView:
TextView myText = (TextView)getLayoutInflater().inflate(R.layout.tvtemplate, null);
You can create a generic style and re-use it on multiple textviews like the one below:
textView.setTextAppearance(this, R.style.MyTextStyle);
Edit: this refers to the Context object.
You can pass a ContextThemeWrapper to the constructor like this:
TextView myText = new TextView(new ContextThemeWrapper(MyActivity.this, R.style.my_style));
You can set the style in the constructor (but styles can not be dynamically changed/set).
View(Context, AttributeSet, int) (the int is an attribute in the current theme that contains a reference to a style)
Answer from Romain Guy
reference
Parameter int defStyleAttr does not specifies the style. From the Android documentation:
defStyleAttr - An attribute in the current theme that contains a
reference to a style resource that supplies default values for the
view. Can be 0 to not look for defaults.
To setup the style in View constructor we have 2 possible solutions:
With use of ContextThemeWrapper:
ContextThemeWrapper wrappedContext = new ContextThemeWrapper(yourContext, R.style.your_style);
TextView textView = new TextView(wrappedContext, null, 0);
With four-argument constructor (available starting from LOLLIPOP):
TextView textView = new TextView(yourContext, null, 0, R.style.your_style);
Key thing for both solutions - defStyleAttr parameter should be 0 to apply our style to the view.
Dynamically changing styles is not supported (yet). You have to set the style before the view gets created, via XML.
When using custom views that may use style inheritance (or event styleable attributes), you have to modify the second constructor in order not to lose the style. This worked for me, without needing to use setTextAppearence():
public CustomView(Context context, AttributeSet attrs) {
this(context, attrs, attrs.getStyleAttribute());
}
The accepted answer was great solution for me. The only thing to add is about inflate() method.
In accepted answer all android:layout_* parameters will not be applied.
The reason is no way to adjust it, cause null was passed as ViewGroup parent.
You can use it like this:
View view = inflater.inflate(R.layout.view, parent, false);
and the parent is the ViewGroup, from where you like to adjust android:layout_*.
In this case, all relative properties will be set.
Hope it'll be useful for someone.
I met the problem too, and I found the way to set style programatically. Maybe you all need it, So I update there.
The third param of View constructor accepts a type of attr in your theme as the source code below:
public TextView(Context context, AttributeSet attrs) {
this(context, attrs, com.android.internal.R.attr.textViewStyle);
}
So you must pass a type of R.attr.** rather than R.style.**
In my codes, I did following steps:
First, customize a customized attr to be used by themes in attr.xml.
<attr name="radio_button_style" format="reference" />
Second, specific your style in your used theme in style.xml.
<style name="AppTheme" parent="android:Theme.Translucent">
<!-- All customizations that are NOT specific to a particular API-level can go here. -->
<item name="radio_button_style">#style/radioButtonStyle</item>
</style>
<style name="radioButtonStyle" parent="#android:style/Widget.CompoundButton.RadioButton">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">64dp</item>
<item name="android:background">#000</item>
<item name="android:button">#null</item>
<item name="android:gravity">center</item>
<item name="android:saveEnabled">false</item>
<item name="android:textColor">#drawable/option_text_color</item>
<item name="android:textSize">9sp</item>
</style>
At the end, use it!
RadioButton radioButton = new RadioButton(mContext, null, R.attr.radio_button_style);
the view created programatically will use the specified style in your theme.
You can have a try, and hope it can work for you perfectly.
We can use TextViewCompact.setTextAppearance(textView, R.style.xyz).
Android doc for reference.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
textView.setTextAppearance(R.style.yourStyle)
you can use Extension Functions kotlin
fun TextView.setStyle(#StyleRes resId: Int) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
setTextAppearance(resId)
} else {
setTextAppearance(context, resId)
}
}
I have only tested with EditText but you can use the method
public void setBackgroundResource (int resid)
to apply a style defined in an XML file.
Sine this method belongs to View I believe it will work with any UI element.
regards.

Categories

Resources