Tuval üzerinde Android Center metni


191

Aşağıdaki kodu kullanarak bir metin görüntülemeye çalışıyorum. Sorun, metnin yatay olarak ortalanmamış olmasıdır. İçin koordinatları ayarladığımda, drawTextmetnin alt kısmını bu konumda ayarlar. Metnin yatay olarak ortalanması için metnin çizilmesini istiyorum.

Bu, sorunumu daha fazla görüntülemek için bir resim:

Ekran görüntüsü

@Override
protected void onDraw(Canvas canvas) {
    // TODO Auto-generated method stub
    super.onDraw(canvas);
    //canvas.drawRGB(2, 2, 200);
    Paint textPaint = new Paint();
    textPaint.setARGB(200, 254, 0, 0);
    textPaint.setTextAlign(Align.CENTER);
    textPaint.setTypeface(font);
    textPaint.setTextSize(300);
    canvas.drawText("Hello", canvas.getWidth()/2, canvas.getHeight()/2  , textPaint);
}

20
Boya nesnenizi onDraw etkinliğinizin içinde beyan etmem. Her yeniden çizildiğinde yeniden yaratılır. Özel sınıf değişkeni yapmayı düşünün.
Christopher Rathgeb

8
Dostum! Resim bağlantınızın NSFW olduğunu lütfen unutmayın! İhtiyatlı olmak istemiyorum, ancak üstsüz kadınların ofiste ekranımda görünmesi gereken reklamlara ihtiyacım yok.
Michael Scheper

5
@MichaelScheper Üzgünüm, bağlantıyı güncelledim!
Sebastian

23
@Sebastian Hey, hala bu bağlantıya sahip misin?
S.Lukas

@ S.Lukas hhahaha
BOTJr.

Yanıtlar:


378

Takip etmeyi dene:

 int xPos = (canvas.getWidth() / 2);
 int yPos = (int) ((canvas.getHeight() / 2) - ((textPaint.descent() + textPaint.ascent()) / 2)) ; 
 //((textPaint.descent() + textPaint.ascent()) / 2) is the distance from the baseline to the center.

 canvas.drawText("Hello", xPos, yPos, textPaint);

10
Mükemmel cevap. Benim için aşağıdakileri kullandım, çünkü metni orta konumda başlamak için metin yerine yatay olarak tam olarak ortalamak zorunda kaldım: int xPos = (Genişlik - textPaint.TextSize * Math.Abs ​​(_text.Length / 2)) / 2; Bunu başarmanın daha iyi bir yolu olup olmadığından emin değilim.
paj7777

Ve muhtemelen en iyi döküm _text.Çok metin uzunluğu için işe yaramayacağı için bir şamandıra uzunluğu.
paj7777

61
paj7777, textPaint.setTextAlign (Align.CENTER) ayarladığınızda bu gerekli değildir;
Costi Muraru

@ paj7777 Bu sadece sabit genişlikli yazı tipleri için işe yarardı. Ayrıca, gerek yok döküm Ģamandıraya; bir şamandıra ile bölerseniz, sonuç bir şamandıra olacaktır. Örneğin float halfLength = text.length() / 2f;buna tür tanıtımı denir .
Michael Scheper

2
Bu yaklaşımı (= orta Paint.descent()ve ile Paint.ascent()) aşağıdaki cevabımda orta metin yaklaşımıyla karşılaştırdım Paint.getTextBounds(). Paint.descent()ve Paint.ascent()gerçek metni dikkate almayın. (Bu yanlışlığı aşağıdaki yazımdaki ekran görüntüsünde tanıyabilirsiniz.) Bu yüzden bu yaklaşıma karşı öneriyorum. İle yaklaşım Paint.getTextBounds()daha doğru çalışıyor gibi görünüyor.
andreas1724

217

Paint.getTextBounds () ile ortala :

resim açıklamasını buraya girin

private Rect r = new Rect();

