How to properly set two different fonts in Android Theme - android

I have an android application with english and arabic as choosable langagues.
My problem is that I need to use a specific font for each langague.
What is the proper way to implement this ?
My idea was to create two different themes in styles.xml, like this :
<resources>
<!-- English application theme. -->
<style name="EnglishTheme" parent="Theme.AppCompat.Light.NoActionBar">
... all my others attributes ...
<item name="pathBoldFont">fonts/FontEnglish-Bold.ttf</item>
<item name="pathRegularFont">fonts/FontEnglish-Regular.ttf</item>
<item name="pathThinFont">fonts/FontEnglish-Thin.ttf</item>
</style>
<!-- Arabic application theme. -->
<style name="ArabicTheme" parent="Theme.AppCompat.Light.NoActionBar">
... all my others attributes ...
<item name="pathBoldFont">fonts/FontArabic-Bold.ttf</item>
<item name="pathRegularFont">fonts/FontArabic-Regular.ttf</item>
<item name="pathThinFont">fonts/FontArabic-Thin.ttf</item>
</style>
</resources>
At application runtime, I check user's locale and load the correct theme. But obviously, this is not very DRY, because I have to write twice every other attributes of the theme.
I've read that this is not possible to update programmatically theme attributes (just fonts in my case) so what could be a better solution ?
FYI I use InflationX/Calligraphy for my custom fonts.

just create a custom TextView Class where u set your fonts and in there whenever you get a font u check for the language selected
public class CustomTextView extends android.support.v7.widget.AppCompatTextView {
public CustomTextView (Context context) {
super(context);
}
public CustomTextView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.CustomTextView,
0, 0);
try {
int custom_font = a.getInt(R.styleable.CustomTextView_custom_font, 0);
switch (custom_font) {
//Regular
case 0:
if(language == "english")
setTypeface(/*return Typeface Regular for english*/);
else setTypeface(/*return Typeface Regular for arabic*/);
break;
//Bold
case 1:
if(language == "english")
setTypeface(/*return Typeface Boldfor english*/);
else setTypeface(/*return Typeface Boldfor arabic*/);
break;
}
} finally {
a.recycle();
}
}
public CustomTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
and in your attr.xml
<declare-styleable name="CustomTextView">
<attr name="custom_font" format="string">
<enum name="regular" value="0" />
<enum name="bold" value="1" />
</attr>
</declare-styleable>
Use it like this
<CustomTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:custom_font="bold"/>

Related

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

Different theme styles for not all views

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

Android: How to custom-declare XML namespace in styles.xml?

