【问题标题】:Android textview text get cut off on the sides with custom fontAndroid textview文本在自定义字体的两侧被截断
【发布时间】:2017-10-19 20:49:44
【问题描述】:

这是在预览版和设备上发生的情况:

TextView 没什么特别的,它只是加载自定义字体:

public class TestTextView extends AppCompatTextView {

    public TestTextView(Context context) {
        super(context);

        init(context);
    }

    public TestTextView(Context context, AttributeSet attrs) {
        super(context, attrs);

        init(context);
    }

    public TestTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        init(context);
    }

    void init(Context context) {

        Typeface t = Typeface.createFromAsset(context.getAssets(), "fonts/daisy.ttf");

        setTypeface(t);
    }
}

布局也很基础,但以防万一:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/material_red200"
    android:orientation="vertical">    

    <*custompackage* .TestTextView
        android:gravity="left"
        android:padding="0dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="just some text for testing"
        android:textColor="@color/material_black"
        android:textSize="100dp" />

</LinearLayout>

如您所见,左侧的部分,如 'j' 和 'f' 被切断了。

设置内边距或边距无效。

当从其他程序中使用时,此字体适合它的框架。

提前致谢。

编辑: @play_err_ 提到的不是我的解决方案。

  • 我在最终版本中使用了一个自动调整大小的文本视图,因此添加空格会非常困难。
  • 我需要解释一下为什么其他程序(例如 Photoshop、After Effects...)可以计算出合适的边界框而 android 不能
  • 我也在动态加载不同的字体,我不想创建一个

    if(badfont)
         addSpaces()
    

【问题讨论】:

  • 尝试删除android:padding="0dp"
  • @AlexanderTumanin 这对结果没有任何影响。
  • 尝试在最后一个字符后添加一个空格或为 TestTextView 设置一个固定宽度。
  • 字体文件可以共享吗?是否应用了任何自定义文本样式和边框?
  • @AmitKumar 这是我使用的字体:1001fonts.com/daisy-script-font.html

标签: android fonts textview


【解决方案1】:

这个答案让我走上了正确的道路: https://stackoverflow.com/a/28625166/4420543

所以,解决方案是创建一个自定义Textview并覆盖onDraw方法:

    @Override
    protected void onDraw(Canvas canvas) {
        final Paint paint = getPaint();
        final int color = paint.getColor();
        // Draw what you have to in transparent
        // This has to be drawn, otherwise getting values from layout throws exceptions
        setTextColor(Color.TRANSPARENT);
        super.onDraw(canvas);
        // setTextColor invalidates the view and causes an endless cycle
        paint.setColor(color);

        System.out.println("Drawing text info:");

        Layout layout = getLayout();
        String text = getText().toString();

        for (int i = 0; i < layout.getLineCount(); i++) {
            final int start = layout.getLineStart(i);
            final int end = layout.getLineEnd(i);

            String line = text.substring(start, end);

            System.out.println("Line:\t" + line);

            final float left = layout.getLineLeft(i);
            final int baseLine = layout.getLineBaseline(i);

            canvas.drawText(line,
                    left + getTotalPaddingLeft(),
                    // The text will not be clipped anymore
                    // You can add a padding here too, faster than string string concatenation
                    baseLine + getTotalPaddingTop(),
                    getPaint());
        }
    }

【讨论】:

  • @lasnow 确定不是。这是覆盖整个绘图机制,然后只使用数据来提供新的渲染。您必须自己渲染光标。
  • 是的,通过删除行 setTextColor(Color.TRANSPARENT); 光标和文本正确显示。现在它正在工作,谢谢!我还必须补充一点,以编程方式更改颜色不起作用。我必须覆盖 setTextColor 并将颜色保存在属性中,然后不要从 paint.getColor() 检索颜色:final int color = this.color != null ? this.color : paint.getColor();
  • 然后,您将使用与原始文本略有偏移的重复文本渲染所有内容。如果不是这种情况,那么您无论如何都不需要整个覆盖解决方案。
  • 我唯一知道的是斜体字体不起作用,现在使用您的解决方案并将颜色设置为透明正在起作用(仅使用 EditText 测试)
  • 我在用这个,剪切问题解决了,但是现在在同一个textView中显示了2个文本
【解决方案2】:

我在EditText 中使用某些字体时遇到了同样的问题。

我的第一次尝试是使用填充。视图大小增加,但文本仍被裁剪。

然后我查看了源代码TextView。在方法onDraw 中调用方法Canvas.clipRect 来执行此裁剪。

我在使用填充时绕过裁剪的解决方案:

1) 创建从Canvas 继承的自定义类并覆盖方法clipRect

public class NonClippableCanvas extends Canvas {

    public NonClippableCanvas(@NonNull Bitmap bitmap) {
        super(bitmap);
    }

    @Override
    public boolean clipRect(float left, float top, float right, float bottom) {
        return true;
    }
}

2) 创建自定义TextView 并覆盖方法onSizeChangedonDraw

onSizeChanged 方法中创建位图和画布。

onDraw 方法中,通过将我们的自定义Canvas 传递给方法super.onDraw 来绘制位图。接下来,在目标画布上绘制此位图。

public class CustomTextView extends AppCompatTextView {
    private Bitmap _bitmap;
    private NonClippableCanvas _canvas;

    @Override
    protected void onSizeChanged(final int width, final int height,
                             final int oldwidth, final int oldheight) {
        if (width != oldwidth || height != oldheight) {
            _bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
            _canvas = new NonClippableCanvas(_bitmap);
        }

        super.onSizeChanged(width, height, oldwidth, oldheight);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        _canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);

