public final class

TypefaceEmojiSpan

extends EmojiSpan

 java.lang.Object

↳ReplacementSpan

androidx.emoji2.text.EmojiSpan

↳androidx.emoji2.text.TypefaceEmojiSpan

Gradle dependencies

compile group: 'androidx.emoji2', name: 'emoji2', version: '1.5.0'

  • groupId: androidx.emoji2
  • artifactId: emoji2
  • version: 1.5.0

Artifact androidx.emoji2:emoji2:1.5.0 it located at Google repository (https://maven.google.com/)

Overview

EmojiSpan subclass used to render emojis using Typeface.

Summary

Constructors
publicTypefaceEmojiSpan(TypefaceEmojiRasterizer metadata)

Default constructor.

Methods
public voiddraw(Canvas canvas, java.lang.CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint)

from EmojiSpangetHeight, getId, getSize, getTypefaceRasterizer
from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Constructors

public TypefaceEmojiSpan(TypefaceEmojiRasterizer metadata)

Default constructor.

Parameters:

metadata: metadata representing the emoji that this span will draw

Methods

public void draw(Canvas canvas, java.lang.CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint)

Source

/*
 * Copyright 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package androidx.emoji2.text;

import android.annotation.SuppressLint;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.text.Spanned;
import android.text.TextPaint;
import android.text.style.CharacterStyle;
import android.text.style.MetricAffectingSpan;

import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;

/**
 * EmojiSpan subclass used to render emojis using Typeface.
 *
 */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public final class TypefaceEmojiSpan extends EmojiSpan {

    /**
     * Paint object used to draw a background in debug mode.
     */
    private static @Nullable Paint sDebugPaint;
    @Nullable
    private TextPaint mWorkingPaint;

    /**
     * Default constructor.
     *
     * @param metadata metadata representing the emoji that this span will draw
     */
    public TypefaceEmojiSpan(final @NonNull TypefaceEmojiRasterizer metadata) {
        super(metadata);
    }

    @Override
    public void draw(@NonNull final Canvas canvas,
            @SuppressLint("UnknownNullness") final CharSequence text,
            @IntRange(from = 0) final int start, @IntRange(from = 0) final int end, final float x,
            final int top, final int y, final int bottom, @NonNull final Paint paint) {
        @Nullable TextPaint textPaint = applyCharacterSpanStyles(text, start, end, paint);
        if (textPaint != null && textPaint.bgColor != 0) {
            drawBackground(canvas, textPaint, x, x + getWidth(), top, bottom);
        }
        if (EmojiCompat.get().isEmojiSpanIndicatorEnabled()) {
            canvas.drawRect(x, top , x + getWidth(), bottom, getDebugPaint());
        }
        getTypefaceRasterizer().draw(canvas, x, y, textPaint != null ? textPaint : paint);
    }

    // compat behavior with TextLine.java#handleText background drawing
    void drawBackground(Canvas c, TextPaint textPaint, float leftX, float rightX, float top,
            float bottom) {
        int previousColor = textPaint.getColor();
        Paint.Style previousStyle = textPaint.getStyle();

        textPaint.setColor(textPaint.bgColor);
        textPaint.setStyle(Paint.Style.FILL);
        c.drawRect(leftX, top, rightX, bottom, textPaint);

        textPaint.setStyle(previousStyle);
        textPaint.setColor(previousColor);
    }

    /**
     * This applies the CharacterSpanStyles that _would_ have been applied to this character by
     * StaticLayout.
     *
     * StaticLayout applies CharacterSpanStyles _after_ calling ReplacementSpan.draw, which means
     * BackgroundSpan will not be applied before draw is called.
     *
     * If any CharacterSpanStyles would impact _this_ location, apply them to a TextPaint to
     * determine if a background needs draw prior to the emoji.
     *
     * @param text text that this span is part of
     * @param start start position to replace
     * @param end end position to replace
     * @param paint paint (from TextLine)
     * @return TextPaint configured
     */
    @Nullable
    private TextPaint applyCharacterSpanStyles(@Nullable CharSequence text, int start, int end,
            Paint paint) {
        if (text instanceof Spanned) {
            Spanned spanned = (Spanned) text;
            CharacterStyle[] spans = spanned.getSpans(start, end, CharacterStyle.class);
            if (spans.length == 0 || (spans.length == 1 && spans[0] == this)) {
                if (paint instanceof TextPaint) {
                    // happy path goes here, retain color and bgColor from caller
                    return (TextPaint) paint;
                } else {
                    return null;
                }
            }
            // there are some non-TypefaceEmojiSpan character styles to apply, update a working
            // paint to apply each span style, like TextLine would have.
            TextPaint wp = mWorkingPaint;
            if (wp == null) {
                wp = new TextPaint();
                mWorkingPaint = wp;
            }
            wp.set(paint);
            //noinspection ForLoopReplaceableByForEach
            for (int pos = 0; pos < spans.length; pos++) {
                if (!(spans[pos] instanceof MetricAffectingSpan)) {
                    // we're in draw, so at this point we can't do anything to metrics don't try
                    spans[pos].updateDrawState(wp);
                }
            }
            return wp;
        } else {
            if (paint instanceof TextPaint) {
                // retain any color and bgColor from caller
                return (TextPaint) paint;
            } else {
                return null;
            }
        }

    }

    @NonNull
    private static Paint getDebugPaint() {
        if (sDebugPaint == null) {
            sDebugPaint = new TextPaint();
            sDebugPaint.setColor(EmojiCompat.get().getEmojiSpanIndicatorColor());
            sDebugPaint.setStyle(Paint.Style.FILL);
        }
        return sDebugPaint;
    }


}