private void drawCenter(Canvas canvas, Paint paint, String text) {
    canvas.getClipBounds(r);
    int cHeight = r.height();
    int cWidth = r.width();
    paint.setTextAlign(Paint.Align.LEFT);
    paint.getTextBounds(text, 0, text.length(), r);
    float x = cWidth / 2f - r.width() / 2f - r.left;
    float y = cHeight / 2f + r.height() / 2f - r.bottom;
    canvas.drawText(text, x, y, paint);
}
  • Paint.Align.CENTER , metnin referans noktasının dikey olarak ortalandığı anlamına gelmez. Referans noktası daima taban çizgisinde bulunur. Peki, neden Paint.Align.LEFT kullanmıyorsunuz ? Yine de referans noktasını hesaplamanız gerekir.

  • Paint.descent () dezavantajı vardır, gerçek metni dikkate almaz. Paint.descent () , metinde alçaltılmış harfler içerip içermediğine bakılmaksızın aynı değeri alır. Kullandığım yüzden r.bottom yerine.

  • API <16 ise Canvas.getHeight () ile bazı sorunlar yaşadım Bu yüzden bunun yerine Canvas.getClipBounds (Rect) kullanın. ( Rect için bellek ayırdığı için Canvas.getClipBounds (). GetHeight () öğesini kullanmayın .)

  • Performans nedenleriyle, nesneleri onDraw () içinde kullanılmadan önce ayırmalısınız . Şöyle drawCenter () içinde çağrılır onDraw'da () nesne Rect r burada bir alan olarak İlk ayırma edilir.


En iyi iki cevabın kodunu kendi koduma (Ağustos 2015) koymaya çalıştım ve sonuçları karşılaştırmak için bir ekran görüntüsü yaptım:

metin merkezli üç versiyon

Metin, kırmızı dolu dikdörtgenin içinde ortalanmalıdır. Kodum beyaz metni, diğer iki kod tamamen gri metni üretir (bunlar aslında aynıdır, çakışıyor). Gri metin biraz fazla düşük ve sağda iki tane fazla.

Testi şu şekilde yaptım:

import android.app.Activity;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;

class MyView extends View {

    private static String LABEL = "long";
    private static float TEXT_HEIGHT_RATIO = 0.82f;

    private FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(0, 0);
    private Rect r = new Rect();
    private Paint paint = new Paint();
    private Paint rectPaint = new Paint();

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

    private void drawTextBounds(Canvas canvas, Rect rect, int x, int y) {
        rectPaint.setColor(Color.rgb(0, 0, 0));
        rectPaint.setStyle(Paint.Style.STROKE);
        rectPaint.setStrokeWidth(3f);
        rect.offset(x, y);
        canvas.drawRect(rect, rectPaint);
    }

    // andreas1724 (white color):
    private void draw1(Canvas canvas, Paint paint, String text) {
        paint.setTextAlign(Paint.Align.LEFT);
        paint.setColor(Color.rgb(255, 255, 255));
        canvas.getClipBounds(r);
        int cHeight = r.height();
        int cWidth = r.width();
        paint.getTextBounds(text, 0, text.length(), r);
        float x = cWidth / 2f - r.width() / 2f - r.left;
        float y = cHeight / 2f + r.height() / 2f - r.bottom;
        canvas.drawText(text, x, y, paint);
        drawTextBounds(canvas, r, (int) x, (int) y);
    }

    // Arun George (light green color):
    private void draw2(Canvas canvas, Paint textPaint, String text) {
        textPaint.setTextAlign(Paint.Align.CENTER);
        textPaint.setColor(Color.argb(100, 0, 255, 0));
        int xPos = (canvas.getWidth() / 2);
        int yPos = (int) ((canvas.getHeight() / 2) - ((textPaint.descent() + textPaint.ascent()) / 2));
        canvas.drawText(text, xPos, yPos, textPaint);
    }

