问题:为什么ImageSpanALIGN_BASELINE没有与基线精确对齐,以及如何解决此对齐问题?
在我的活动中,我创建一个SpannableString并用ImageSpan替换字符串的一部分。ImageSpan使用24x24像素纯黑色PNG图像并设置为ALIGN_BASELINE

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        TextView myTextView = findViewById(R.id.myTextView);
        SpannableString ss = new SpannableString("Hello world!");

        Drawable d = ContextCompat.getDrawable(this, R.drawable.box);
        d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
        ImageSpan imageSpan = new ImageSpan(d, ImageSpan.ALIGN_BASELINE);
        ss.setSpan(imageSpan, 2, 5, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);

        myTextView.setText(ss);
    }
}

我的布局中有两个视图:aTextView和aView来显示基线
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    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">

    <TextView
        android:id="@+id/myTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#ff9800"
        android:textSize="48sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <View
        android:layout_width="match_parent"
        android:layout_height="1px"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintBaseline_toBaselineOf="@id/myTextView"
        android:background="#de4caf50"/>

</android.support.constraint.ConstraintLayout>

如您所见,该行与TextView的基线正确对齐,但ImageSpan略低于基线。
android - 如何使ImageSpan按基线对齐-LMLPHP

最佳答案

只是,好吧,错了。由于ALIGN_BASELINE定义如下,可拉伸的垂直放置处于关闭状态:
一个常量,指示此范围的底部应与周围文本的基线对齐。
这显然没有发生。请看这篇文章的结尾,看看出了什么问题。我建议如下解决方法:

// Override draw() to place the drawable correctly when ALIGN_BASELINE.
ImageSpan imageSpan = new ImageSpan(d, ImageSpan.ALIGN_BASELINE) {
    public void draw(Canvas canvas, CharSequence text, int start,
                     int end, float x, int top, int y, int bottom,
                     @NonNull Paint paint) {
        if (mVerticalAlignment != ALIGN_BASELINE) {
            super.draw(canvas, text, start, end, x, top, y, bottom, paint);
            return;
        }
        Drawable b = getDrawable();
        canvas.save();
        // If we set transY = 0, then the drawable will be drawn at the top of the text.
        // y is the the distance from the baseline to the top of the text, so
        // transY = y will draw the top of the drawable on the baseline. We want the             // bottom of the drawable on the baseline, so we subtract the height
        // of the drawable.
        int transY = y - b.getBounds().bottom;
        canvas.translate(x, transY);
        b.draw(canvas);

        canvas.restore();
    }
};

这段代码生成了下面的图像,我认为是正确的。
android - 如何使ImageSpan按基线对齐-LMLPHP
为什么方框没有画在基线上?
以下是从DynamicDrawableSpan#draw()中绘制框的源代码:
public void draw(@NonNull Canvas canvas, CharSequence text,
        @IntRange(from = 0) int start, @IntRange(from = 0) int end, float x,
        int top, int y, int bottom, @NonNull Paint paint) {
    Drawable b = getCachedDrawable();
    canvas.save();
    int transY = bottom - b.getBounds().bottom;
    if (mVerticalAlignment == ALIGN_BASELINE) {
        transY -= paint.getFontMetricsInt().descent;
    }
    canvas.translate(x, transY);
    b.draw(canvas);
    canvas.restore();
}

以及DynamicDrawableSpan#draw()的文档。唯一让我们感兴趣的论点是:
底线:底线。
对于所使用的解决方案代码和模拟器,draw()bottom作为94计算,即,框将从顶部绘制94像素。transY是178,而基线被定义为零,因此178-94=84像素,即框的高度或DynamicDrawableSpan。这就可以了。
在同一个模拟器的top中,底部=224,而可拉伸的高度仍为84,如上图所示。为什么垫底224?这是字体的行高。
b.getBounds().bottom现在计算为224-84=140像素。这将使框的底部位于行的底部,但低于基线。
再做一次调整:
transY -= paint.getFontMetricsInt().descent;

在测试中,DynamicDrawableSpan#draw()是41,所以现在transY变成99。因为94是正确的答案,99把盒子放得太低了5个像素,这就是你看到的。
有关android字体度量的说明,请参见Android 101: Typography

关于android - 如何使ImageSpan按基线对齐,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/53541301/

10-10 01:49