Skip to content

Commit e789d6a

Browse files
committed
Refactored code to make it easier to define a font family for the application along with its own specific ‘textStyle’ values.
For example, you can have the Roboto font family with ‘normal’, ‘bold’, ‘condensed’, ‘condensedBold’ etc. and also the SourceSansPro family with ‘lightItalic’ and ‘black’. Normally in one application theme you have a single font family, but this also supports multiple font families at once.
1 parent c40795a commit e789d6a

19 files changed

+325
-79
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ local.properties
1818
# Eclipse project files
1919
.classpath
2020
.project
21+
.gradle/
2122

2223
# Proguard folder generated by Eclipse
2324
proguard/

Library/src/main/AndroidManifest.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
android:allowBackup="true"
1111
android:icon="@drawable/ic_launcher"
1212
android:label="@string/app_name"
13-
android:theme="@style/AppTheme"
13+
android:theme="@style/AppTheme.Light"
1414
android:name=".MyApp">
1515
<activity
1616
android:name=".MainActivity"
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

Library/src/main/java/org/codeandmagic/android/MainActivity.java

+65-21
Original file line numberDiff line numberDiff line change
@@ -7,58 +7,102 @@
77
import android.view.Menu;
88
import android.view.MenuItem;
99
import android.view.View;
10+
import android.view.View.OnClickListener;
1011
import android.view.ViewGroup;
1112