    // VinceStyling (light blue color):
    private void draw3(Canvas yourCanvas, Paint mPaint, String pageTitle) {
        mPaint.setTextAlign(Paint.Align.LEFT);
        mPaint.setColor(Color.argb(100, 0, 0, 255));
        r = yourCanvas.getClipBounds();
        RectF bounds = new RectF(r);
        bounds.right = mPaint.measureText(pageTitle, 0, pageTitle.length());
        bounds.bottom = mPaint.descent() - mPaint.ascent();
        bounds.left += (r.width() - bounds.right) / 2.0f;
        bounds.top += (r.height() - bounds.bottom) / 2.0f;
        yourCanvas.drawText(pageTitle, bounds.left, bounds.top - mPaint.ascent(), mPaint);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        int margin = 10;
        int width = w - 2 * margin;
        int height = h - 2 * margin;
        params.width = width;
        params.height = height;
        params.leftMargin = margin;
        params.topMargin = margin;
        setLayoutParams(params);
        paint.setTextSize(height * TEXT_HEIGHT_RATIO);
        paint.setAntiAlias(true);
        paint.setTypeface(Typeface.create(Typeface.SERIF, Typeface.BOLD_ITALIC));
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawColor(Color.rgb(255, 0, 0));
        draw1(canvas, paint, LABEL);
        draw2(canvas, paint, LABEL);
        draw3(canvas, paint, LABEL);
    }
}

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRequestedOrientation (ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        FrameLayout container = new FrameLayout(this);
        container.setLayoutParams(new ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT));
        container.addView(new MyView(this));
        setContentView(container);
    }
}

1
Çok teşekkür ederim .. Mantık gerçekten bana çok yardımcı oldu.
Sujay BM

Çok teşekkür ederim .. Mantığınız da bana gerçekten çok yardımcı oldu!
LiuWenbin_NO.

Metni gerçekten merkezleyen tek cevap budur.
Joery

64

Dikey olarak hizalamak zordur çünkü metin inişi ve tırmanışı gerçekleşti, birçok adam TextWidth ve TextHeight'ı almak için Paint.getTextBounds () kullandı, ancak metin merkezini çok fazla yapmıyor. Burada Paint.measureText () ' i TextWidth'i hesaplamak için kullanabiliriz, sadece iniş ve çıkışla çıkarma yaptığımız TextHeight, daha sonra TextSize'e en fazla yaklaşımı elde ettik, aşağıdaki çalışma birbirleri için oldukça kolaydır.

// the Paint instance(should be assign as a field of class).
Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setTextSize(getResources().getDimension(R.dimen.btn_textsize));

// the display area.
Rect areaRect = new Rect(0, 0, 240, 60);

// draw the background style (pure color or image)
mPaint.setColor(Color.BLACK);
yourCanvas.drawRect(areaRect, mPaint);

String pageTitle = "文字小说";

RectF bounds = new RectF(areaRect);
// measure text width
bounds.right = mPaint.measureText(pageTitle, 0, pageTitle.length());
// measure text height
bounds.bottom = mPaint.descent() - mPaint.ascent();

bounds.left += (areaRect.width() - bounds.right) / 2.0f;
bounds.top += (areaRect.height() - bounds.bottom) / 2.0f;

mPaint.setColor(Color.WHITE);
yourCanvas.drawText(pageTitle, bounds.left, bounds.top - mPaint.ascent(), mPaint);

kod ile çekilen ekran

Bu arada, biz çok kullanım tavsiye RectF ziyade Rect pozisyonları daha doğru değerlere gerektiğinden Tecrübelerime göre, RectF üst ve alt sapma xhdpi cihazda sadece bir piksel yapılır, Rect'iniz iki tane daha olurdu.


Yazıldığı gibi, var "Paint" kafa karıştırıcıdır. Nasıl atanır? bounds.bottom için metnin yükselişini ve alçalmasını nasıl biliyor?
RoundSparrow hilltx

1
evet, cevabımı bunun için optimize ettim, lütfen bir göz atın.
VinceStyling

Değerli zamanımı kurtardığın için teşekkürler))
Andrey

16

Kodunuz, metnin taban çizgisinin ortasını, görünümün ortasına çiziyor. Bazı noktası, x, y metni ortalamak için, metnin merkezini hesaplamak ve koymak gerekir o noktada.

Bu yöntem, x, y noktasında ortalanmış metin çizer. Görünümünüzün merkezine geçirirseniz, metni ortalanmış olarak çizer.

private void drawTextCentered(String text, int x, int y, Paint paint, Canvas canvas) {
    int xPos = x - (int)(paint.measureText(text)/2);
    int yPos = (int) (y - ((textPaint.descent() + textPaint.ascent()) / 2)) ;

    canvas.drawText(text, xPos, yPos, textPaint);
}