I'm trying to put the custom XML namespace in the styles.xml and inherit it in the layout. I don't know how to declare the custom XML namespace in the styles.xml as I do in layout xml (e.g. xmlns:app="http://schemas.android.com/tools").
How do I use custom XML namespace in the styles.xml?
What I have:
The font asset, ReallyCoolFont.ttf is saved in the asset/fonts.
my_layout.xml:
<TextView
<!-- more attributes here -->
app:customFont="fonts/ReallyCoolFont.ttf"
<!-- more attributes here -->
</TextView>
styles.xml:
<style name="CoolTextView">
<!-- more items here -->
<!-- more items here -->
</style>
What I'd like to have:
my_layout.xml:
<TextView
<!-- more attributes here -->
style="#style/CoolTextView
<!-- more attributes here -->
</TextView>
styles.xml:
<style name="CoolTextView">
<!-- more items here -->
<item name="app:customFont">ReallyCoolFont.ttf</item>
<!-- more items here -->
</style>
Error I get:
Error:(1403, 21) No resource found that matches the given name: attr 'app:customFont'.
You need to define an attribute for your fonts in attr.xml file in res folder:
<attr name="myfonts" format="string" />
And you need to define custom style for your TextView and here we use our defined attribute(myfonts):
<declare-styleable name="MyCustomStyle">
<attr name="myfonts" />
</declare-styleable>
Then styles can be declared:
<style name="CoolTextView">
<item name="myfonts">ReallyCoolFont.ttf</item>
</style>
summary of what you have so far:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="myfonts" format="string">
</attr>
<declare-styleable name="MyCustomStyle">
<attr name="myfonts" />
</declare-styleable>
<style name="CoolTextView">
<item name="myfonts">ReallyCoolFont.ttf</item>
</style>
</resources>
4)Now your layout would be:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.example.MyCustomTextView
android:id="#+id/result"
style="#style/CoolTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="HELLO WORLD!"
android:textSize="24dp"
android:gravity="center" >
</com.example.MyCustomTextView>
</RelativeLayout>
5)and your MyCustomTextView is:
public class MyCustomTextView extends TextView {
private static final String TAG = "TextView";
public MyCustomTextView(Context context) {
super(context);
}
public MyCustomTextView(Context context, AttributeSet attrs) {
super(context, attrs);
settingFont(context, attrs);
}
public MyCustomTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
settingFont(context, attrs);
}
private void settingFont(Context ctx, AttributeSet attrs) {
TypedArray a = ctx.obtainStyledAttributes(attrs, R.styleable.MyCustomStyle);
String customFont = a.getString(R.styleable.MyCustomStyle_myfonts);
Typeface tf = null;
try {
tf = Typeface.createFromAsset(ctx.getAssets(), customFont);
} catch (Exception e) {
Log.e(TAG,e.getMessage());
a.recycle();
return;
}
setTypeface(tf);
a.recycle();
}
}
I assumed you put the font in asset not in asset/fonts directory.
also I highly recommend read this.
You don't need to add any prefix to reference your custom attributes in the style resource files. Doing it like this will work just fine:
<style name="CoolTextView">
<item name="customFont">ReallyCoolFont.ttf</item>
</style>
The answer is to NOT specify the namespace in the style.
<?xml version="1.0" encoding="utf-8" ?>
<resources xmlns:custom="http://schemas.android.com/apk/res/com.custom.project">
<style name="CustomStyle">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="customAttr">value</item> <!-- tee hee -->
</style>
</resources>
You don't need any prefixes, it will work without them. This is code from one of my projects, which works just fine
<style name="defaultTriangle">
<item name="triangleColor">#FF33B5E5</item>
<item name="triangleStrokeColor">#android:color/black</item>
<item name="triangleStrokeWidth">3dp</item>
</style>
<si.kseneman.views.Triangle
style="#style/defaultTriangle"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:padding="10dp"
android:rotation="0"
/>
I've done next for custom font
CustomTextView
public class KlavikaTextView extends TextView {
private final static int KLAVIKA_BOLD = 0;
private final static int KLAVIKA_BOLD_ITALIC = 1;
private final static int KLAVIKA_LIGHT = 2;
private final static int KLAVIKA_LIGHT_ITALIC = 3;
private final static int KLAVIKA_MEDIUM = 4;
private final static int KLAVIKA_MEDIUM_ITALIC = 5;
private final static int KLAVIKA_REGULAR = 6;
private final static int KLAVIKA_REGULAR_ITALIC = 7;
public KlavikaTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
parseAttributes(context, attrs);
}
public KlavikaTextView(Context context, AttributeSet attrs) {
super(context, attrs);
parseAttributes(context, attrs);
}
public KlavikaTextView(Context context) {
super(context);
}
private void parseAttributes(Context context, AttributeSet attrs) {
TypedArray values = context.obtainStyledAttributes(attrs, R.styleable.KlavikaTextView);
// The value 0 is a default, but shouldn't ever be used since the attr is an enum
int typeface = values.getInt(R.styleable.KlavikaTextView_typeface, KLAVIKA_REGULAR);
// You can instantiate your typeface anywhere, I would suggest as a
// singleton somewhere to avoid unnecessary copies
switch (typeface) {
case KLAVIKA_BOLD:
setTypeface(App.klavikaBold);
break;
case KLAVIKA_BOLD_ITALIC:
setTypeface(App.klavikaBoldItalic);
break;
case KLAVIKA_LIGHT:
setTypeface(App.klavikaLight);
break;
case KLAVIKA_LIGHT_ITALIC:
setTypeface(App.klavikaLightItalic);
break;
case KLAVIKA_MEDIUM:
setTypeface(App.klavikaMedium);
break;
case KLAVIKA_MEDIUM_ITALIC:
setTypeface(App.klavikaMediumItalic);
break;
case KLAVIKA_REGULAR_ITALIC:
setTypeface(App.klavikaRegularItalic);
break;
case KLAVIKA_REGULAR:
default:
setTypeface(App.klavikaRegular);
break;
}
}}
Then in values I've created attr.xml
<!-- Define the values for the attribute -->
<attr name="typeface" format="enum">
<enum name="klavika_bold" value="0" />
<enum name="klavika_bold_italic" value="1" />
<enum name="klavika_light" value="2" />
<enum name="klavika_light_italic" value="3" />
<enum name="klavika_medium" value="4" />
<enum name="klavika_medium_italic" value="5" />
<enum name="klavika_regular" value="6" />
<enum name="klavika_regular_italic" value="7" />
</attr>
<!--
Tell Android that the class "KlavikaTextView" can be styled,
and which attributes it supports-->
<declare-styleable name="KlavikaTextView">
<attr name="typeface" />
</declare-styleable>
Next created the style
<style name="TextView.Example">
<item name="typeface">klavika_bold</item>
</style>
This style you can use for your xml Layouts
style="#style/TextView.Example"
<?xml version="1.0" encoding="utf-8"?>
<resources
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res/com.my.project">
<style name="my_style"> <item name="custom:tag">some_value</item> </style>
</resources>
you are trying to apply XML namespacing to an attribute value, which won't work. In this case, you should specify the package name directly, like this:
<style name="my_style"> <item name="com.my.project:tag">some_value</item> </style>
Fast solution via code!
String pathFont = "fonts/ReallyCoolFont.ttf";
TextView text = (TextView) findViewById(R.id.TextView1);
Typeface fontFace = Typeface.createFromAsset( getAssets(), pathFont );
text.setTypeface( fontFace );
Third-party libraries , solve the problem, in XML!
1 - https://github.com/leok7v/android-textview-custom-fonts
2 - https://github.com/ragunathjawahar/android-typeface-textview
My suggestion
You will have other needs, and for each component
you will have to customize a class.
Another problem you have other layouts and N TextView components
for maintenance you will have a lot of work .
I use this method in projects in the OnCreate of my activity
if I need to change the font I have to do this
only in the OnCreate method of each activity .
private static final String FONT = "ReallyCoolFont.ttf";
public static void allTextView(final Context context, final View root) {
String fontPath = FONT;
try {
if (root instanceof ViewGroup) {
ViewGroup viewGroup = (ViewGroup) root;
int childCount = viewGroup.getChildCount();
for (int i = 0; i < childCount; i++)
allTextView(context, viewGroup.getChildAt(i) );
} else if (root instanceof TextView)
((TextView) root).setTypeface(Typeface.createFromAsset(context.getAssets(), fontPath));
} catch (Exception e) {
e.printStackTrace();
}
}
// call in OnCreate Activity
allTextView(this, findViewById(R.layout.main) );
The custom attributes are defined using <declare-styleable> tags; usually the file is called attrs.xml. The namespace declaration will have your app's package name in it.
The whole process is described here: Creating a View Class | Android Developers

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" />

How to define default styles for a custom control?

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.

Categories

Resources