360 andev 2017 stacy devino header

Spread your Wings with Spannables

If you enjoy talks from 360 AnDev, please support the conference via Patreon!

Spannables are THE key to perfectly scaled, dynamic, performant content. Stop using Html.toText() to do complex layouts that end up rendering fuzzy and slow in comparison. Create layered buttons without overdrawing or recalculation, support multiple font types and background images in a single line. Learn how to create complex Canvas drawings programmatically and multiple languages without destroying a layout. It’s perhaps one of the least covered APIs (even by Google) that is used by the Top Pros.


Intoduction

I’m Stacy Devino. I’m a mobile tech lead at a fin-tech company.

Spanning the Details

Spannables

  • Spannables are not well documented, yet every coder has learned how to write actual HTML at some point.

  • Spanable Types include: Editable, SpannableString, SpannableStringBuilder, and Spannable Factories.

String / General Spans

If you’ve ever used Spannables or spans, you’ve probably used it for strings, string content, and typefaces. Those objects are often associated with text appearance, typeface, bullet spans, strike-throughs, and quotes.

Android Encyclopedia of Spans

I have made an Android encyclopedia of spans. To note, there are no references on Stack Overflow, and no examples, but this document gives you one-line descriptions and then categories.

Examples of Spannables in Action

  • New York Times - they use bullet points that render quickly. The text is bold, contain multiple colors, and have different sizes on the same line. Moreover, there are clickable objects on single lines that link you to multiple places.

  • Google Docs - there’s strikethroughs, multiple colors, and fonts of different text styles.

But Why Use Spannables?

  • You can have multiple objects in a single line.
  • Twitter: Clickable objects, URL objects, and things that can be traversed via text-to-speech.

  • Faster rendering.

  • More programmatic control and simpler XML layouts.

  • Perfect scaling on every device.

Spannable types

Spannable

Get more development news like this

Spannable is the base class. It can contain mutable and immutable objects and supports different types of spans. You can have a span that has no content in its constructor; this is helpful when you will reuse it or do reassignments.

Editable

An Editable is editable, you use this in situations where you’re going to be using an EditText, or if you are building a messenger application that has objects with different fonts, and languages on the same string.

Simple Text Spannables & Spans

If I want to make an object bolded, italicized, do the following:


String simpleString = "Simple String Example";
String textToBold = "Simple";

SpannableString content = new SpannableString(simpleString);

//Bold Italic Text
content.setSpan(new StyleSpan(Typeface.BOLD_ITALIC),
    simpleString.indexOf(textToBold),
    simpleString.indexOf(textToBold) + textToBold.length(),
    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); //Spanned

 myTextView.setText(content);


Here, I declare a new style span, and provide it the typeface of bold and italic; it’s going to inherit whatever other objects that are part of that text view already.

Fancy Text Spans

Using Spannable objects:


String textToStrike = "String";
//Strikethrough - not possible using Html.toText()
content.setSpan(new StrikethroughSpan(),
    simpleString.indexOf(textToStrike),
    simpleString.indexOf(textToBold) + textToBold.length(),
    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); //Spanned
//Change Font Color (since font is the foreground)
content.setSpan(new
    ForegroundColorSpan(getColor(Color.PURPLE)),
    simpleString.indexOf(textToStrike),
    simpleString.indexOf(textToBold) + textToBold.length(),
    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); //Spanned

Taking our first input, we continue to this new output to give it a strikethrough and make it purple.

Clicky Action Spans

I want to make it blue to signify that it’s a clickable object. To accomplish this, you can use a ClickableSpan:


String textToClick= "Example";
ClickableSpan clickableSpan = new ClickableSpan() {
    @Override
    public void onClick(View textView) {
        //Set Some Kind of Clickable Action
        startActivity(new Intent(MyActivity.this, NextActivity.class));}
    @Override
    public void updateDrawState(TextPaint textPaint) {
            super.updateDrawState(textPaint);
            textPaint.setUnderlineText(true);
            textPaint.setColor(getColor(Color.BLUE);}
};

Then on the canvas inside the spannable:


public class RedRoundedBackgroundSpan extends ReplacementSpan {
        private int CORNER_RADIUS = 5;
        private int backgroundColor = 0;
        private int textColor = 0;
        public RedRoundedBackgroundSpan(Context context) {super();
            	backgroundColor = context.getColor(Color.RED;
            	textColor = context.getColor(Color.WHITE);}
        @Override
        public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
            	RectF rect = new RectF(x, top, x + measureText(paint, text, start, end), bottom);
            	paint.setColor(backgroundColor);
            	canvas.drawRoundRect(rect, CORNER_RADIUS, CORNER_RADIUS, paint);
            	paint.setColor(textColor);
            	canvas.drawText(text, start, end, x, y, paint);}
        @Override
        public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
            	return Math.round(paint.measureText(text, start, end)); }
        private float measureText(Paint paint, CharSequence text, int start, int end) {
            	return paint.measureText(text, start, end);} } //End of class

I’m making my own rectangle Canvas in red with rounded corners.

Speciality Spans

Locale

The following forces a US locale string. Suppose an app is in Spanish, but the brand name must appear in US Locale. This would be a way to accomplish that:

LocaleSpan localSpan = new LocaleSpan(new Locale("en", "US"));

Pictures

Adding a bitmap or drawable to the Spannable object.


Drawable yao = getResources().getDrawable(R.drawable.reallyface);
yao.setBounds(0, 0, yao.getIntrinsicWidth(), yao.getIntrinsicHeight());
ImageSpan span = new ImageSpan(yao, ImageSpan.ALIGN_BASELINE);

You can blur certain things to animate an object or animate a span. You can also use any mask filter.


BlurMaskFilter blurFilter = new BlurMaskFilter(2.0f,
    BlurMaskFilter.Blur.NORMAL);
MaskFilterSpan blurMask = new MaskFilterSpan(blurFilter);
//Now, you can make text Blurry! Any type of Mask can be used here.

Accessibility

Accessibility is an issue when working with spans because content IDs are not going to be accurate.


public static TtsSpan getPhoneTtsSpan(String phoneNumberString){
  	final TtsSpan.TelephoneBuilder builder=new TtsSpan.TelephoneBuilder();
  	if (phoneNumber == null) {
    		builder.setNumberParts(splitAtNonNumerics(phoneNumberString)); }
 	else {
    		if (phoneNumber.hasCountryCode()) {
      			builder.setCountryCode(Integer.toString(phoneNumber.getCountryCode()))}
    		builder.setNumberParts(Long.toString(phoneNumber.getNationalNumber()));
  	}
  	return builder.build();
}

Accessibility Scanner does a great job of noticing a number of things, but it doesn’t catch all these references inside of Spannable objects - you need to declare this actively.

Next Up: Android Architecture Components and Realm

General link arrow white

About the content

This talk was delivered live in July 2017 at 360 AnDev. The video was recorded, produced, and transcribed by Realm, and is published here with the permission of the conference organizers.

Stacy Devino

Stacy is a recognized Intel Innovator, Six Sigma Blackbelt, GDG organizer, and is the Women Techmakers Lead for Dallas/Ft. Worth. She specializes in Android performance coding, stemming from a deep knowledge of the Android OS. You can usually find her helping people with their projects, as well as shooting the breeze with her fellow makers! #perfmatters

4 design patterns for a RESTless mobile integration »

close