I have a custom control which extends the DialogPreference where I have custom attributes and I want to define default values for them.
Here is the relevent part of my attrs.xml:
<!-- definition of my custom attributes -->
<declare-styleable name="MyPreference">
<attr name="myAttr1" format="string" />
<attr name="myAttr2" format="reference" />
</declare-styleable>
<!-- declatation of my style for my AppTheme -->
<declare-styleable name="AppTheme">
<attr name="myPreferenceStyle" format="reference" />
</declare-styleable>
themes.xml:
<style name="AppTheme" parent="#style/Theme.Sherlock.Light.DarkActionBar">
<!-- try of replacing the default text color -->
<item name="android:textAppearance">#style/WhiteText</item>
<item name="myPreferenceStyle">#style/Preference.My</item>
</style>
styles.xml:
<style name="WhiteText" parent="#android:style/TextAppearance">
<!-- set the default color to white... however it doesn't work -->
<item name="android:textColor">#fff</item>
</style>
<style name="Preference">
<item name="android:positiveButtonText">#android:string/ok</item>
<item name="android:negativeButtonText">#android:string/cancel</item>
</style>
<style name="Preference.My">
<item name="android:dialogLayout">#layout/preferences_my_picker</item>
<item name="myAttr1">#string/unknown</item>
<item name="myAttr2">#array/bits</item>
</style>
So I have defined that I want that the class MyPreference should have the default values like this:
android:positiveButtonText = "OK"
android:negativeButtonText = "Cancel"
android:dialogLayout = <ref to a layout>
myAttr1 = "Unknown"
myAttr2 = [1, 2, 4]
But when I try to access them I get nothing:
public MyPreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyPreference, defStyle, 0);
String txt = a.getText(R.styleable.MyPreference_myAttr1);
// txt == null :(
int bitsResId = a.getResourceId(R.styleable.MyPreference_myAttr2, -1);
// next line will crash bitsResId == -1
int[] bits = res.getIntArray(bitsResId);
a.recycle();
}
public MyPreference(Context context, AttributeSet attrs) {
this(context, attrs, R.attr.myPreferenceStyle);
}
I would be really helpful if somebody could explain me what I do wrong. And also why I cannot change the default text color to white.
Related
i have a button that extends MaterialButton and i’m trying to access custom attributes i defined in styles.xml.
But the TypedArray doesnt contain them
Here is my custom style
<style name="AppWidget.Button" parent="Widget.MaterialComponents.Button">
<item name="android:padding">#dimen/textview_horizontal_padding</item>
<item name="cornerRadius">24dp</item>
<item name="android:insetTop">0dp</item>
<item name="android:insetBottom">0dp</item>
<item name="android:textAppearance">#style/AppStyle.TextAppearance.Button</item>
<item name="android:textColor">#color/button_text</item>
<item name="backgroundTint">#color/button_bg</item>
<item name="strokeColor">#color/button_stroke</item>
<item name="strokeWidth">2dp</item>
<item name="progressWidth">20dp</item>
<item name="progressColor">#color/dph_teal</item>
</style>
My custom attributes are progressWidth and progressColor
Here is my attrs.xml
<declare-styleable name="Button">
<attr name="progressColor" format="color" />
<attr name="progressWidth" format="dimension" />
</declare-styleable>
In my theme I'm setting
<item name="materialButtonStyle">#style/AppWidget.Button</item>
and how i try to retrieve them
private fun init(context: Context, attrs: AttributeSet?, defStyleAttr: Int) {
val ta = context.obtainStyledAttributes(attrs, R.styleable.Button, defStyleAttr, 0)
try {
val hasValue1 = ta.hasValue(R.styleable.Button_progressColor) //always false
val hasValue2 = ta.hasValue(R.styleable.Button_progressWidth) //always false
progressColor = ta.getColor(R.styleable.Button_progressColor, Color.WHITE)
progressWidth = ta.getDimension(R.styleable.Button_progressWidth, 5f)
} finally {
ta.recycle()
}
}
Any idea why? I’m basically drawing a progress indicator in the button
In order to read values set in your default style, you'll need to pass in the correct defStyleAttr to your constructor.
To do this, you should also have a constructor which has just the Context and AttributeSet as arguments. You can call through to your method passing R.attr.materialButtonStyle as defStyleAttr.
The call to obtainStyledAttributes uses the value of defStyleAttr to read the default style that is set in your theme.
I think maybe you forgot to in the Androidmanifest.xml set the style or else it will not be loaded
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:roundIcon="#mipmap/ic_launcher_round"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppWidget.Button">
In my Android app I have two different themes (light and dark).
For example:
<style name="AppThemeDark" parent="Theme.AppCompat">
<item name="colorPrimary">#android:color/black</item>
<item name="colorPrimaryDark">#android:color/black</item>
<item name="colorAccent">#android:color/holo_red_dark</item>
<item name="android:textColor">#android:color/white</item>
<item name="windowActionModeOverlay">true</item>
</style>
<style name="AppThemeLight" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="colorPrimary">#color/colorPrimary</item>
<item name="colorPrimaryDark">#color/colorPrimaryDark</item>
<item name="colorAccent">#color/colorAccent</item>
<item name="windowActionModeOverlay">true</item>
</style>
So, now I can apply, for example, different text colors to a TextView (white for dark theme and black for light):
<item name="android:textViewStyle">#style/TextViewDark</item>
<style name="TextViewDark">
<item name="android:textColor">?android:attr/colorAccent</item>
</style>
But it will apply to all TextViews.
The main question, is it possible to make in XML (not programmatically) next:
Light theme: Half of TextViews text color black, and another half green.
Black theme: TextViews that black in Light theme - red, and another half - blue (which are green in Light theme).
Create 2 classes extends TextView
public class OneTextView extends TextView {
public OneTextView(Context context) {
super(context);
init(context);
}
public OneTextView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public OneTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context){
int[] attrs = new int[] { R.attr.myFirstColor};
TypedArray ta = context.obtainStyledAttributes(attrs);
int appColor = ta.getColor(0, 0);
ta.recycle();
// set theme color
setTextColor(appColor);
}
}
public class SecondTextView extends TextView {
public SecondTextView(Context context) {
super(context);
init(context);
}
public SecondTextView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public SecondTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context){
int[] attrs = new int[] { R.attr.mySecondColor};
TypedArray ta = context.obtainStyledAttributes(attrs);
int appColor = ta.getColor(0, 0);
ta.recycle();
// set theme color
setTextColor(appColor);
}
}
each class you can use in xml like this
<com.route.to.class.OneTextView
android:layout_width="match_parent"
android:layout_height="match_parent" />
OneTextView can have black and red colors
SecondTextView can have green and blue colors
define attr.xml in values
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="myFirstColor" format="color" />
<attr name="mySecondColor" format="color" />
</resources>
Then in your styles.xml, define colors for each theme:
<style name="Theme.MyApp" parent="#style/Theme.Light">
<item name="myFirstColor">#color/black</item>
<item name="mySecondColor">#color/green</item>
</style>
<style name="Theme.MyApp.Dark" parent="#style/Theme.Dark">
<item name="myFirstColor">#color/green</item>
<item name="mySecondColor">#color/blue</item>
</style>
you have defined in styles.xml
<style name="TextViewDark">
<item name="android:textColor">?android:attr/colorAccent</item>
</style>
<style name="TextViewLight">
<item name="android:textColor">#color/green</item>
</style>
then you can use it in main.xml
<LinearLayout>
<TextView
android:id="#+id/light_text_view"
android:text"i´m use light theme"
style="#style/TextViewLight"/>
<TextView
android:id="#+id/dark_text_view"
android:text"i´m use darktheme"
style="#style/TextViewDark"/>
</LinearLayout>
you don´t need to define styles in AppThemes
I have created a Custom Class called RoundedButton that extends Button. I want to assign some default parameters. How to go about it?
There was no change after I implemented the xml codes below. Basically the style was not getting added by default. I don't know what I am missing.
in styles.xml
<resources>
<style name="AppTheme" parent="#android:style/Theme.Light">
<item name="RoundedButtonStyle">#style/CustomView</item>
</style>
<style name="CustomView">
<item name="android:clickable">true</item>
<item name="android:focusable">true</item>
<item name="android:focusableInTouchMode">true</item>
<item name="android:colorBackground">#color/transparent</item>
<item name="android:textColor">#color/blue</item>
<item name="android:windowBackground">#color/transparent</item>
<item name="android:background">#drawable/button_border</item>
<item name="android:gravity">center</item>
</style>
</resources>
in attrs.xml
<resources>
<attr name="RoundedButtonStyle" type="reference" />
</resources>
manifest:
android:theme="#style/AppTheme"
RoundButton.java
public RoundedButton(Context context) {
super(context);
ctx = context;
}
public RoundedButton(Context context, AttributeSet attrs) {
super(context, attrs, R.attr.RoundedButtonStyle);
ctx = context;
TypedArray attrsArray = context.obtainStyledAttributes(attrs, R.styleable.RoundedButtonAttrs, 0, 0);
initAttributesArray(attrsArray);
attrsArray.recycle();
initializeRoundedButton();
}
I have used this and this for trying to solve it.
EDIT: Earlier I had an issue that the background was light grey and did not change, I discovered the issue was because of a line of code that I had later in the UI Class.
I'm working on an OpenSource lib as an alternative for the built-in android PopupMenu, in my lib I'd like to make everything customizable such that if you want to use it and change the popup colors/dimensions it becomes easy.
The full source code is here:
https://github.com/shehabic/Droppy/tree/styleable_ui
I'm few issues .. briefly I learned that to make your widget/custom view styleable you have to define 1-Custom View, 2-Styleable attributes, 3-Default values for such styleable attributes.
so I have the following xml files:
res/droppy__attr.xml
<resources>
<declare-styleable name="Droppy">
<attr name="droppyMenuItemTitleStyle" format="reference"/>
</declare-styleable>
<declare-styleable name="DroppyMenuItemTitle">
<attr name="android:textColor"/>
<attr name="android:minWidth"/>
<attr name="android:minHeight"/>
<attr name="android:layout_height"/>
<attr name="android:layout_width"/>
<attr name="android:layout_gravity"/>
<attr name="android:gravity"/>
<attr name="android:layout_weight"/>
</declare-styleable>
</resource>
res/droppy__styles.xml
<resources>
<style name="Theme.DroppyDefaults" parent="android:Theme">
<item name="droppyMenuItemTitleStyle">#style/Droppy.DroppyMenuItemTitle</item>
</style>
<style name="Droppy">
</style>
<style name="Droppy.DroppyMenuItemTitle">
<item name="android:textColor">#color/darkgrey</item>
<item name="android:minWidth">128dp</item>
<item name="android:minHeight">30dp</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_width">0dp</item>
<item name="android:layout_gravity">end|center_vertical</item>
<item name="android:gravity">center_vertical</item>
<item name="android:layout_weight">1</item>
</style>
</resource>
res/droppy__defaults.xml
<resources>
<!-- Menu Item Title -->
<color name="default_menu_item_title_textColor">#color/darkgrey</color>
<dimen name="default_menu_item_title_minWidth">128dp</dimen>
<dimen name="default_menu_item_title_minHeight">30dp</dimen>
<dimen name="default_menu_item_title_layout_width">0dp</dimen>
<!-- There's no easy way to set default values for the following -->
<!--<float name="default_menu_item_title_layout_weight">1</float>-->
<!--<item name="default_menu_item_title_layout_gravity">end|center_vertical</item>-->
<!--<item name="default_menu_item_title_gravity">center_vertical</item>-->
</resources>
In the block above here's the 1st issue,
Now here's the code I use in my custom view:
DroppyMenuItem.java
public class DroppyMenuItemTitle extends TextView {
public DroppyMenuItemTitle(Context context) {
this(context, null);
}
public DroppyMenuItemTitle(Context context, AttributeSet attrs) {
this(context, attrs, R.attr.droppyMenuItemTitleStyle);
}
public DroppyMenuItemTitle(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
final int defaultWidth = ViewGroup.LayoutParams.MATCH_PARENT;
final float defaultWeight = 1;
final int defaultGravity = Gravity.CENTER_VERTICAL;
final int defaultLayoutGravity = Gravity.END | Gravity.CENTER_VERTICAL;
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DroppyMenuItemTitle, defStyleAttr, 0);
int width = (int) a.getDimension(R.styleable.DroppyMenuItemTitle_android_layout_width, defaultWidth);
int height = a.getInt(R.styleable.DroppyMenuItemTitle_android_layout_height, ViewGroup.LayoutParams.WRAP_CONTENT);
setGravity(a.getInt(R.styleable.DroppyMenuItemTitle_android_gravity, defaultGravity));
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(width, height);
lp.width = width;
lp.height = height;
lp.weight = a.getFloat(R.styleable.DroppyMenuItemTitle_android_layout_weight, defaultWeight);
lp.gravity = a.getInteger(R.styleable.DroppyMenuItemTitle_android_layout_gravity, defaultLayoutGravity);
setLayoutParams(lp);
}
As per the following line:
int width = (int) a.getDimension(R.styleable.DroppyMenuItemTitle_android_layout_width, defaultWidth);
if I use that customView and specify width as wrap_content or match_parent, it works fine as these are translated to -1 and -2.
But if specify it as XXdp (e.g. 20dp) it fails as the number now is actually a float not int
the error I get is as follows:
E/AndroidRuntime(2940): java.lang.NumberFormatException: Invalid int: "20.0dip"
In my Android application I need to apply style for a group of buttons, instead of styling each button individual. Something like this:
<?xml version="1.0" encoding="utf-8"?>
<!-- put here style="#ststyle/Button_Style" -->
<Button android:id="#+id/button1" android:text="#string/b01" />
<Button android:id="#+id/button2" android:text="#string/b02" />
<Button android:id="#+id/button4" android:text="#string/b03" />
<!-- end style -->
You can write the style for button like this ;
style_btn.xml
<style name="style_btn" parent="Wrap">
<item name="android:background">#drawable/btn_bg</item>
<item name="android:gravity">center</item>
<item name="android:textColor">#android:color/white</item>
<item name="android:textStyle">bold</item>
<item name="android:layout_marginTop">4dp</item>
<item name="android:minWidth">90dp</item>
</style>
apply that style to your button :
<Button
android:id="#+id/attach_file"
style="#style/style_btn"
android:layout_centerVertical="true"
android:background="#drawable/orange_bg"
android:drawablePadding="10dp"
android:drawableRight="#drawable/attach"
android:text="#string/str_email_attach" />
If you need the style to all of the buttons in your application, mention in your App theme style, Then no need to apply for every button.
<style name="YourTheme" parent="android:Theme.Light">
<item name="android:buttonStyle">#style/Button</item>
</style>
If you need the style to particluar button , then apply to every button
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="#style/Button"
android:text="Button" />
I know I'm late joining the party but I stumbled across this when trying to figure out the same problem myself.
What I did was:
Depending on how may button groups you have (say 3 for example) you need to subclass button and create three custom button classes (see below)
//Custom button 1
public class CustomButton1 extends Button {
public CustomButton1(Context context, AttributeSet attrs) {
super(context, attrs, R.attr.attrStyle1);
}
public CustomButton1(Context context, AttributeSet attrs, int defStyle) {
super(context, null, R.attr.attrStyle1);
}
}
//Custom button 2
public class CustomButton2 extends Button {
public CustomButton2(Context context, AttributeSet attrs) {
super(context, attrs, R.attr.attrStyle2);
}
public CustomButton2(Context context, AttributeSet attrs, int defStyle) {
super(context, null, R.attr.attrStyle2);
}
}
//Custom button 3
public class CustomButton3 extends Button {
public CustomButton3(Context context, AttributeSet attrs) {
super(context, attrs, R.attr.attrStyle3);
}
public CustomButton3(Context context, AttributeSet attrs, int defStyle) {
super(context, null, R.attr.attrStyle3);
}
}
You can see from the custom classes I have passed a custom attr. These I define in my styles.xml and use them as reference. See my styles.xml below:
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
</style>
<style name="theme1">
<item name="#attr/attrStyle1">#style/CustomButton1</item>
<item name="#attr/attrStyle2">#style/CustomButton2</item>
<item name="#attr/attrStyle3">#style/CustomButton3</item>
<item name="android:background">#color/warning_yellow_colour</item>
</style>
<style name="CustomButton1" parent = "#android:style/Widget.Button">
<item name="android:textColor">#color/white_colour</item>
<item name="android:padding">20dp</item>
<item name="android:background">#color/banner_background_sensor_colour</item>
</style>
<style name="CustomButton2" parent = "#android:style/Widget.Button">
<item name="android:textColor">#color/white_colour</item>
<item name="android:padding">20dp</item>
<item name="android:background">#color/button_red_colour</item>
</style>
<style name="CustomButton3" parent = "#android:style/Widget.Button">
<item name="android:textColor">#color/white_colour</item>
<item name="android:padding">20dp</item>
<item name="android:background">#color/text_blue_colour</item>
</style>
<attr name="attrStyle1" format="reference"/>
<attr name="attrStyle2" format="reference"/>
<attr name="attrStyle3" format="reference"/>
By linking the style to the attr, you then apply that style to your custom class, which you can then duplicate as many times as needed
CustomButton1 theme1 = (CustomButton1)findViewById(R.id.theme1);
CustomButton2 theme2 = (CustomButton2)findViewById(R.id.theme2);
CustomButton3 theme3 = (CustomButton3)findViewById(R.id.theme3);
Hopefully this is of benefit to someone!