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.

Managing an SQLite database on an Android Virtual Device

Mobile database development is often an unfamiliar when coming from the world of web development. Many database administrators are used to managing their databases through a management tool such as SQL Server Management Studio, phpMyAdmin or MySQL Workbench. They can feel a bit out of place when they find themselves creating SQLite databases by writing queries in Java or Objective-C code. Sometimes having a visual representation of the data just helps with understanding a system and can speed up development.

So, how can I directly manage an SQLite file?

When running an Android application on an actual device, we always have access to the file system over USB, so we can just download the SQLite database to our machine and manage it from there. In addition, there are many SQLite browser applications available for Android, such as SQLite Manager and SQLite Viewer, that allow you to browse the contents of an SQLite database directly from your device.

Note: The SQLite database for your application will always be found in the data -> data -> {application package name} -> databases folder on the Android file system.

The problem is that many developers do not develop on an actual Android device, at least not in the early stages. Instead, they run their application on the Android Virtual Device through the Android Developer Tools or Eclipse. While it is a bit more complicated, you can also download your application’s SQLite database from a virtual device. Here’s how it’s done:

Before following the steps below, make sure that your Android Virtual Device is running and connected to Android Developer Tools. Also, make sure that your application has successfully created an SQLite database on the file system.

1. Open the DDMS perspective in ADT by clicking the DDMS button in the top right hand corner of the application. You may only see the “Java” and “Debug” perspectives initially. If that is the case, you will need to click the open perspective button and choose “Other…” from the menu. From the next screen, choose DDMS and click OK.

perspective

The “Open Perspective” button in ADT.

The "Open Perspective" dialog in ADT with DDMS highlighted.

The “Open Perspective” dialog in ADT with DDMS highlighted.

2. You should see your AVD information in the left hand pane. In the right hand pane, choose the File Explorer tab.

3. From the file tree, expand to the data -> data -> {application package name} -> databases folder where you should see your SQLite database.

4. Select the database file and click the “Pull a file from the device” button in the top right hand corner of the File Explorer pane. A save dialog should appear.

The DDMS File Browser with the "Pull a file from the device" button shown.

The DDMS File Browser with the “Pull a file from the device” button shown.

5. Choose a location to save the file on your local machine.

Once the files is on your local machine, you can use any SQLite management tool to browse and modify your database. SQLite Explorer is a great free, open-source option for those looking for a recommendation.

There is also a plug-in for Eclipse available that will handle the process of downloading and opening the database for you. It allows you to manage the database directly from Eclipse. You can download it here.

If you need to make a change to the database, you can do it through your management software, and use the “Push a file onto the device” button from the File Explorer to push the updated database to the virtual device.

This is a great method of seeing exactly what is going on with your database every step of the way. While unit testing is still a crucial part of making sure your application functions correctly, visualizing the data can help to quickly resolve obvious bugs.

Thanks for reading. Feel free to leave any questions or comments.