@MarcioGranzotto android.graphics.Paintmetni çizmek için kullanılıyor
William Reed

4

Metni ortalamak için en iyi çözümün aşağıdaki gibi olduğunu düşünüyorum:

textPaint.setTextAlign(Paint.Align.CENTER);
//textPaint is the Paint object being used to draw the text (it must be initialized beforehand)
float textY=center.y;
float textX=center.x; 
// in this case, center.x and center.y represent the coordinates of the center of the rectangle in which the text is being placed
canvas.drawText(text,textX,textY,textPaint);    `

Bu tam olarak yol gibi görünüyor, soru soran kişi metni ortalamayı denedi. Çünkü metni dikey olmayacak Paint.Align.Center anlamına gelmez metin referans noktası dikey bir şekilde hizalanması.
andreas1724

3

benim için çalışır: textPaint.textAlign = Paint.Align.CENTER ile textPaint.getTextBounds

private fun drawNumber(i: Int, canvas: Canvas, translate: Float) {
            val text = "$i"
            textPaint.textAlign = Paint.Align.CENTER
            textPaint.getTextBounds(text, 0, text.length, textBound)

            canvas.drawText(
                    "$i",
                    translate + circleRadius,
                    (height / 2 + textBound.height() / 2).toFloat(),
                    textPaint
            )
        }

sonuç:

resim açıklamasını buraya girin


1

Bunu basitleştirmek için bir yöntem oluşturmak:

    public static void drawCenterText(String text, RectF rectF, Canvas canvas, Paint paint) {
    Paint.Align align = paint.getTextAlign();
    float x;
    float y;
    //x
    if (align == Paint.Align.LEFT) {
        x = rectF.centerX() - paint.measureText(text) / 2;
    } else if (align == Paint.Align.CENTER) {
        x = rectF.centerX();
    } else {
        x = rectF.centerX() + paint.measureText(text) / 2;
    }
    //y
    metrics = paint.getFontMetrics();
    float acent = Math.abs(metrics.ascent);
    float descent = Math.abs(metrics.descent);
    y = rectF.centerY() + (acent - descent) / 2f;
    canvas.drawText(text, x, y, paint);

    Log.e("ghui", "top:" + metrics.top + ",ascent:" + metrics.ascent
            + ",dscent:" + metrics.descent + ",leading:" + metrics.leading + ",bottom" + metrics.bottom);
}

rectF metni çizmek istediğiniz alandır, İşte bu kadar. ayrıntılar


1

Statik düzen kullanıyorsak

mStaticLayout = new StaticLayout(mText, mTextPaint, mTextWidth,
                Layout.Alignment.ALIGN_CENTER, 1.0f, 0, true);

Layout.Alignment.ALIGN_CENTER bu işe yarayacak . Statik düzenin başka avantajları da vardır.

Referans: Android Belgeleri


1

Bu benim için çalıştı:

 paint.setTextAlign(Paint.Align.CENTER);
        int xPos = (newWidth / 2);
        int yPos = (newHeight / 2);
        canvas.drawText("Hello", xPos, yPos, paint);

eğer birisi herhangi bir sorun bulursa lütfen bana bildirin


metin orta noktadan yazmaya başlayacak, ancak metnin tamamı ortada olmayacak
M Shaban Ali

1

Benim durumumda, metni bir tuvalin ortasına koymak zorunda kalmadım, dönen bir tekerleğe. Başarılı olmak için bu kodu kullanmak zorunda olmasına rağmen:

fun getTextRect(textSize: Float, textPaint: TextPaint, string: String) : PointF {
    val rect = RectF(left, top, right, bottom)
    val rectHeight = Rect()
    val cx = rect.centerX()
    val cy = rect.centerY()

    textPaint.getTextBounds(string, 0, string.length, rectHeight)
    val y = cy + rectHeight.height()/2
    val x = cx - textPaint.measureText(string)/2

    return PointF(x, y)
}

Sonra View sınıfından bu yöntemi çağırır:

private fun drawText(canvas: Canvas, paint: TextPaint, text: String, string: String) {
    val pointF = getTextRect(paint.textSize, textPaint, string)
    canvas.drawText(text, pointF!!.x, pointF.y, paint)
}
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.