Android ReplacementSpan Example

android.text.style.ReplacementSpan class is a supper span class which you can extend to create your customized span class. You can draw anything as you want in your custom span class, and then use it to replace the specified characters in your textview string.

If you are not familiar with android SpannableString, please read article Android SpannableString Example for basic introduction.

1. Example Overview.

This example implement below effect with IconReplacementSpan which extends android.text.style.ReplacementSpan. Generally your customize span class just need to override below two ReplacemengSpan methods.

  1. getSize() : Return the span area width.
  2. draw() : Draw the span content in this method. In out example we draw both span background and text in it.

android replacementspan example

2. IconReplacementSpan.java

To understand more accurately for below java code, please refer this picture which shows you the android text font diagram.

android text font diagram

package com.dev2qa.example.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.support.annotation.IntRange;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextPaint;
import android.text.TextUtils;
import android.text.style.ReplacementSpan;
import android.util.DisplayMetrics;
import android.util.TypedValue;

/**
 * Created by Jerry on 10/29/2017.
 */

public class IconReplacementSpan extends ReplacementSpan {

    private Context context;

    private int backgroundColorId;
    private float backgroundHeight;
    private float backgroundWidth;
    private float backgroundRadius;
    private Paint backgroundPaint;

    private String text;
    private int textColorId;
    private float textSize;
    private float textMarginRight;
    private Paint textPaint;

    public IconReplacementSpan(Context context, int backgroundColorId, int textColorId, String text) {
        // Do not allow empty text.
        if(TextUtils.isEmpty(text))
        {
            return;
        }

        // Init instance variable value.
        this.context = context.getApplicationContext();

        DisplayMetrics displayMetrics = this.context.getResources().getDisplayMetrics();

        // Init text related variable value.
        this.text = text;
        this.textColorId = textColorId;
        this.textMarginRight = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5, displayMetrics);
        this.textSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 25, displayMetrics);

        // Init background related variable value.
        this.backgroundColorId = backgroundColorId;
        this.backgroundHeight = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 25f, displayMetrics);
        this.backgroundWidth = this.caculateBackgroundWidth(text, displayMetrics, this.textSize);
        this.backgroundRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5, displayMetrics);

        // Init background paint.
        this.backgroundPaint = new Paint();
        this.backgroundPaint.setColor(this.backgroundColorId);
        this.backgroundPaint.setStyle(Paint.Style.FILL);
        this.backgroundPaint.setAntiAlias(true);

        // Init text paint.
        this.textPaint = new TextPaint();
        this.textPaint.setColor(this.textColorId);
        this.textPaint.setTextSize(this.textSize);
        this.textPaint.setAntiAlias(true);
        this.textPaint.setTextAlign(Paint.Align.CENTER);
    }

    /* Return the background width after replace text. */
    @Override
    public int getSize(@NonNull Paint paint, CharSequence charSequence, @IntRange(from = 0) int i, @IntRange(from = 0) int i1, @Nullable Paint.FontMetricsInt fontMetricsInt) {
        return (int)(this.backgroundWidth + this.textMarginRight);
    }

    /*Draw the span component in the canvas.*/
    @Override
    public void draw(@NonNull Canvas canvas, CharSequence text, @IntRange(from = 0) int start, @IntRange(from = 0) int end, float x, int top, int y, int bottom, @NonNull Paint paint) {

        // Draw background.
        Paint.FontMetrics backgroundFontMetrics = this.backgroundPaint.getFontMetrics();
        float textHeight = backgroundFontMetrics.descent - backgroundFontMetrics.ascent;
        //Calculate draw background y coordinate.
        float backgroundStartY = y + (textHeight - this.backgroundHeight) / 2 + backgroundFontMetrics.ascent;

        RectF backgroundRect = new RectF(x, backgroundStartY, x + this.backgroundWidth, backgroundStartY + this.backgroundHeight);
        canvas.drawRoundRect(backgroundRect, this.backgroundRadius, this.backgroundRadius, this.backgroundPaint);

        // Draw text.
        Paint.FontMetrics textFontMetrics = textPaint.getFontMetrics();
        float textRectHeight = textFontMetrics.bottom - textFontMetrics.top;
        canvas.drawText(this.text, x + this.backgroundWidth / 2, backgroundStartY + (this.backgroundHeight - textRectHeight) / 2 - textFontMetrics.top, textPaint);
    }

    /* Calculate and return background width.*/
    private float caculateBackgroundWidth(String text, DisplayMetrics displayMetrics, float textSize) {
        if (text.length() > 1) {
            Paint textPaint = new Paint();
            textPaint.setTextSize(textSize);
            Rect textRect = new Rect();
            // calculate textRect
            textPaint.getTextBounds(text, 0, text.length(), textRect);
            // Get context resource padding value.
            float padding = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4, displayMetrics);
            int textWidth = textRect.width();
            return textWidth + padding * 2;
        } else {
            // If has only one character, then return background width is equal to height.
            return this.backgroundHeight;
        }
    }
}

3. ReplacementSpanActivity.java

package com.dev2qa.example;

import android.graphics.Color;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.style.ReplacementSpan;
import android.widget.TextView;

import com.dev2qa.example.view.IconReplacementSpan;

import java.util.ArrayList;
import java.util.List;


public class ReplacementSpanActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_replacement_span);

        TextView replacementSpanTextView = (TextView) findViewById(R.id.textViewReplacementSpan);
        replacementSpanTextView.setTextSize(20);

        List<ReplacementSpan> spanList = new ArrayList<>();
        String content = "  This article will show you an example about how to use android ReplacementSpan to draw a text with customized background color at any place in your text.";
        // This span will add a Like text icon.
        IconReplacementSpan likeSpan = new IconReplacementSpan(getApplicationContext(), Color.BLUE, Color.WHITE, "Like");
        spanList.add(likeSpan);

        // This span will add a H text icon.
        IconReplacementSpan hSpan = new IconReplacementSpan(getApplicationContext(), Color.GREEN, Color.RED, "H");
        spanList.add(hSpan);

        SpannableString spannableString = new SpannableString(content);
        // Loop in the spanList.
        for (int i = 0; i < spanList.size(); i++) {
            spannableString.setSpan(spanList.get(i), i, i + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        }
        replacementSpanTextView.setText(spannableString);
    }
}

4. activity_replacement_span.xml

There are only one TextView component in this layout file.

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.dev2qa.example.ReplacementSpanActivity">

    <TextView
        android:id="@+id/textViewReplacementSpan"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="TextView"
        tools:layout_editor_absoluteY="0dp"
        tools:layout_editor_absoluteX="8dp" />
</android.support.constraint.ConstraintLayout>
(Visited 1,017 times, 3 visits today)
READ :   Android Change Screen Brightness Programmatically Example

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.