Tuval üzerine çizilecek metin yüksekliğini ölçme (Android)


132

Metnin yüksekliğini ölçmenin basit bir yolu var mı? Şimdi yaptığım yol measureText(), genişliği elde etmek için Paint'i kullanmak , ardından yaklaşık bir yükseklik elde etmek için deneme yanılma yoluyla bir değer bulmak. Ben de uğraşıyordum FontMetrics, ancak bunların hepsi berbat yaklaşık yöntemler gibi görünüyor.

Her şeyi farklı çözünürlükler için ölçeklendirmeye çalışıyorum. Bunu yapabilirim, ancak göreceli boyutları belirlemek için çok sayıda hesaplama içeren inanılmaz derecede ayrıntılı kod elde ederim. Nefret ettim! Daha iyi bir yol olmalı.

Yanıtlar:


136

Hakkında ne paint.getTextBounds () (nesne yöntemi)


1
Bir metnin yüksekliğini değerlendirdiğimde bu çok garip sonuçlar veriyor. Kısa bir metnin yüksekliği 12 olurken GERÇEKTEN uzun bir metnin yüksekliği 16 olur (16 yazı tipi boyutu verildiğinde). Bana mantıklı gelmiyor (android 2.3.3)
AgentKnopf

36
Yükseklikteki varyans, metinde
altların

209

Neye ihtiyacınız olduğuna bağlı olarak yüksekliği ölçmenin farklı yolları vardır.

getTextBounds

Az miktarda sabit metni tam olarak ortalamak gibi bir şey yapıyorsanız, muhtemelen istersiniz getTextBounds. Sınırlayıcı dikdörtgeni şu şekilde elde edebilirsiniz

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

Aşağıdaki resimlerde de görebileceğiniz gibi, farklı dizeler farklı yükseklikler verecektir (kırmızı ile gösterilmiştir).

görüntü açıklamasını buraya girin

Bu farklı yükseklikler, metin ne olursa olsun sabit bir yüksekliğe ihtiyaç duyduğunuzda bazı durumlarda bir dezavantaj olabilir. Sonraki bölüme bakın.

Paint.FontMetrics

Yazı tipi ölçülerinden yazı tipinin yüksekliğini hesaplayabilirsiniz. Yükseklik her zaman aynıdır çünkü belirli bir metin dizesinden değil yazı tipinden elde edilir.

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

Taban çizgisi, metnin üzerine oturduğu çizgidir. İniş genellikle bir karakterin çizginin altına en fazla gideceği yerdir ve yükselme genellikle bir karakterin çizginin üstüne çıkacağı en uzundur. Yüksekliği elde etmek için yükselişi çıkarmanız gerekir çünkü bu negatif bir değerdir. (Taban çizgisi ekranı büyütür y=0ve yküçültür.)

Aşağıdaki resme bakın. Her iki dizenin yükseklikleri 234.375.

görüntü açıklamasını buraya girin

Yalnızca metin yüksekliği yerine satır yüksekliğini istiyorsanız, aşağıdakileri yapabilirsiniz:

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

Bunlar bottomve tophattın. Baştaki (satırlar arası boşluk) genellikle sıfırdır, ancak yine de eklemelisiniz.

Yukarıdaki görüntüler bu projeden alınmıştır . Yazı Tipi Metriklerinin nasıl çalıştığını görmek için onunla oynayabilirsiniz.

StaticLayout

Çok satırlı metnin yüksekliğini ölçmek için bir StaticLayout. Bu cevapta biraz detaylı konuştum , ancak bu yüksekliği elde etmenin temel yolu şudur:

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(); 

Güzel açıklama. Ekran görüntüleri hangi uygulamadan geliyor?
Micheal Johnson

1
@MichealJohnson, uygulamayı buraya GitHub projesi olarak ekledim .
Suragch

1
"GetTextSize" size ne veriyor peki?
android geliştiricisi

1
Paint getTextSize()size piksel birimi cinsinden yazı tipi boyutunu verir (birimlerin aksine sp). @androiddeveloper
Suragch

