Android textview文本在自定义字体的两侧被切断

时间:2017-05-19 16:29:16

标签: android fonts textview

这是在预览和设备上发生的事情: Text bug

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>

如您所见,左侧部分,如&#39; j&#39;和&#39; f&#39;被切断了。

设置填充或边距不起作用。

从其他程序使用时,此字体符合其框架。

提前致谢。

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

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

    if(badfont)
         addSpaces()
    

8 个答案:

答案 0 :(得分:10)

这个答案让我走上了正确的道路: 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());
        }
    }

答案 1 :(得分:4)

我遇到了同样的问题,我找到了一个使用TextView.shadowLayer的thouse班轮解决方案。

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

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

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

答案 2 :(得分:2)

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

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

enter image description here

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

enter image description here

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

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 TextView {
    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);
    }
}

enter image description here

答案 3 :(得分:0)

android:gravity="center"

或使用

android:layout_paddingleft="value"

希望它能起作用..

答案 4 :(得分:0)

如果将其包装在另一个布局中并为其添加填充怎么办?例如:

<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都不幸与添加额外空格有关。

我已经创建了错误故障单 here,因为它看起来像是一个错误。

答案 5 :(得分:0)

一种解决方法是在键入之前添加一个空格。这样可以节省大量代码,但会导致左侧出现“填充”。

android:text=" text after a space"

答案 6 :(得分:0)

重做的@Dmitry Kopytov解决方案:

  • 在科特林
  • 回收旧位图
  • 添加了文档
  • 如果无法创建位图(内存不足),请返回默认的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
    }
}

答案 7 :(得分:0)

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