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)
}
}
Related
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
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>
My app has several themes from which the user can chose (red, blue, green etc.) Every theme has a corresponding button style (RedTheme => RedButton).
Now I have created a custom view which slightly extends "Button" and unfortunately this button does not get any style, except I use the "style" attribute in a layout.xml.
I don’t use any additional attributes, I just want that my Button style is applied.
I want to do something similar than:
<item name="android:imageButtonStyle">#style/ImageButtonmyTimeRed</item>
I have already read many other posts like this one:
How to: Define theme (style) item for custom widget
but I’m not 100% sure if this solution fits for me since I don’t need any additional attributes.
I'm using the three parameter super constructor and pass the button style resource id. This works for all my custom themes. Thanks #pskink for pointing me into right direction.
public ClockButton(Context context, AttributeSet attrs) {
super(context, attrs, R.attr.buttonStyle);
...
}
My theme looks like this:
<style name="LilaTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="buttonStyle">#style/ButtonmyTimeLila</item>
</style>
I'd like to know why my code doesn't work the way I think it should work. I extended the original RadioButton widget with one little change. Here is the code:
<!-- language: java -->
public class RadioButton extends android.widget.RadioButton {
public RadioButton(Context context) {
super(context);
}
public RadioButton(Context context, AttributeSet attrs) {
super(context, attrs, R.style.MyRadioButton);
}
public RadioButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
}
This change is R.style.MyRadioButton in constructor. Next I entered following line to my styles.xml:
<!-- language: xml -->
<style name="MyRadioButton" parent="#android:style/Widget.CompoundButton.RadioButton" />
Widget.CompoundButton.RadioButton is style which defines drawable for RadioButton.
To use my own RadioButton I made a layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<RadioGroup
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<RadioButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Default radio button" />
<com.example.test.RadioButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="My radio button" />
</RadioGroup>
</LinearLayout>
What I think I did is I inherited style from default RadioButton and use that style in constructor (the same way it is done in original RadioButton source code). Problem is that my RadioButton doesn't show any drawable, just text and I was wondering why?
It also does not react on click because tapping it does not uncheck "Default radio button" despite the fact both are in RadioGroup.
Ok, I finally found a problem in my code.
Android documentation says:
defStyleAttr An attribute in the current theme that contains a reference to a style resource to apply to this view. If 0, no default style will be applied.
What I did wrong was passing style instead of attr (reference to style in current theme).
So here is the solution:
public RadioButton(Context context, AttributeSet attrs) {
super(context, attrs, R.attr.myRadioButtonStyle);
}
I left code in styles.xml untouched:
<style name="MyRadioButtonStyle" parent="#android:style/Widget.CompoundButton.RadioButton" />
But i had to create my custom theme in themes.xml and apply it to current Activity:
<style name="MyTheme">
<item name="myRadioButtonStyle">#style/MyRadioButtonStyle</item>
</style>
And in the attrs.xml:
<declare-styleable name="MyTheme">
<attr name="myRadioButtonStyle" format="reference" />
</declare-styleable>
My previous code which did not work was based on someone else's old code where View constructor attribute was named defStyle what led me to mistake. Android developers fixed documentation by renaming attribute to defStyleAttr. Here is an issue which documents it: https://code.google.com/p/android/issues/detail?id=12683
Last post clearly says:
The documentation is incorrect. The third constructor must be an attribute, e.g. R.attr.*
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.