How to have an image with a dynamic text in it, all in a drawable, like the “today” action item on Google Calendar app?

ε祈祈猫儿з 提交于 2019-12-04 23:46:10

OK, found the answer about how to have the same font for the TextPaint of the Drawable class I've made:

mPaint.typeface = ResourcesCompat.getFont(context, R.font.lato)

The result:

Here's the full implementation of this class:

class TextDrawable(context: Context, text: CharSequence) : Drawable() {
    companion object {
        private val DEFAULT_COLOR = Color.WHITE
        private val DEFAULT_TEXT_SIZE_IN_DP = 12
    }

    private val mTextBounds = Rect()
    private val mPaint: TextPaint = TextPaint(Paint.ANTI_ALIAS_FLAG)
    private val mDrawable: Drawable?

    var text: CharSequence = text
        set (value) {
            field = value
            invalidateSelf()
        }

    init {
        mPaint.typeface = ResourcesCompat.getFont(context, R.font.lato)
        mPaint.color = DEFAULT_COLOR
        mPaint.textAlign = Align.CENTER
        val textSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_TEXT_SIZE_IN_DP.toFloat(), context.resources.displayMetrics)
        mPaint.textSize = textSize
        mDrawable = AppCompatResources.getDrawable(context, R.drawable.ic_backtodate)
        mDrawable!!.setBounds(0, 0, mDrawable.intrinsicWidth, mDrawable.intrinsicHeight)
    }

    override fun draw(canvas: Canvas) {
        val bounds = bounds
        mDrawable!!.draw(canvas)
        mPaint.getTextBounds(text.toString(), 0, text.length, mTextBounds);
        val textHeight = mTextBounds.bottom - mTextBounds.top
        canvas.drawText(text as String?, (bounds.right / 2).toFloat(), (bounds.bottom.toFloat() + textHeight + 1) / 2, mPaint)
    }

    override fun getOpacity(): Int = mPaint.alpha
    override fun getIntrinsicWidth(): Int = mDrawable!!.intrinsicWidth
    override fun getIntrinsicHeight(): Int = mDrawable!!.intrinsicHeight

    override fun setAlpha(alpha: Int) {
        mPaint.alpha = alpha
        invalidateSelf()
    }

    override fun setColorFilter(filter: ColorFilter?) {
        mPaint.colorFilter = filter
        invalidateSelf()
    }

}

EDIT: this code is now complete and works well. It should work fine, and is partially based on Calendar app itself, as was recommended to me to look at (here and here) .

Reference is made to your other question "How to fully mimic Action item view in the toolbar, for a customized one?"

I have incorporated the approach in my answer to the above-referenced question into your implementation of a custom drawable in your answer to this question. Below is a new version of TextDrawable.java that dynamically builds a boxed TextView for display as the desired icon for a menu item. It avoids drawing caches and simply manages a TextView internally for display.

TextDrawable.java

public class TextDrawable extends Drawable {
    private final int mIntrinsicSize;
    private final TextView mTextView;

    public TextDrawable(Context context, CharSequence text) {
        mIntrinsicSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DRAWABLE_SIZE,
                                                         context.getResources().getDisplayMetrics());
        mTextView = createTextView(context, text);
        mTextView.setWidth(mIntrinsicSize);
        mTextView.setHeight(mIntrinsicSize);
        mTextView.measure(mIntrinsicSize, mIntrinsicSize);
        mTextView.layout(0, 0, mIntrinsicSize, mIntrinsicSize);
    }

    private TextView createTextView(Context context, CharSequence text) {
        TextView textView = new TextView(context);
//        textView.setId(View.generateViewId()); // API 17+
        LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
            LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
        lp.gravity = Gravity.CENTER;
        textView.setLayoutParams(lp);
        textView.setGravity(Gravity.CENTER);
        textView.setBackgroundResource(R.drawable.ic_backtodate);
        textView.setTextColor(Color.WHITE);
        textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_TEXT_SIZE);
        textView.setText(text);
        return textView;
    }

    public void setText(CharSequence text) {
        mTextView.setText(text);
        invalidateSelf();
    }

    @Override
    public void draw(@NonNull Canvas canvas) {
        mTextView.draw(canvas);
    }

    @Override
    public int getOpacity() {
        return PixelFormat.OPAQUE;
    }

    @Override
    public int getIntrinsicWidth() {
        return mIntrinsicSize;
    }

    @Override
    public int getIntrinsicHeight() {
        return mIntrinsicSize;
    }

    @Override
    public void setAlpha(int alpha) {
    }

    @Override
    public void setColorFilter(ColorFilter filter) {
    }

    private static final int DRAWABLE_SIZE = 32; // device-independent pixels (DP)
    private static final int DEFAULT_TEXT_SIZE = 12; // device-independent pixels (DP)
}

Invoke this custom Drawable as follows (Kotlin):

mTextDrawable = TextDrawable(this, "1")
menu.add("goToToday").setIcon(mTextDrawable).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS)

To change the displayed date (Kotlin):

mTextDrawable?.setText(i.toString())    
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!