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

前端 未结 2 1535
無奈伤痛
無奈伤痛 2021-02-19 07:43

Background

Google Calendar app has an action item that dynamically change according to the current day (\"today\"):

I\'m required to do a very similar

相关标签:
2条回答
  • 2021-02-19 08:19

    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())    
    
    0 讨论(0)
  • 2021-02-19 08:20

    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) .

    0 讨论(0)
提交回复
热议问题