如何以可绘制的方式(其中包括“今天")以动态图片的形式显示图像. Google日历应用中的操作项? [英] How to have an image with a dynamic text in it, all in a drawable, like the "today" action item on Google Calendar app?

查看:74
本文介绍了如何以可绘制的方式(其中包括“今天")以动态图片的形式显示图像. Google日历应用中的操作项?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Google日历应用程序具有一个操作项,该操作项会根据当天(今天")动态更改:

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

我需要做一个非常相似的事情,但围绕文本的图像会稍有不同.

I'm required to do a very similar thing, but with a slightly different image that surrounds the text.

我确实通过创建一个包含文本和图像的Drawable使其成功工作(基于 此处 ).

I did succeed to make it work, by creating a Drawable that has text and image in it (based on here).

但是,我认为我做得不够好:

However, I'm don't think I did it well enough :

  1. 设备上的文本字体可能有所不同,因此可能与我写的内容不太吻合.
  2. 不确定是因为VectorDrawable还是文本,但我认为文本似乎不太居中.似乎在左边.如果我使用两位数,则尤其如此:

  1. 对于垂直居中,我认为我没有进行正确的计算.我在那儿尝试了更多合乎逻辑的事情,但它们并没有居中.

我尝试过的

这是完整的代码(也可以在项目中的 此处 ):

What I've tried

Here's the full code (also available here in a project) :

TextDrawable.java

public class TextDrawable extends Drawable {
    private static final int DEFAULT_COLOR = Color.WHITE;
    private static final int DRAWABLE_SIZE = 24;
    private static final int DEFAULT_TEXT_SIZE = 8;
    private Paint mPaint;
    private CharSequence mText;
    private final int mIntrinstSize;
    private final Drawable mDrawable;

    public TextDrawable(Context context, CharSequence text) {
        mText = text;
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(DEFAULT_COLOR);
        mPaint.setTextAlign(Align.CENTER);
        float textSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_TEXT_SIZE, context.getResources().getDisplayMetrics());
        mPaint.setTextSize(textSize);
        mIntrinstSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DRAWABLE_SIZE, context.getResources().getDisplayMetrics());
        mDrawable = AppCompatResources.getDrawable(context, R.drawable.ic_backtodate);
        mDrawable.setBounds(0, 0, mIntrinstSize, mIntrinstSize);
    }

    @Override
    public void draw(Canvas canvas) {
        Rect bounds = getBounds();
        mDrawable.draw(canvas);
        canvas.drawText(mText, 0, mText.length(),
                bounds.centerX(), bounds.centerY() + mPaint.getFontMetricsInt(null) / 3, mPaint); // this seems very wrong
    }

    @Override
    public int getOpacity() {
        return mPaint.getAlpha();
    }

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

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

    @Override
    public void setAlpha(int alpha) {
        mPaint.setAlpha(alpha);
    }

    @Override
    public void setColorFilter(ColorFilter filter) {
        mPaint.setColorFilter(filter);
    }
}

MainActivity.kt

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val drawable = TextDrawable(this, "1")
        imageView.setImageDrawable(drawable)
    }
}

ic_backtodate.xml

<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="25dp" android:height="25dp"
        android:viewportHeight="76.0" android:viewportWidth="76.0">
    <path
        android:fillColor="#ffffff" android:fillType="evenOdd"
        android:pathData="M47.294,60.997H28.704C21.148,60.997 15,54.755 15,47.083V28.905c0,-7.672 6.148,-13.913 13.704,-13.913h18.59C54.852,14.992 61,21.233 61,28.905v18.178c0,7.672 -6.148,13.914 -13.706,13.914zM57.592,28.905c0,-5.763 -4.62,-10.453 -10.298,-10.453h-18.59c-5.676,0 -10.296,4.69 -10.296,10.453v18.178c0,5.765 4.62,10.454 10.296,10.454h18.59c5.678,0 10.298,-4.689 10.298,-10.454z"/>
</vector>

