Dynamic Font Size (and other styles) in an Android App

I was recently tasked with adding the ability to change font size globally through the user’s selection in the application settings panel. Android provides accessibility settings on the device level that allow you change the font size for all apps on the device. This is made easy to implement in your app through built-in structures in Android, but documentation is not specific on the most efficient way implement this on an application level. This is likely to be because it is not an encouraged practice, but in my particular case, the requirements were that this needed to be changed in the settings for the application since more options would be available than the standard usability features of Android provide.

SharedPreferences

The key to storing settings in an Android application is SharedPreferences. The examples below do not demonstrate how to write values using the SharedPreferences editor, just how to retrieve them. For more information on writing to SharedPreferences, see this article.

The case against subclassing TextView

Do a quick Google search on implementing a global font size in code and you’ll see many responses suggest to subclass TextView and load the text size options there. Then all you have to do is make sure you use this class for every TextView in your layout. Simple enough, right? Will this work? Absolutely. Is this option going to cause more trouble than its worth as development on your app becomes more complex? Definitely.

The problem here is that many of the Android widgets subclass TextView, such as Button, RadioButton, and CheckBox. Some of these are indirect subclasses of TextView, which makes implementing your version of TextView in these classes a pain. Not to mention the trouble that could arise from migrating to newer versions of Android in the future.

Styles vs. Theming

As you may already know, you set styles for your layouts to control the look and feel of the view. Themes are essentially just collections of these styles. Theming can often be ignored by developers because it is seen more as a method for controlling collections of styles and not just a single style like text size. Many developers think, “I like the default Android theme, so I don’t need to worry about configuring themes.” However, you can use a theme just for text size settings; they don’t need define values for every property. Using a theme over styles provides us with one huge advantage: we can set a theme for the entire view programmatically. Without this, we would have to traverse the tree of children in the view and set each style using the associated property. With multiple properties to change and multiple nested child views, this can get complicated.

Example

First, we need to define the settings in our themes.xml file. This file normally resides in the “res/layout” folder within your application. If it does not exist, you can create it. Here I only use options of Small, Medium and Large, but we could easily add many other options here if necessary. Here is an example:

<resources>
	<style name="FontSizeSmall">
		<item name="android:textSize">12sp</item>
	</style>
	<style name="FontSizeMedium">
		<item name="android:textSize">16sp</item>
	</style>
	<style name="FontSizeLarge">
		<item name="android:textSize">20sp</item>
	</style>
</resources>

Then we will create a class to handle loading our preferences. I called it FontSizeActivity, you could call it ThemedActivity if you are setting more values than just the font size.

public class FontSizeActivity extends Activity {
	@Override
	public void onStart() {
		super.onStart();

		// Enclose everything in a try block so we can just
		// use the default view if anything goes wrong.
		try {
			// Get the font size value from SharedPreferences.
			SharedPreferences settings =
				getSharedPreferences("com.example.YourAppPackage", Context.MODE_PRIVATE);

			// Get the font size option.  We use "FONT_SIZE" as the key.
			// Make sure to use this key when you set the value in SharedPreferences.
			// We specify "Medium" as the default value, if it does not exist.
			String fontSizePref = settings.getString("FONT_SIZE", "Medium");

			// Select the proper theme ID.
			// These will correspond to your theme names as defined in themes.xml.
			int themeID = R.style.FontSizeMedium;
			if (fontSizePref == "Small") {
				themeID = R.style.FontSizeSmall;
			}
			else if (fontSizePref == "Large") {
				themeID = R.style.FontSizeLarge;
			}

			// Set the theme for the activity.
			setTheme(themeID);
		}
		catch (Exception ex) {
			ex.printStackTrace();
		}
	}
}

Finally, we extend our existing Activity classes with the new FontSizeActivity, like this:

public class AppActivity extends FontSizeActivity {

Caveats

Yes, I know. I plead the case against subclassing TextView and then I ended up subclassing Activity for my example. What gives? Think of it this way; you should have a much fewer amount of Activities in your application than you do TextViews or widgets that inherit TextView. This will be exponentially so as complexity increases, so this solution requires less changes in code for you. In addition, the built-in subclasses of Activity are much less commonly used than the subclasses of TextView. You will need to extend those activities as well, but again they will ultimately require less code.

Conclusion

I’ve fought with all of the other ways to allow style based settings in Android and none of them have even come close to being this easy to implement. It is important to consider future changes to your application and this solution handily beats the other options in that regard. Thanks for reading.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s