Цветная коробка с письмами а-ля Gmail

Мне было интересно, как они генерируются и генерируются ли они каждый раз, когда я открываю приложение, или сохраняются (кешируются).

Это просто холст (программно) или они используют XML? Примерно так, а потом программно добавляют букву:

<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
    <size android:width="1px" android:height="1dp"/>
    <solid android:color="#FFFFF5EE/>
</shape>

person Marcos    schedule 16.04.2014    source источник


Ответы (2)


генерируются каждый раз, когда я открываю приложение, или сохраняются (кешируются)

Маленький столбец A, маленький столбец B. Используется небольшой кеш, но это не то, что вы думаете. Каждую плитку можно разделить на 4 части, кэш используется для извлечения определенной позиции. Что касается фона, цвета сопоставляются с использованием String.hashCode, чтобы гарантировать, что один и тот же адрес электронной почты (или ключ) всегда будет отображаться на один и тот же цвет. Но фактическая буква рисуется с использованием Canvas.drawText.

Вот пример:

/**
 * Used to create a {@link Bitmap} that contains a letter used in the English
 * alphabet or digit, if there is no letter or digit available, a default image
 * is shown instead
 */
public class LetterTileProvider {

    /** The number of available tile colors (see R.array.letter_tile_colors) */
    private static final int NUM_OF_TILE_COLORS = 8;

    /** The {@link TextPaint} used to draw the letter onto the tile */
    private final TextPaint mPaint = new TextPaint();
    /** The bounds that enclose the letter */
    private final Rect mBounds = new Rect();
    /** The {@link Canvas} to draw on */
    private final Canvas mCanvas = new Canvas();
    /** The first char of the name being displayed */
    private final char[] mFirstChar = new char[1];

    /** The background colors of the tile */
    private final TypedArray mColors;
    /** The font size used to display the letter */
    private final int mTileLetterFontSize;
    /** The default image to display */
    private final Bitmap mDefaultBitmap;

    /**
     * Constructor for <code>LetterTileProvider</code>
     * 
     * @param context The {@link Context} to use
     */
    public LetterTileProvider(Context context) {
        final Resources res = context.getResources();

        mPaint.setTypeface(Typeface.create("sans-serif-light", Typeface.NORMAL));
        mPaint.setColor(Color.WHITE);
        mPaint.setTextAlign(Align.CENTER);
        mPaint.setAntiAlias(true);

        mColors = res.obtainTypedArray(R.array.letter_tile_colors);
        mTileLetterFontSize = res.getDimensionPixelSize(R.dimen.tile_letter_font_size);

        mDefaultBitmap = BitmapFactory.decodeResource(res, android.R.drawable.sym_def_app_icon);
    }

    /**
     * @param displayName The name used to create the letter for the tile
     * @param key The key used to generate the background color for the tile
     * @param width The desired width of the tile
     * @param height The desired height of the tile
     * @return A {@link Bitmap} that contains a letter used in the English
     *         alphabet or digit, if there is no letter or digit available, a
     *         default image is shown instead
     */
    public Bitmap getLetterTile(String displayName, String key, int width, int height) {
        final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        final char firstChar = displayName.charAt(0);

        final Canvas c = mCanvas;
        c.setBitmap(bitmap);
        c.drawColor(pickColor(key));

        if (isEnglishLetterOrDigit(firstChar)) {
            mFirstChar[0] = Character.toUpperCase(firstChar);
            mPaint.setTextSize(mTileLetterFontSize);
            mPaint.getTextBounds(mFirstChar, 0, 1, mBounds);
            c.drawText(mFirstChar, 0, 1, 0 + width / 2, 0 + height / 2
                    + (mBounds.bottom - mBounds.top) / 2, mPaint);
        } else {
            c.drawBitmap(mDefaultBitmap, 0, 0, null);
        }
        return bitmap;
    }

    /**
     * @param c The char to check
     * @return True if <code>c</code> is in the English alphabet or is a digit,
     *         false otherwise
     */
    private static boolean isEnglishLetterOrDigit(char c) {
        return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9';
    }

    /**
     * @param key The key used to generate the tile color
     * @return A new or previously chosen color for <code>key</code> used as the
     *         tile background color
     */
    private int pickColor(String key) {
        // String.hashCode() is not supposed to change across java versions, so
        // this should guarantee the same key always maps to the same color
        final int color = Math.abs(key.hashCode()) % NUM_OF_TILE_COLORS;
        try {
            return mColors.getColor(color, Color.BLACK);
        } finally {
            mColors.recycle();
        }
    }

}

Вот цвета по умолчанию и размер текста:

<!-- All of the possible tile background colors -->
<array name="letter_tile_colors">
    <item>#f16364</item>
    <item>#f58559</item>
    <item>#f9a43e</item>
    <item>#e4c62e</item>
    <item>#67bf74</item>
    <item>#59a2be</item>
    <item>#2093cd</item>
    <item>#ad62a7</item>
</array>

<!-- The default letter tile text size -->
<dimen name="tile_letter_font_size">33sp</dimen>
<!-- The deafult tile size -->
<dimen name="letter_tile_size">64dp</dimen>

Реализация

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    final Resources res = getResources();
    final int tileSize = res.getDimensionPixelSize(R.dimen.letter_tile_size);

    final LetterTileProvider tileProvider = new LetterTileProvider(this);
    final Bitmap letterTile = tileProvider.getLetterTile("name", "key", tileSize, tileSize);

    getActionBar().setIcon(new BitmapDrawable(getResources(), letterTile));
}

Результаты "T", "E", "S", "T":

T

E

S

T

person adneal    schedule 17.04.2014
comment
какой должен быть ключ? - person SMahdiS; 13.07.2015
comment
Похоже, что код получен из почтового приложения и находится под лицензией Apache License 2.0. - person Phil; 26.08.2015
comment
Это очень полезно. Спасибо - person Christopher Kikoti; 14.09.2016
comment
Ключ - это источник, в котором получена первая буква @SMahdiS. - person Christopher Kikoti; 14.09.2016

Основываясь на ответе @adneal, если мы хотим создавать круглые значки, мы можем изменить функцию getLetterTile следующим образом:

public Bitmap getLetterTile(String displayName, String key, int width, int height, boolean round) {
    final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    final char firstChar = displayName.charAt(0);

    final Canvas c = mCanvas;
    c.setBitmap(bitmap);

    if (round) {
        Paint paint = new Paint();
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(pickColor(key));
        c.drawCircle(width/2, height/2 , width/2, paint);
    } else {
        c.drawColor(pickColor(key));
    }

    if (isEnglishLetterOrDigit(firstChar)) {
        mFirstChar[0] = Character.toUpperCase(firstChar);
        mPaint.setTextSize(mTileLetterFontSize);
        mPaint.getTextBounds(mFirstChar, 0, 1, mBounds);
        c.drawText(mFirstChar, 0, 1, 0 + width / 2, 0 + height / 2
                + (mBounds.bottom - mBounds.top) / 2, mPaint);
    } else {
        c.drawBitmap(mDefaultBitmap, 0, 0, null);
    }
    return bitmap;
}
person Jose Gómez    schedule 01.12.2017
comment
Это также может быть улучшено с помощью кэширования - person TheRealChx101; 28.09.2019
comment
@ TheRealChx101 Конечно, хотя я не думаю, что это будет иметь значение, так как это очень быстро и вызывается только тогда, когда требуется рендеринг (onCreate и, возможно, при изменении элемента). - person Jose Gómez; 06.10.2019