题:为什么ImageSpan ALIGN_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);
}
}
Run Code Online (Sandbox Code Playgroud)
我的布局中有两个视图: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>
Run Code Online (Sandbox Code Playgroud)
如您所见,该线与 toTextView的基线正确对齐,但ImageSpan略低于基线。
那只是,好吧,错了。drawable 的垂直放置是关闭的,因为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();
}
};
Run Code Online (Sandbox Code Playgroud)
此代码生成以下图像,我认为这是正确的。
为什么盒子没有绘制在基线上?
这是从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();
}
Run Code Online (Sandbox Code Playgroud)
以及DynamicDrawableSpan#draw()的文档。draw()我们感兴趣的唯一论点是bottom:
底部整数:线的底部。
对于解决方案代码和使用的模拟器,transY计算DynamicDrawableSpan为 94,即框将从顶部绘制 94 个像素。top是 178 而基线被定义为零,所以 178 - 94 = 84 像素,这是框或b.getBounds().bottom. 这检查出来。
在DynamicDrawableSpan#draw()同一个模拟器中,bottom = 224 而 drawable 的高度仍然是 84,如上所示。为什么是底部 224?这是字体的行高。
transY现在计算为 224 - 84 = 140 像素。这会将框的底部放在线的底部,但低于基线。
又做了一项调整:
transY -= paint.getFontMetricsInt().descent;
Run Code Online (Sandbox Code Playgroud)
在测试中, paint.getFontMetricsInt().descent是 41,所以transY现在变成了 99。由于 94 是正确答案,99 将框放低了 5 个像素,这就是你看到的。
有关Android字体指标的说明,请参阅Android 101:排版。
| 归档时间: |
|
| 查看次数: |
808 次 |
| 最近记录: |