如何减少TextView的行间距

9
我正在尝试通过在TextView中设置负的“add”值来减小行间距。这个方法很有效,但底部的一行会被截断。
主要布局。
<TextView
    android:id="@+id/text_view"
    android:padding="dp"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerHorizontal="true"
    android:layout_centerVertical="true"
    tools:context=".MainActivity" />

主要活动:(注意


package com.font_test;

import android.app.Activity;
import android.graphics.Typeface;
import android.os.Bundle;
import android.widget.TextView;

public class MainActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final Typeface typeface = Typeface.createFromAsset(getAssets(), "fonts/custom_fonts.ttf");
        final TextView tv = (TextView) findViewById(R.id.text_view);
        tv.setTypeface(typeface);
        tv.setTextSize(60);
        tv.setLineSpacing(-30f, 1f);  // *** -30 to reduce line spacing
        tv.setBackgroundColor(0x280000ff);
        tv.setText("gggkiiikkk" + "\n" + "gikgikgik" + "\n" + "kigkigkig");
    }
}

这导致视图底部被截断(请注意底行的“g”): 减小行距会削掉底行的 'g' 似乎问题与错误的布局测量有关。如果我将TextView设置为
 android:layout_height="fill_parent"

它确实可以正确地呈现:

使用'fill_parent'不会截断底部线

有什么办法可以修复它?如果有帮助,我不介意使用丑陋的解决方法。我还可以访问FontForge并修改字体文件(如果需要)。


它是否也发生在内置字体上?或者其他自定义字体?可能是字体没有报告正确的下降值。 - Kevin Coppock
1
在最后一行也应用了“LineSpacing of -30f”。这就是为什么最后一行无法正确显示的原因。所以你可以在你的情况下设置底部填充为30...@kcoppock我认为“descent values”没有任何问题。 - Mohsin Naeem
@kcoppock,我使用typeface = Typeface.SANS_SERIF;时遇到了相同的问题。 - user1139880
@Mohsin,添加android:paddingBottom="60dp"在底部增加了一个边距,但字体仍然被截断。请参考http://imgur.com/du5xJ。 - user1139880
7个回答

10

littleFluffyKittys的回答很好,但如果通过XML设置行间距,则在某些设备上它不起作用。

我通过比较字体的原始高度和textview计算出的一行高度来计算所需的额外高度。 如果行高小于字体高度,则将差值添加一次。

这适用于至少API 10,可能更低(只是没有测试过更低的版本)。

public class ReducedLineSpacingTextView extends TextView {

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

    public ReducedLineSpacingTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public ReducedLineSpacingTextView(Context context) {
        super(context);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int truncatedHeight = getPaint().getFontMetricsInt(null) - getLineHeight();
        if (truncatedHeight > 0) {
            setMeasuredDimension(getMeasuredWidth(), getMeasuredHeight() + truncatedHeight);
        }
    }

}

1
这绝对是最好的解决方案。 - Dave Thomas
如果我需要通过编程来减小行间距,即setLineSpacing(float, float)在你的解决方案中的作用是什么? - ericn

6

我遇到了同样的问题,但当我尝试使用小于1的间距倍增器时。

我创建了一个TextView的子类,它可以自动修复最后一行的截断,并且不需要在底部设置已知/固定的间距。

只需使用这个类,你可以正常使用它,你不需要应用任何额外的间距或修复。

public class ReducedLineSpacingTextView extends TextView {

    private boolean mNegativeLineSpacing = false;

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

    public ReducedLineSpacingTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public ReducedLineSpacingTextView(Context context) {
        super(context);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (mNegativeLineSpacing) { // If you are only supporting Api Level 16 and up, you could use the getLineSpacingExtra() and getLineSpacingMultiplier() methods here to check for a less than 1 spacing instead.
            Layout layout = getLayout();
            int truncatedHeight = layout.getLineDescent(layout.getLineCount()-1);
            setMeasuredDimension(getMeasuredWidth(), getMeasuredHeight() + truncatedHeight);
        }
    }

    @Override
    public void setLineSpacing(float add, float mult) {
        mNegativeLineSpacing = add < 0 || mult < 1;
        super.setLineSpacing(add, mult);
    }

}

1

很好! 这会完成工作,但是在任何有变量的地方放置常量值都不是一个好主意。您可以使用lineSpacing值以动态方式将它们添加到onMeasure方法中。 请注意,这些值始终可通过“getLineSpacingExtra()”和“getLineSpacingMultiplier()”获得。或者更简单的是,您可以将两个值相加得到“getLineHeight()”。

虽然我觉得这个值应该包含在onMeasure方法中,但您始终可以测量您需要的确切高度,然后进行简单的检查:

final int measuredHeight = getMeasuredHeight();
if (measuredHeight < neededHeight) {
    setMeasuredDimension(getMeasuredWidth, neededHeight);   
}

最后一件事,您不需要在单独的属性中传递上下文。如果您查看构造函数,上下文已经存在。如果您需要在组件代码中使用它,只需使用“getContext()”即可。
希望能有所帮助。

1

使用此功能可减少文本视图中的行间距。

android:lineSpacingMultiplier="0.8"

**


0

这是我根据Jose在这里的答案所做的,看起来它能够工作。我对布局机制的复杂性不是很熟悉。这段代码安全吗?有什么问题吗?

布局:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <com.font_test.MyTextView
        android:id="@+id/text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        tools:context=".MainActivity" />

</RelativeLayout>

添加了一个自定义TextView,将垂直高度延伸N个像素:

package com.font_test;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.TextView;

public class MyTextView extends TextView {

    public MyTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        // TODO: provide an API to set extra bottom pixels (50 in this example)
        setMeasuredDimension(getMeasuredWidth(), getMeasuredHeight() + 50);       
    }
}

结果文本视图呈现时不会在底部截断:

enter image description here


这对你真的有用吗?对我来说,它的作用与底部填充相同-会向TextView添加间距,但最后一行仍然会被裁剪。 - jab11

0
只需在您的TextView xml声明中添加paddingBottom,选择一个产生良好结果的值。然后分别为其他填充(顶部、左侧和右侧)设置值。这应该可以解决您的问题。

将android:paddingBottom="60dp" 添加到TextView中添加了填充,但是底部字体仍被截断。请参见http://imgur.com/du5xJ。 - user1139880

0
如果填充不起作用,边距应该能解决问题。如果您仍然有问题,您可以将行距值应用于视图的onMeasure方法。您需要创建一个自定义组件并扩展onMeasure函数。

将 android:layout_margin="20dp" 或 android:layout_marginBottom="20dp" 添加到 TextView 上并没有帮助。最后一行仍然被截断了。我认为这个边距是在 TextView 确定其所需测量值之后,由父视图添加的。我还会尝试使用您提出的自定义组件建议。 - user1139880
谢谢Jose。自定义组件的想法似乎有效。请查看我的回答。将您的答案标记为正确答案。 - user1139880
我发布了一些修复此错误的TextView代码。希望能有所帮助。https://dev59.com/AGfWa4cB1Zd3GeqPejFu#13386208 - cottonBallPaws
这不是问题的解决方案,实际上下面有更好的解决方案。 - Tosa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接