Canvas에 그릴 텍스트 높이 측정 (Android)


132

텍스트 높이를 측정하는 간단한 방법은 무엇입니까? 내가 지금하고있는 방법은 페인트를 사용 measureText()하여 너비를 얻은 다음 시행 착오를 통해 대략적인 높이를 얻는 것입니다. 나는 또한 엉망 FontMetrics이되었지만, 이것들은 모두 대략적인 방법처럼 보입니다.

다른 해상도로 조정하려고합니다. 나는 그것을 할 수 있지만, 상대적인 크기를 결정하기 위해 많은 계산으로 엄청나게 장황한 코드로 끝납니다. 난 싫어! 더 좋은 방법이 있어야합니다.

답변:


136

paint.getTextBounds () (객체 메소드) 는 어떻습니까 ?


1
텍스트 높이를 평가할 때 매우 이상한 결과가 발생합니다. 짧은 텍스트의 높이는 12이고 REALLY 긴 텍스트의 높이는 16입니다 (글꼴 크기는 16 임). 나에게 이해가되지 않습니다 (안드로이드 2.3.3)
AgentKnopf

35
높이의 차이는 텍스트에 하강자가있는 곳입니다. 예를 들어 '높음'은 줄 아래 g 부분으로 인해 '낮음'보다 큽니다.
FrinkTheBrave

208

필요한 것에 따라 높이를 측정하는 방법은 여러 가지가 있습니다.

getTextBounds

적은 양의 고정 텍스트를 정확하게 가운데에 맞추는 것과 같은 일을하는 경우 원할 것 getTextBounds입니다. 이처럼 경계 사각형을 얻을 수 있습니다

Rect bounds = new Rect();
mTextPaint.getTextBounds(mText, 0, mText.length(), bounds);
int height = bounds.height();

다음 이미지에서 볼 수 있듯이 문자열마다 다른 높이가 표시됩니다 (빨간색으로 표시).

여기에 이미지 설명을 입력하십시오

텍스트의 크기에 관계없이 일정한 높이가 필요한 경우에 따라 높이가 다른 것이 단점이 될 수 있습니다. 다음 섹션을 참조하십시오.

Paint.FontMetrics

글꼴 메트릭에서 글꼴의 높이를 계산할 수 있습니다. 높이는 특정 텍스트 문자열이 아닌 글꼴에서 가져 오기 때문에 항상 동일합니다.

Paint.FontMetrics fm = mTextPaint.getFontMetrics();
float height = fm.descent - fm.ascent;

기준선은 텍스트가있는 선입니다. 하강은 일반적으로 캐릭터가 선 아래로 가장 먼 곳이며 상승은 일반적으로 캐릭터가 선 위로 가장 먼 곳입니다. 높이를 얻으려면 음수 가기 때문에 상승을 빼야합니다. (기준선이다 y=0y스크린까지 descreases).

다음 이미지를보십시오. 두 문자열의 높이는입니다 234.375.

여기에 이미지 설명을 입력하십시오

텍스트 높이가 아닌 선 높이를 원하면 다음을 수행 할 수 있습니다.

float height = fm.bottom - fm.top + fm.leading; // 265.4297

이것 bottomtop라인입니다. 선행 (인라인 간격)은 일반적으로 0이지만 어쨌든 추가해야합니다.

위의 이미지는 이 프로젝트 에서 나온 것입니다 . 이것으로 놀아서 Font Metrics의 작동 방식을 볼 수 있습니다.

정적 레이아웃

여러 줄 문자의 높이를 측정하려면을 사용해야합니다 StaticLayout. 이 답변 에서 그것에 대해 자세히 이야기 했지만이 높이를 얻는 기본 방법은 다음과 같습니다.

String text = "This is some text. This is some text. This is some text. This is some text. This is some text. This is some text.";

TextPaint myTextPaint = new TextPaint();
myTextPaint.setAntiAlias(true);
myTextPaint.setTextSize(16 * getResources().getDisplayMetrics().density);
myTextPaint.setColor(0xFF000000);

int width = 200;
Layout.Alignment alignment = Layout.Alignment.ALIGN_NORMAL;
float spacingMultiplier = 1;
float spacingAddition = 0;
boolean includePadding = false;

StaticLayout myStaticLayout = new StaticLayout(text, myTextPaint, width, alignment, spacingMultiplier, spacingAddition, includePadding);

float height = myStaticLayout.getHeight(); 

좋은 설명입니다. 스크린 샷은 어떤 앱입니까?
Micheal Johnson

1
@MichealJohnson, 여기에 GitHub 프로젝트 로 앱을 추가했습니다 .
Suragch

1
그렇다면 "getTextSize"는 무엇을 제공합니까?
안드로이드 개발자

1
그림판 getTextSize()은 픽셀 단위로 글꼴 크기를 제공합니다 (단위와 반대 sp). @androiddeveloper
Suragch

2
픽셀 단위의 글꼴 크기와 측정 된 높이 및 FontMetrics 치수 의 관계는 무엇입니까 ? 그것은 더 탐구하고 싶은 질문입니다.
Suragch

85

@bramp의 대답은 정확합니다. 부분적으로 계산 된 경계가 암시 적 시작 좌표가 0, 0 인 텍스트를 완전히 포함하는 최소 사각형이라는 점은 언급하지 않았기 때문입니다.

즉, 예를 들어 "Py"의 높이는 픽셀 단위로 다른 높이가 필요하기 때문에 "py"또는 "hi"또는 "oi"또는 "aw"의 높이와 다릅니다.

이것은 결코 고전적인 자바의 FontMetrics와 동등하지 않습니다.

텍스트의 너비는 고통스럽지 않지만 높이는 큽니다.

