Overriding Style in Custom View Constructor - android

I am attempting to set the style for a custom view through its constructor. It is not having the intended effect.
QueueButton.java
public class QueueButton extends ImageButton {
public QueueButton(Context context) {
super(context);
}
public QueueButton(Context context, AttributeSet attrs) {
super(context, attrs, R.style.queuebutton);
}
public QueueButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, R.style.queuebutton);
}
}
(in layout)
<QueueButton
android:id="#+id/queueBtn"
style="#style/queuebutton"
android:layout_width="50dp"
android:layout_height="35dp"
android:layout_gravity="center_horizontal"
android:focusable="false"
android:src="#drawable/remove_queue_icon" />
In the screen capture below, there are three different outcomes.
Left: The result from the class as described, but without the style declared in XML.
Middle: The same XML, but with super(context, attrs) in the two-argument constructor.
Right: The result from declaring the style in XML. This is the desired appearance.
Evidently, this is the wrong way to do this. I have not been able to find the relevant information for why that is and how to achieve the appropriate result.

I believe you need to pass in an attribute and not a style directly.
Add an attribute to your attrs.xml file (or create one in the values folder if you don't have one already).
Then create a theme for your app and link the new attribute to the required style in that theme (Or just add it to an existing theme if you're already using one).
Finally, in the custom view constructors pass the attribute to the super constructor. Android will look for that attribute in the context's theme (as per the documentation), and should use it.
Please note, however that if a style is specified in the XMl it will override the one you use in the constructor, as it takes precedence.
That's how things should look eventually:
attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="queueButtonStyle" format="reference" />
</resources>
styles.xml:
<style name="AppBaseTheme" parent="android:Theme.Holo.Light">
<item name="queueButtonStyle">#style/queuebutton</item>
</style>
<style name="queuebutton">
...content...
</style>
And the custom view class constructors:
public class QueueButton extends ImageButton {
public QueueButton(Context context) {
super(context);
}
public QueueButton(Context context, AttributeSet attrs) {
super(context, attrs, R.attr.queueButtonStyle);
}
public QueueButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, R.attr.queueButtonStyle);
}
}

Related

Style in custom view not affected on super constructor call

