Как уменьшить интервал между строками TextView

Я пытаюсь уменьшить интервал между строками в TextView, установив отрицательное "добавить" в TextView.setLineSpacing(). Он работает хорошо, за исключением того, что нижняя строка усекается.

основная разметка

<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"в нижней строке):

Reducing the line spacing cuts the 'g' at the bottom line

кажется, что проблема связано с неправильным измерением макета. Если я установлю TextView в

 android:layout_height="fill_parent"

Это правильно:

Using 'fill_parent' does not truncate the bottom line

есть идеи, как это исправить? Я не против уродливых обходных путей, если это поможет. У меня также есть доступ к FontForge, и я могу изменить файл шрифта, если это необходимо.

7 ответов


Если заполнение не работает, маржа должна выполнять эту работу. Если у вас все еще есть проблема, вы всегда можете применить значение межстрочного интервала к методу onMeasure представления. Вам придется создать пользовательский компонент для этого и расширьте onMeasure.


littleFluffyKittys ответ хорош, но он не работал на некоторых устройствах, если linespacing был установлен через xml

я вычисляю дополнительную высоту, необходимую, сравнивая исходную высоту шрифта с высотой, которую textview вычисляет для строки. Если высота строки меньше высоты шрифта, разница добавляется один раз.

это работает по крайней мере до API 10 propably ниже (просто не тестируется ниже)

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.

Я создал подкласс 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);
    }

}

приятно! Это сделает работу, но это никогда не хорошая идея, чтобы поместить значения констант везде, где у нас есть переменные. Вы можете использовать значения lineSpacing, чтобы добавить их в метод onMeasure способом dinamyc. Обратите внимание, что эти значения всегда доступны через "getLineSpacingExtra()" и "getLineSpacingMultiplier()". Или еще проще вы можете получить значение обоих суммированных: "getLineHeight ()".

хотя мне кажется, что это значение должно быть включено в метод onMeasure, вы можете всегда измеряйте точную высоту, которая вам нужна, а затем сделайте простую проверку:

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

и последнее, вам не нужно передавать контекст в отдельном атрибуте. Если вы посмотрите на свои конструкторы, контекст уже существует. Если вам нужен код вашего компонента, вы можете просто использовать " getContext ()".

надеюсь, что это помогает.


использовать это, чтобы уменьшить межстрочный интервал в тексте **

android: lineSpacingMultiplier= "0.8"

**


просто добавьте paddingBottom в объявление вашего TextView xml, выберите значение, которое дает хороший результат. И, следовательно, установить значения для других прокладок (сверху, пусть и справа). Это должно исправить вашу проблему.


это то, что я сделал, основываясь на ответе Хосе здесь, и, похоже, работает. Я не очень хорошо знаком со сложным механизмом компоновки. Этот код безопасен? Каких либо проблем с ним?

планировка:

<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