问题

  1. 如何克服不同的字体问题?我已经在全球范围内使用"Lato"字体(不在示例应用程序中,而是在实际应用程序中,使用支持库的API,但将其内置到应用程序中),但我不认为Paint对象可以使用它,对吧?

  1. How can I overcome the different fonts issues? I already use "Lato" font globally (not in the sample app, but in the real app, using "downloaded fonts" API of the support library, but having them built into the app instead), but I don't think Paint Object can use it, right?

我怎样才能使文本居中对齐?

How can I center the text well?

我已经通过视图层次结构工具研究了Google日历在此部分的工作方式.在我看来,他们似乎只是使用TextView.他们是如何做到的呢?也许使用9补丁?但这对工具栏项目效果很好吗?

I've looked, via View-hierarchy tool, at how Google Calendar works for this part. To me it seems they just used TextView. How did they do it? Maybe using a 9-patch? But does it work well for Toolbar items?



目前,由于我的日程安排太紧,因此无法使用可绘制解决方案.知道如何做好仍然会很高兴.

For now, because I'm tight on schedule, I can't use the drawable solution. Would still be nice to know how to do it well.

我当前的解决方案不涉及此问题.我只是使用模仿普通动作项目的特殊视图.它不是完美的(不能完全模仿一个真实的动作项目),但是现在就足够了.由于它不是完美的,我在 此处 的新线程上写了它.

My current solution doesn't involve it. I just use a special view that mimics a normal action item. It's not perfect (doesn't fully mimics a real action item), but it will be enough for now. Because it's not perfect, I wrote about it on a new thread, here.

由于此方法实际上可以很好地工作,并且仍然可以作为常规操作项使用,所以我决定再次尝试一下.

since this actually can work well, and still stay as a normal action item, I've decided to give it another try.

我设法使文本居中,但是字体是现在的问题.看来,如果操作系统使用自己的字体,即使我将"Lato"设置为该应用程序之一,它也不会在我制作的可绘制对象中使用:

I've managed to center the text nicely, but the font is the issue now. It seems that if the OS uses a font of its own, even if I've set "Lato" to be the one of the app, it's not used in the drawable I've made:

我认为这是我需要在这里解决的最后一个问题.

I think it's the last issue I need to fix here.

代码如下:

styles.xml

    <item name="android:fontFamily" tools:targetApi="jelly_bean">@font/lato</item>
    <item name="fontFamily">@font/lato</item>

MainActivity.kt

class MainActivity : AppCompatActivity() {
    lateinit var textDrawable: TextDrawable

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        textDrawable = TextDrawable(this, "1")
        setSupportActionBar(toolbar)
        val handler = Handler()
        val runnable = object : Runnable {
            var i = 1
            override fun run() {
                if (isFinishing||isDestroyed)
                    return
                textDrawable.text = (i + 1).toString()
                i = (i + 1) % 31
                handler.postDelayed(this, 1000)
            }
        }
        runnable.run()
    }

    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        menu.add("goToToday").setIcon(textDrawable).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS)
        menu.add("asd").setIcon(R.drawable.abc_ic_menu_copy_mtrl_am_alpha).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS)
        return super.onCreateOptionsMenu(menu)
    }
}

TextDrawable.kt

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

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

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

    init {
        mPaint.color = DEFAULT_COLOR
        mPaint.textAlign = Align.CENTER
        val textSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_TEXT_SIZE.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)
        canvas.drawText(text, 0, text.length,
                bounds.centerX().toFloat(), (bounds.centerY() + mPaint.getFontMetricsInt(null) / 3).toFloat(), mPaint) // this seems very wrong, but seems to work fine
    }

    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()
    }

}



我想我已经找到了如何通过使用文本来设置字体:

I think I've found how to have a font for the text, by using :

mPaint.typeface=TypefaceCompat.createFromResourcesFamilyXml(...)

不确定如何填充参数.仍在调查...

Not sure though how to fill the parameters. Still investigating...

推荐答案

确定,找到了关于如何为我制作的Drawable类的TextPaint使用相同字体的答案:

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)

结果:

这是此类的完整实现:​​

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()
    }

}

此代码现已完成,可以正常运行.它应该可以正常工作,并且部分基于Calendar应用程序本身,正如我建议我看的那样( 此处 ).

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

这篇关于如何以可绘制的方式(其中包括“今天")以动态图片的形式显示图像. Google日历应用中的操作项?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