2
Piksel birimi cinsinden yazı tipi boyutunun ölçülen yükseklik ve FontMetrics boyutlarıyla ilişkisi nedir? Bu, daha fazla araştırmak istediğim bir soru.
Suragch

85

@ bramp'ın cevabı doğrudur - kısmen, hesaplanan sınırların, 0, 0 örtük başlangıç ​​koordinatlarına sahip metni tamamen içeren minimum dikdörtgen olacağını belirtmemesi nedeniyle.

Bu, örneğin "Py" nin yüksekliğinin "py" veya "hi" veya "oi" veya "aw" yüksekliğinden farklı olacağı anlamına gelir çünkü piksel açısından farklı yükseklikler gerektirirler.

Bu hiçbir şekilde klasik java'daki FontMetrics ile eşdeğer değildir.

Bir metnin genişliği çok acı verici olmasa da, yüksekliği fazladır.

Özellikle, çizilen metni dikey olarak ortaya hizalamanız gerekiyorsa, çizmeyi düşündüğünüz metni kullanmak yerine "a" metninin sınırlarını (tırnak işaretleri olmadan) almayı deneyin. Benim için çalışıyor...

Demek istediğim şu:

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

Metni dikey olarak ortaya hizalamak, sınırlayıcı dikdörtgeni dikey olarak ortaya hizalamak anlamına gelir - bu, farklı metinler için farklıdır (büyük harfler, uzun harfler vb.). Ama aslında yapmak istediğimiz şey, oluşturulmuş metinlerin temellerini yükseltilmiş veya oluklu görünmeyecek şekilde hizalamaktır. Bu nedenle, en küçük harfin (örneğin "a") merkezini bildiğimiz sürece, diğer metinler için hizalamasını yeniden kullanabiliriz. Bu, tüm metinleri merkeze hizalamanın yanı sıra onları temel hizalayacaktır.


25
Yıllardır görmedim x >> 1. Sadece bunun için oylama :)
keaukraine

62
İyi bir modern derleyici bunu görecek x / 2ve optimize edecekx >> 1
intrepidis

37
@keaukraine x / 2, Chris'in yorumunu göz önünde bulundurarak kod okurken çok daha arkadaşça davranıyor .
d3dave

bufferbu örnekte ne var ? O mi canvasgeçirilen draw(Canvas)yöntemle?
AutonomousApps

@AutonomousApps evet, bu tuval
Chisko

16

Yükseklik, Paint değişkeninde ayarladığınız metin boyutudur.

Yüksekliği bulmanın başka bir yolu da

mPaint.getTextSize();

3

Sen kullanabilirsiniz android.text.StaticLayoutgerekli sınırları belirlemek ve daha sonra çağırmak için sınıf getHeight(). Metni (düzende bulunan) draw(Canvas)yöntemini çağırarak çizebilirsiniz .


2

GetTextSize () yöntemini kullanarak bir Paint nesnesinin metin boyutunu kolayca alabilirsiniz. Örneğin:

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();

Nereden 24.0fgeliyor?
Erigami

24.0f bu sadece bir metin boyutu örneğidir
moondroid

1

Kullanmalısınız Rect.width()ve Rect.Height()bunun getTextBounds()yerine hangisinden döndü . Bu benim için çalışıyor.


2
Birden fazla metin bölümüyle uğraşıyorsanız, hayır. Nedeni yukarıdaki cevabımda.
Nar Gar

0

Hala sorunu olan varsa, bu benim kodum.

Kare (genişlik = yükseklik) olan özel bir görünümüm var ve ona bir karakter atamak istiyorum. onDraw()kullanmama rağmen karakterin yüksekliğini nasıl alacağımı gösterir. Karakter, görünümün ortasında görüntülenecektir.

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 () 'daki ayırmalar için -1: Sınıftaki alanları bildirirseniz (yapıcıda başlatırsanız) ve bunları onDraw ()' da yeniden kullanırsanız bir ton performans faydası elde edersiniz.
zoltish
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.