        super.onDraw(_canvas);

        canvas.drawBitmap(_bitmap, 0, 0, null);
    }
}

【讨论】:

  • 当我使用它时,它可以工作,但如果我需要缩放 TextView,它会显示模糊。
【解决方案3】:

我也遇到了同样的问题,我为那些使用TextView.shadowLayer的thouse找到了一个单一的解决方案。

这是基于[Dmitry Kopytov]带来here的源代码:

editTextOrTextView.setShadowLayer(editTextOrTextView.textSize, 0f, 0f, Color.TRANSPARENT)

就是这样,现在TextView.onDraw() 中的canvas.clipRect 不会切断卷曲字体的边了。

【讨论】:

  • 如果视图中有大量文本,这可能会导致严重的性能损失。
【解决方案4】:

重新设计的@Dmitry Kopytov 解决方案:

  • 在 Kotlin 中
  • 回收旧位图
  • 添加了文档
  • 如果无法创建位图(内存不足),则使用默认的 TextView 渲染

代码:

/**
 * This TextView is able to draw text on the padding area.
 * It's mainly used to support italic texts in custom fonts that can go out of bounds.
 * In this case, you've to set an horizontal padding (or just end padding).
 *
 * This implementation is doing a render-to-texture procedure, as such it consumes more RAM than a standard TextView,
 * it uses an additional bitmap of the size of the view.
 */
class TextViewNoClipping(context: Context, attrs: AttributeSet?) : AppCompatTextView(context, attrs) {
    private class NonClippableCanvas(@NonNull val bitmap: Bitmap) : Canvas(bitmap) {
        override fun clipRect(left: Float, top: Float, right: Float, bottom: Float): Boolean {
            return true
        }
    }

    private var rttCanvas: NonClippableCanvas? = null

    override fun onSizeChanged(width: Int, height: Int,
                               oldwidth: Int, oldheight: Int) {
        if ((width != oldwidth || height != oldheight) && width > 0 && height > 0) {
            rttCanvas?.bitmap?.recycle()
            try {
                Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)?.let {
                    rttCanvas = NonClippableCanvas(it)
                }
            } catch (t: Throwable) {
                // If for some reasons the bitmap cannot be created, we fall back on default rendering (potentially cropping the text).
                rttCanvas?.bitmap?.recycle()
                rttCanvas = null
            }
        }

        super.onSizeChanged(width, height, oldwidth, oldheight)
    }

    override fun onDraw(canvas: Canvas) {
        rttCanvas?.let {
            // Clear the RTT canvas from the previous font.
            it.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)

            // Draw on the RTT canvas (-> bitmap) that will use clipping on the NonClippableCanvas, resulting in no-clipping
            super.onDraw(it)

            // Finally draw the bitmap that contains the rendered text (no clipping used here, will display on top of padding)
            canvas.drawBitmap(it.bitmap, 0f, 0f, null)

        } ?: super.onDraw(canvas) // If rtt is not available, use default rendering process
    }
}

【讨论】:

    【解决方案5】:

    一种解决方法是在键入之前添加一个空格。它将为您节省大量编码,但会导致左侧“填充”。

    android:text=" text after a space"

    【讨论】:

    • 这不是一个好主意。此解决方案可能仅适用于单行文本。如果有多行,则必须计算哪些文本进入哪一行并在前面添加一个空格(加上当前行的内容是否在空格后到达下一行,或者如果一个单词对于单行来说太长)。此外,一个空间可能还不够。所以这只适用于非常特殊的情况。
    • 另一种解决方案是将文本框宽度增加几个dps并将文本放置在中心。这可以在活动 Java 文件中完成。
    • 这个提示对我的情况很有用。我需要输出 1-3 个符号,它们从侧面被剪掉。我不想用覆盖类做任何复杂和通用的事情。添加空格解决了我的问题。
    【解决方案6】:

    如果你将它包装在另一个布局中并添加填充呢?例如这样的:

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="24dp">
            <*custompackage* .TestTextView
            android:gravity="left"
            android:padding="0dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="just some text for testing"
            android:textColor="@color/material_black"
            android:textSize="100dp" />
    </RelativeLayout>
    

    没有你的字体和其他主题等我刚刚用草书字体尝试了它,在我的机器上它看起来像这样。 screenshot

    更新: 看起来您不是唯一一个遇到此问题的人,其他答案 herehere 不幸的是都与添加额外空格有关。

    我已经创建了一个 bug 票证 here,因为它在我看来像是一个 bug。

    【讨论】:

    • 我认为它仍然被切断。看第一个字符“j”
    • 哦,我的错。最初并没有真正注意到这一点。我只是假设这是字体的样式。
    • 填充导致文本视图变小(如预期),对剪切没有任何影响。
    • 是的,做了一些谷歌搜索似乎人们对这个问题提出的唯一“修复”是添加额外的空格。正如我在上面的回答中提到的那样,我已经用谷歌创建了一个错误票(不确定他们是否会回应,但值得一试)。
    • @Kai Bug 票是个好主意。我也很好奇他们是否会回应。
    【解决方案7】:

    TextView.BufferType.SPANNABLE 替换为TextView.BufferType.NORMAL

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-10-04
      • 1970-01-01
      • 2014-12-01
      • 2015-04-19
      • 1970-01-01
      • 2012-01-24
      • 2018-03-20
      相关资源
      最近更新 更多