1213
public class MainActivity extends ActionBarActivity {
1314

15+
public static final String THEME_ID = "theme_id";
16+
1417
@Override
1518
protected void onCreate(Bundle savedInstanceState) {
1619
super.onCreate(savedInstanceState);
20+
21+
// The AppTheme.Light theme shows Roboto and SourceSansPro fonts.
22+
// The AppTheme.Dark theme shows SourceSansPro font.
23+
final int themeId;
24+
if (getIntent().hasExtra(THEME_ID)) {
25+
themeId = getIntent().getIntExtra(THEME_ID, R.style.AppTheme_Light);
26+
setTheme(themeId);
27+
} else {
28+
themeId = MyApp.getThemeId(this);
29+
}
30+
1731
setContentView(R.layout.activity_main);
1832

33+
// Demo fragments for showing the fonts in action.
1934
if (savedInstanceState == null) {
20-
getSupportFragmentManager().beginTransaction()
21-
.add(R.id.container, new PlaceholderFragment())
22-
.commit();
35+
final Fragment fragment = (R.style.AppTheme_Light == themeId) ? RobotoFragment.newInstance()
36+
: SourceSansProFragment.newInstance();
37+
getSupportFragmentManager().beginTransaction().add(R.id.container, fragment).commit();
2338
}
2439
}
2540

2641

2742
@Override
2843
public boolean onCreateOptionsMenu(Menu menu) {
29-
30-
// Inflate the menu; this adds items to the action bar if it is present.
3144
getMenuInflater().inflate(R.menu.main, menu);
3245
return true;
3346
}
3447

3548
@Override
3649
public boolean onOptionsItemSelected(MenuItem item) {
37-
// Handle action bar item clicks here. The action bar will
38-
// automatically handle clicks on the Home/Up button, so long
39-
// as you specify a parent activity in AndroidManifest.xml.
40-
int id = item.getItemId();
41-
if (id == R.id.action_settings) {
42-
return true;
50+
return item.getItemId() == R.id.action_settings || super.onOptionsItemSelected(item);
51+
}
52+
53+
public void switchTheme() {
54+
final int themeId;
55+
if (getIntent().hasExtra(THEME_ID)) {
56+
themeId = getIntent().getIntExtra(THEME_ID, R.style.AppTheme_Light);
57+
} else {
58+
themeId = MyApp.getThemeId(this);
4359
}
44-
return super.onOptionsItemSelected(item);
60+
final int newThemeId = (R.style.AppTheme_Light == themeId) ? R.style.AppTheme_Dark : R.style.AppTheme_Light;
61+
finish();
62+
startActivity(getIntent().putExtra(THEME_ID, newThemeId));
4563
}
4664

47-
/**
48-
* A placeholder fragment containing a simple view.
49-
*/
50-
public static class PlaceholderFragment extends Fragment {
5165

52-
public PlaceholderFragment() {
66+
public static class RobotoFragment extends Fragment {
67+
68+
public static RobotoFragment newInstance() {
69+
return new RobotoFragment();
5370
}
5471

5572
@Override
56-
public View onCreateView(LayoutInflater inflater, ViewGroup container,
57-
Bundle savedInstanceState) {
58-
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
73+
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
74+
final View rootView = inflater.inflate(R.layout.fragment_main_roboto, container, false);
75+
5976
// Apply a custom TextStyle from code.
60-
TypefaceTextView textView = (TypefaceTextView) rootView.findViewById(R.id.textView);
77+
final TypefaceTextView textView = (TypefaceTextView) rootView.findViewById(R.id.textView);
6178
textView.setTextStyle(RobotoTextStyle.BOLD);
79+
80+
rootView.findViewById(R.id.switch_theme).setOnClickListener(new OnClickListener() {
81+
@Override
82+
public void onClick(View v) {
83+
((MainActivity) getActivity()).switchTheme();
84+
}
85+
});
86+
return rootView;
87+
}
88+
}
89+
90+
public static class SourceSansProFragment extends Fragment {
91+
92+
public static SourceSansProFragment newInstance() {
93+
return new SourceSansProFragment();
94+
}
95+
96+
@Override
97+
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
98+
final View rootView = inflater.inflate(R.layout.fragment_main_source, container, false);
99+
100+
rootView.findViewById(R.id.switch_theme).setOnClickListener(new OnClickListener() {
101+
@Override
102+
public void onClick(View v) {
103+
((MainActivity) getActivity()).switchTheme();
104+
}
105+
});
62106
return rootView;
63107
}
64108
}

Library/src/main/java/org/codeandmagic/android/MyApp.java

+15-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package org.codeandmagic.android;
22

33
import android.app.Application;
4+
import android.content.Context;
5+
import android.content.pm.PackageInfo;
6+
import android.content.pm.PackageManager;
47

58
/**
69
* Created by evelina on 16/01/2014.
@@ -10,6 +13,17 @@ public class MyApp extends Application {
1013
@Override
1114
public void onCreate() {
1215
super.onCreate();
13-
TypefaceManager.setTextStyleExtractor(RobotoTextStyleExtractor.getInstance());
16+
TypefaceManager.addTextStyleExtractor(RobotoTextStyleExtractor.getInstance());
17+
TypefaceManager.addTextStyleExtractor(SourceSansProTextStyleExtractor.getInstance());
18+
}
19+
20+
public static int getThemeId(Context context) {
21+
try {
22+
String packageName = MyApp.class.getPackage().getName();
23+
PackageInfo packageInfo = context.getPackageManager().getPackageInfo(packageName, PackageManager.GET_META_DATA);
24+
return packageInfo.applicationInfo.theme;
25+
} catch (Exception e) {
26+
return 0;
27+
}
1428
}
1529
}
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,34 @@
11
package org.codeandmagic.android;
22

33
/**
4+
* Implementation of {@link TextStyle} defining the possible values for the 'textStyle' attribute
5+
* using the Roboto font.
46
* Created by evelina on 16/01/2014.
57
*/
68
public enum RobotoTextStyle implements TextStyle {
79

8-
NORMAL("roboto/Roboto-Regular.ttf"),
9-
LIGHT("roboto/Roboto-Light.ttf"),
10-
BOLD("roboto/Roboto-Bold.ttf"),
11-
CONDENSED("roboto/RobotoCondensed-Regular.ttf"),
12-
CONDENSED_LIGHT("roboto/RobotoCondensed-Light.ttf"),
13-
CONDENSED_BOLD("roboto/RobotoCondensed-Bold.ttf");
10+
NORMAL("normal", "roboto/Roboto-Regular.ttf"),
11+
LIGHT("light", "roboto/Roboto-Light.ttf"),
12+
BOLD("bold", "roboto/Roboto-Bold.ttf"),
13+
CONDENSED("condensed", "roboto/RobotoCondensed-Regular.ttf"),
14+
CONDENSED_LIGHT("condensedLight", "roboto/RobotoCondensed-Light.ttf"),
15+
CONDENSED_BOLD("condensedBold", "roboto/RobotoCondensed-Bold.ttf");
1416

17+
private String mName;
1518
private String mFontName;
1619

17-
RobotoTextStyle(String fontName) {
20+
RobotoTextStyle(String name, String fontName) {
21+
mName = name;
1822
mFontName = fontName;
1923
}
2024

2125
@Override
2226
public String getFontName() {
2327
return mFontName;
2428
}
29+
30+
@Override
31+
public String getName() {
32+
return mName;
33+
}
2534
}
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
package org.codeandmagic.android;
22

3-
import android.app.Application;
4-
53
/**
6-
* Implementation of {@TextStyleExtractor} which defines what fonts are used by the
7-
* {@link Application}.
4+
* Implementation of {@TextStyleExtractor} for the Roboto font.
85
*/
9-
public class RobotoTextStyleExtractor implements TextStyleExtractor {
6+
public class RobotoTextStyleExtractor extends TextStyleExtractor {
107

118
private static final RobotoTextStyleExtractor INSTANCE = new RobotoTextStyleExtractor();
129

@@ -18,9 +15,4 @@ public static TextStyleExtractor getInstance() {
1815
public TextStyle[] getTextStyles() {
1916
return RobotoTextStyle.values();
2017
}
21-
22-
@Override
23-
public TextStyle getFromTextStyleOrdinal(int textStyleOrdinal) {
24-
return getTextStyles()[textStyleOrdinal];
25-
}
2618
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package org.codeandmagic.android;
2+
3+
/**
4+
* Implementation of {@link TextStyle} defining the possible values for the 'textStyle' attribute
5+
* using the SourceSansPro font.
6+
* Created by evelina on 17/01/2014.
7+
*/
8+
public enum SourceSansProTextStyle implements TextStyle {
9+
10+
NORMAL("regular", "sourcesanspro/SourceSansPro-Regular.ttf"),
11+
LIGHT_ITALIC("lightItalic", "sourcesanspro/SourceSansPro-LightItalic.ttf"),
12+
SEMI_BOLD_ITALIC("semiBoldItalic", "sourcesanspro/SourceSansPro-SemiBoldItalic.ttf"),
13+
BLACK("black", "sourcesanspro/SourceSansPro-Black.ttf");
14+
15+
private String mName;
16+
private String mFontName;
17+
18+
SourceSansProTextStyle(String name, String fontName) {
19+
mName = name;
20+
mFontName = fontName;
21+
}
22+
23+
@Override
24+
public String getFontName() {
25+
return mFontName;
26+
}
27+
28+
@Override
29+
public String getName() {
30+
return mName;
31+
}
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package org.codeandmagic.android;
2+
3+
/**
4+
* Implementation of {@TextStyleExtractor} for the SourceProSans font.
5+
*/
6+
public class SourceSansProTextStyleExtractor extends TextStyleExtractor {
7+
8+
private static final SourceSansProTextStyleExtractor INSTANCE = new SourceSansProTextStyleExtractor();
9+
10+
public static TextStyleExtractor getInstance() {
11+
return INSTANCE;
12+
}
13+
14+
@Override
15+
public TextStyle[] getTextStyles() {
16+
return SourceSansProTextStyle.values();
17+
}
18+
}
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
package org.codeandmagic.android;
22

33
/**
4+
* Interface for linking a 'textStyle' attribute with the actual font asset name.
5+
*
46
* Created by evelina on 16/01/2014.
57
*/
68
public interface TextStyle {
79

810
String getFontName();
11+
12+
String getName();
913
}

Library/src/main/java/org/codeandmagic/android/TextStyleExtractor.java

+10-3
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,16 @@
33
/**
44
* Created by evelina on 16/01/2014.
55
*/
6-
public interface TextStyleExtractor {
6+
public abstract class TextStyleExtractor {
77

8-
TextStyle getFromTextStyleOrdinal(int textStyleOrdinal);
8+
public abstract TextStyle[] getTextStyles();
99

10-
TextStyle[] getTextStyles();
10+
public TextStyle getFromTextStyle(String textStyleName) {
11+
for (TextStyle textStyle : getTextStyles()) {
12+
if (textStyle.getName().equals(textStyleName)) {
13+
return textStyle;
14+
}
15+
}
16+
return null;
17+
}
1118
}

Library/src/main/java/org/codeandmagic/android/TypefaceManager.java

+29-13
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
package org.codeandmagic.android;
22

33
import android.content.Context;
4+
import android.content.res.Resources.Theme;
45
import android.content.res.TypedArray;
56
import android.graphics.Typeface;
7+
import android.text.TextUtils;
68
import android.util.AttributeSet;
7-
import android.view.ContextThemeWrapper;
9+
import android.util.TypedValue;
810
import android.widget.TextView;
911

12+
import java.util.ArrayList;
1013
import java.util.HashMap;
11-
import java.util.Map;
14+
import java.util.HashSet;
1215

1316
/**
1417
* Utility class used to apply a custom {@link Typeface} to a {@link TextView} subclass.
@@ -21,12 +24,12 @@ public static TypefaceManager getInstance() {
2124
return INSTANCE;
2225
}
2326

24-
public static void setTextStyleExtractor(TextStyleExtractor textStyleExtractor) {
25-
INSTANCE.mTextStyleExtractor = textStyleExtractor;
27+
public static void addTextStyleExtractor(TextStyleExtractor textStyleExtractor) {
28+
INSTANCE.mTextStyleExtractors.add(textStyleExtractor);
2629
}
2730

28-
private final Map<TextStyle, Typeface> mTypefaces = new HashMap<TextStyle, Typeface>();
29-
private TextStyleExtractor mTextStyleExtractor;
31+
private final HashMap<TextStyle, Typeface> mTypefaces = new HashMap<TextStyle, Typeface>();
32+
private final HashSet<TextStyleExtractor> mTextStyleExtractors = new HashSet<TextStyleExtractor>();
3033

3134
private TypefaceManager() {
3235
// Singleton
@@ -43,15 +46,28 @@ private TypefaceManager() {
4346
public void applyTypeface(TextView textView, Context context, AttributeSet attrs) {
4447
final TypedArray styleValues = context.obtainStyledAttributes(attrs, new int[]{android.R.attr.textAppearance});
4548
final int textAppearanceStyleId = styleValues.getResourceId(0, 0);
49+
4650
if (textAppearanceStyleId != -1) {
47-
final ContextThemeWrapper themedContext = new ContextThemeWrapper(context, textAppearanceStyleId);
48-
final TypedArray textAppearanceStyleValues = themedContext.obtainStyledAttributes(new int[]{R.attr.textStyle});
49-
final int textStyleOrdinal = textAppearanceStyleValues.getInt(0, -1);
50-
if (textStyleOrdinal != -1) {
51-
final TextStyle textStyle = mTextStyleExtractor.getFromTextStyleOrdinal(textStyleOrdinal);
52-
applyTypeface(textView, textStyle);
51+
final Theme textAppearanceStyle = context.getResources().newTheme();
52+
textAppearanceStyle.applyStyle(textAppearanceStyleId, true);
53+
54+
final TypedValue textStyleValue = new TypedValue();
55+
textAppearanceStyle.resolveAttribute(R.attr.textStyle, textStyleValue, true);
56+
57+
if (textStyleValue.type == TypedValue.TYPE_STRING) {
58+
final String textStyleName = textStyleValue.string.toString();
59+
60+
if (!TextUtils.isEmpty(textStyleName)) {
61+
for (TextStyleExtractor extractor : mTextStyleExtractors) {
62+
63+
final TextStyle textStyle = extractor.getFromTextStyle(textStyleName);
64+
if (textStyle != null) {
65+
applyTypeface(textView, textStyle);
66+
break;
67+
}
68+
}
69+
}
5370
}
54-
textAppearanceStyleValues.recycle();
5571
}
5672
styleValues.recycle();
5773
}

0 commit comments

Comments
 (0)