특히, 그려진 텍스트를 세로로 가운데 정렬해야하는 경우, 그리려는 텍스트를 사용하는 대신 따옴표없이 텍스트 "a"의 경계를 가져와보십시오. 나를 위해 작동합니다 ...

여기 내가 의미하는 바가있다 :

Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.LINEAR_TEXT_FLAG);

paint.setStyle(Paint.Style.FILL);
paint.setColor(color);
paint.setTextAlign(Paint.Align.CENTER);
paint.setTextSize(textSize);

Rect bounds = new Rect();
paint.getTextBounds("a", 0, 1, bounds);

buffer.drawText(this.myText, canvasWidth >> 1, (canvasHeight + bounds.height()) >> 1, paint);
// remember x >> 1 is equivalent to x / 2, but works much much faster

텍스트를 세로로 가운데 정렬한다는 것은 경계 사각형을 세로로 가운데 정렬하는 것을 의미합니다. 이는 텍스트 (대문자, 긴 글자 등)마다 다릅니다. 그러나 실제로 원하는 것은 렌더링 된 텍스트의 기준선을 정렬하여 높이가 높거나 그루브가 나타나지 않도록하는 것입니다. 따라서 가장 작은 문자의 중심 (예 : "a")을 알고있는 한 나머지 텍스트에 대한 정렬을 재사용 할 수 있습니다. 그러면 모든 텍스트가 중앙 정렬되고 기본 정렬됩니다.


25
x >> 1오랫동안 보지 못했습니다 . 그것만 찬성 :)
keaukraine

62
훌륭한 최신 컴파일러는이를보고 x / 2최적화합니다.x >> 1
intrepidis

37
@keaukraine x / 2은 Chris의 의견을 고려하여 코드를 읽을 때 훨씬 더 친숙합니다.
d3dave

무엇 buffer이 예에서? 그것은 canvas에 전달 draw(Canvas)방법은?
AutonomousApps

@AutonomousApps 네, 캔버스입니다
Chisko

16

높이는 페인트 변수에서 설정 한 텍스트 크기입니다.

높이를 찾는 또 다른 방법은

mPaint.getTextSize();

3

당신은 사용할 수 있습니다 android.text.StaticLayout필요한 범위를 지정한 다음 전화 클래스 getHeight(). draw(Canvas)메소드 를 호출하여 레이아웃에 포함 된 텍스트를 그릴 수 있습니다 .


2

getTextSize () 메서드를 사용하여 Paint 객체의 텍스트 크기를 간단히 얻을 수 있습니다. 예를 들면 다음과 같습니다.

Paint mTextPaint = new Paint (Paint.ANTI_ALIAS_FLAG);
//use densityMultiplier to take into account different pixel densities
final float densityMultiplier = getContext().getResources()
            .getDisplayMetrics().density;  
mTextPaint.setTextSize(24.0f*densityMultiplier);

//...

float size = mTextPaint.getTextSize();

어디에서 24.0f왔습니까?
Erigami

24.0f 텍스트 크기의
예일뿐입니다.

1

대신 Rect.width()Rect.Height()에서 반환 된 것을 사용해야합니다 getTextBounds(). 그것은 나를 위해 작동합니다.


2
여러 세그먼트의 텍스트를 다루는 경우에는 아닙니다. 그 이유는 위의 대답에 있습니다.
Nar Gar

0

여전히 문제가있는 사람은 이것이 내 코드입니다.

정사각형 (너비 = 높이) 인 사용자 정의보기가 있고 문자를 할당하고 싶습니다. onDraw()문자를 사용하지 않지만 높이를 얻는 방법을 보여줍니다. 화면 중간에 문자가 표시됩니다.

public class SideBarPointer extends View {

    private static final String TAG = "SideBarPointer";

    private Context context;
    private String label = "";
    private int width;
    private int height;

    public SideBarPointer(Context context) {
        super(context);
        this.context = context;
        init();
    }

    public SideBarPointer(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.context = context;
        init();
    }

    public SideBarPointer(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        this.context = context;
        init();
    }

    private void init() {
//        setBackgroundColor(0x64FF0000);
    }

    @Override
    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        height = this.getMeasuredHeight();
        width = this.getMeasuredWidth();

        setMeasuredDimension(width, width);
    }

    protected void onDraw(Canvas canvas) {
        float mDensity = context.getResources().getDisplayMetrics().density;
        float mScaledDensity = context.getResources().getDisplayMetrics().scaledDensity;

        Paint previewPaint = new Paint();
        previewPaint.setColor(0x0C2727);
        previewPaint.setAlpha(200);
        previewPaint.setAntiAlias(true);

        Paint previewTextPaint = new Paint();
        previewTextPaint.setColor(Color.WHITE);
        previewTextPaint.setAntiAlias(true);
        previewTextPaint.setTextSize(90 * mScaledDensity);
        previewTextPaint.setShadowLayer(5, 1, 2, Color.argb(255, 87, 87, 87));

        float previewTextWidth = previewTextPaint.measureText(label);
//        float previewTextHeight = previewTextPaint.descent() - previewTextPaint.ascent();
        RectF previewRect = new RectF(0, 0, width, width);

        canvas.drawRoundRect(previewRect, 5 * mDensity, 5 * mDensity, previewPaint);
        canvas.drawText(label, (width - previewTextWidth)/2, previewRect.top - previewTextPaint.ascent(), previewTextPaint);

        super.onDraw(canvas);
    }

    public void setLabel(String label) {
        this.label = label;
        Log.e(TAG, "Label: " + label);

        this.invalidate();
    }
}

3
onDraw ()에 할당 할 경우 -1 : 클래스에서 필드를 선언하고 (생성자에서 시작) onDraw ()에서 다시 사용하면 많은 성능 이점을 얻을 수 있습니다.
zoltish
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.