I'm trying to create custom view and set its style programatically. I created custom view based on AppCompatButton:
public class RangeSelectorButton extends androidx.appcompat.widget.AppCompatButton {
public int onClickKey = -1;
public RangeSelectorButton(Context context) {
this(context, null);
}
public RangeSelectorButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public RangeSelectorButton(Context context, AttributeSet attrs) {
this(context, attrs, R.style.RangeSelectorButton);
}
}
and now I get stuck in strange behaviour:
<ru.SomeDomain.CustomViews.RangeSelector
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true" android:focusable="true">
<!-- In this case all works fine -->
<ru.SomeDomain.RangeSelectorButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minHeight="10dp"
android:minWidth="40dp"
style="#style/RangeSelectorButton"
/>
<!-- Style not applies -->
<ru.SomeDomain.CustomViews.RangeSelectorButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minHeight="10dp"
android:minWidth="40dp"
/>
</ru.SomeDomain.CustomViews.RangeSelector>
What I should to do if I don't want use style attribute in xml every time when I create my custom view?
If it is necessary my style.xml contains:
<style name="RangeSelectorButton" parent="#android:style/Widget.Button">
<item name="android:background">#drawable/range_toggle_button_selector</item>
</style>
range_toggle_button_selector.xml:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/range_unselected" android:state_pressed="false" />
<item android:drawable="#drawable/range_selected" android:state_pressed="true" />
</selector>
The problem here is that you're passing a style in the place of the defStyleAttr parameter, which expects an attribute, not a style. There's two solutions here:
Use an attribute. In attrs.xml declare:
<attr name="rangeSelectorButtonStyle" format="reference"/>
and in your app theme:
<item name="rangeSelectorButtonStyle">#style/RangeSelectorButton</style>
and change your constructor to:
public RangeSelectorButton(Context context, AttributeSet attrs) {
this(context, attrs, R.attr.rangeSelectorButtonStyle);
}
If you're making a library for example, this is the best way of doing it since it allows users to customize your widget style.
If you don't want to use an attribute, you can call the fourth View constructor, which has a parameter that takes a default style and not an attribute:
View(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
Add this constructor to your view and in the second constructor, call the fourth like this:
public RangeSelectorButton(Context context, AttributeSet attrs) {
this(context, attrs, 0, R.style.RangeSelectorButton);
}
Note that the fourth constructor requires API 21.

Android - Custom Font Button - color specified in style not being set

I have created a MyButton class to set and display a custom
`public class MyButton extends android.support.v7.widget.AppCompatButton {
public MyButton(Context context) {
super(context);
init();
}
public MyButton(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public MyButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init(){
Typeface tf = Typeface.createFromAsset(getContext().getAssets(), "fonts/Filmcryptic.ttf");
setTypeface(tf);
}
}
`
In my styles.xml file, I have set a style like this, where I only want to override the textcolor and the size -
<style name="taWhiteButtonText" parent="#android:style/TextAppearance">
<item name="android:textColor">#FF0000</item>
<item name="android:textSize">20dp</item>
</style>
In my layout file, I am using it so -
<com.xx.yyy.MyButton
android:id="#+id/button_start_quiz"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:background="#drawable/chalkbutton"
android:text="Start Quiz"
style="#style/taWhiteButtonText"
/>
The problem I face is that the font size changes as I update the style, but the font color does not get reflected.
What am I doing wrong?
Change your parent in style.xml file.
<style name="taWhiteButtonText" parent="Widget.AppCompat.Button">
<item name="android:textColor">#FF0000</item>
<item name="android:textSize">20dp</item>
</style>

Custom style for all Edit Texts in the application

I have an Android application which uses Material design theme with backward compatibility through AppCompat.
There are multiple Textview's and EditText's in my application. I would like the same properties to be applied to all these TextView's and EditText's across the application. In order to achieve this, i have defined a custom style as shown below:
<!-- Base application theme. -->
<style name="ParentTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">#color/colorPrimary</item>
<item name="colorPrimaryDark">#color/colorPrimaryDark</item>
<item name="colorAccent">#color/colorAccent</item>
</style>
<style name="ArabicTheme" parent="ParentTheme">
<item name="android:editTextStyle">#style/arabicEditText</item>
<item name="android:textViewStyle">#style/arabicTextView</item>
</style>
<style name="arabicEditText" parent="#android:style/Widget.EditText">
<item name="android:gravity">right</item>
<item name="android:ellipsize">end</item>
</style>
<style name="arabicTextView" parent="#android:style/Widget.TextView">
<item name="android:gravity">right</item>
</style>
In my AndroidManifest.xml file under the <Application> tag, i have set android:theme="#style/ArabicTheme".
Below is the output of the activity:
As seen in the above output, the Style is being applied to TextView only. However, The same is not being applied to EditText.
Incase, if i explicitly specify these properties to the EditText in the corresponding Actitivy's xml as shown below:
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPersonName"
android:hint="Name"
android:ellipsize="end"
android:gravity="right"
android:ems="10"
android:layout_below="#+id/textView"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_marginTop="16dp"
android:id="#+id/editText" />
i.e I have explicitly added android:ellipsize="end" and android:gravity="right" to the <EditText>, and only then the output is as expected:
Like i said, i have multiple TextView's and EditText's and i cannot explicitly add these properties to all of them. So, is there a way i can achieve this using Styles or any other approach? Am i doing something wrong here?
Your approach is correct. Just remove android: from the editText's attribute name:
<item name="editTextStyle">#style/arabicEditText</item>
I cannot explain this though. I guess appCompat things don't reuse android attributes, but add another ones with similiar names. Same goes with colorPrimary, srcCompat and others.
I have been doing this
public class LightEditText extends android.widget.EditText{
public LightEditText(Context context) {
super(context);
setFont();
}
public LightEditText(Context context, AttributeSet attrs) {
super(context, attrs);
setFont();
}
public LightEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setFont();
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public LightEditText(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
setFont();
}
/**
* This method is used to set the given font to the TextView.
*/
private void setFont() {
Typeface typeface = TypefaceCache.get(getContext().getAssets(), "fonts/Roboto-Light.ttf");
setTypeface(typeface);
}
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
}
Then inside your xml file
<com.packagename.LightEditText
android:id="#+id/edtTaskName"
android:layout_height="wrap_content"
android:layout_width="match_parent"/>
Do the above method for setting common properties(fontType,style..etc) to editext

Android custom view: can't add own styles

I've found an improvement that may help not to hardcode view values into java classes in my android app. I wonder how can i get it to work. I want to to use this kind of view in my adapter (that means that i cannot use #style attribute in my layout's root). What am i doing wrong?
Code:
styles.xml:
<resources>
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="customViewStyleRef">#style/CustomViewStyle</item>
</style>
<attr name="customViewStyleRef" format="reference"/>
<style name="CustomViewStyle" parent="#android:style/Widget.TextView">
<item name="android:layout_margin">10dp</item>
</style>
</resources>
View class:
public class CustomView extends View {
public CustomView(Context context) {
super(context, null);
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs, R.attr.customViewStyleRef);
}
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, R.attr.customViewStyleRef);
}
public void init(#NonNull ViewGroup parentView) {
inflate(getContext(), R.layout.custom_view, parentView);
}
}
View layout:
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#f00"
android:textColor="#fff"
android:textSize="50sp"
android:text="test text"/>
</merge>
Problem: The TextView container does not have any padding that i specified in styles.xml (see the image).
You should initialize the TextView, then use the style for the TextView or you can solve this problem by putting style name inside the TextView and use an id for the TextView it will help for using it.
<TextView
android:id="#+id/custom_tv"
style="#style/CustomViewStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#f00"
android:textColor="#fff"
android:textSize="50sp"
android:text="test text"/>
I think i've figured this out. My built files were somehow cached and i couldn't get it to work. And i took a wrong testing parameter. My approach worked 😁

sans-serif-light with "fake bold"

I have set up the following theme for my app:
<style name="AppTheme" parent="Theme.Sherlock.Light">
<item name="android:textViewStyle">#style/RobotoTextViewStyle</item>
</style>
<style name="RobotoTextViewStyle" parent="android:Widget.TextView">
<item name="android:fontFamily">sans-serif-light</item>
</style>
Therefor when I create a TextView I get the "roboto light" font which I want. Some TextViews however, I would like to set the textStyle="bold" attribute but it doesn't work since the light font doesn't have a "native" (?) bold variant.
On the other side if I programmatically use the setTypeface method I could get a bold font:
textView.setTypeface(textView.getTypeface(), Typeface.BOLD);
This font is derived from the roboto light and looks really good.
I would like to have this bold light font but I wonder what the most elegant way to do it is.
Can it be done solely using xml?
What is the best implementation if I need to create a "BoldTextView extends TextView"?
I ended up creating a class for it:
public class FakeBoldTextView extends TextView {
public FakeBoldTextView(Context context) {
super(context);
setTypeface(getTypeface(), Typeface.BOLD);
}
public FakeBoldTextView(Context context, AttributeSet attrs) {
super(context, attrs);
setTypeface(getTypeface(), Typeface.BOLD);
}
public FakeBoldTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setTypeface(getTypeface(), Typeface.BOLD);
}
}
And using it in XML like this:
<the.package.name.FakeBoldTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Example string" />

Categories

Resources