Đo chiều cao văn bản sẽ được vẽ trên Canvas (Android)


132

Bất kỳ cách thẳng về phía trước để đo chiều cao của văn bản? Cách tôi đang làm bây giờ là bằng cách sử dụng Paint measureText()để lấy chiều rộng, sau đó bằng cách dùng thử và tìm lỗi giá trị để có chiều cao gần đúng. Tôi cũng đã loay hoay với FontMetrics, nhưng tất cả những thứ này có vẻ giống như các phương pháp gần đúng.

Tôi đang cố gắng mở rộng mọi thứ cho các độ phân giải khác nhau. Tôi có thể làm điều đó, nhưng tôi kết thúc với mã dài vô cùng với nhiều tính toán để xác định kích thước tương đối. Tôi ghét nó! Có phải là một cách tốt hơn.

Câu trả lời:


136

Điều gì về paint.getTextBound () (phương thức đối tượng)


1
Điều này mang lại kết quả rất kỳ lạ, khi tôi đánh giá chiều cao của một văn bản. Một văn bản ngắn dẫn đến chiều cao là 12, trong khi văn bản dài THỰC SỰ dẫn đến chiều cao là 16 (cho cỡ chữ là 16). Không có ý nghĩa gì với tôi (android 2.3.3)
AgentKnopf

35
Phương sai về chiều cao là nơi bạn có con cháu trong văn bản, tức là 'Cao' cao hơn 'Thấp' vì một phần của g bên dưới dòng
FrinkTheBrave

208

Có nhiều cách khác nhau để đo chiều cao tùy thuộc vào những gì bạn cần.

getTextBound

Nếu bạn đang làm một cái gì đó như chính xác tập trung một lượng nhỏ văn bản cố định, bạn có thể muốn getTextBounds. Bạn có thể có được hình chữ nhật giới hạn như thế này

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

Như bạn có thể thấy đối với các hình ảnh sau, các chuỗi khác nhau sẽ cho các độ cao khác nhau (hiển thị màu đỏ).

nhập mô tả hình ảnh ở đây

Những độ cao khác nhau này có thể là một bất lợi trong một số tình huống khi bạn chỉ cần một chiều cao không đổi cho dù văn bản là gì. Xem phần tiếp theo.

Sơn.FontMetrics

Bạn có thể tính toán độ cao của phông chữ từ các số liệu phông chữ. Chiều cao luôn giống nhau vì được lấy từ phông chữ, không phải bất kỳ chuỗi văn bản cụ thể nào.

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

Đường cơ sở là dòng mà văn bản nằm trên. Hậu duệ nói chung là người xa nhất mà một nhân vật sẽ đi dưới dòng và người đi lên nói chung là người xa nhất mà một nhân vật sẽ đi trên dòng. Để có được chiều cao, bạn phải trừ đi vì nó là một giá trị âm. (Đường cơ sở là y=0ygiảm dần màn hình.)

Nhìn vào hình ảnh sau đây. Độ cao cho cả hai chuỗi là 234.375.

nhập mô tả hình ảnh ở đây

Nếu bạn muốn chiều cao dòng thay vì chỉ chiều cao văn bản, bạn có thể làm như sau:

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

Đây là bottomtopcủa dòng. Hàng đầu (khoảng cách giữa các dòng) thường bằng không, nhưng dù sao bạn cũng nên thêm nó.

Những hình ảnh trên đến từ dự án này . Bạn có thể chơi xung quanh nó để xem cách thức hoạt động của Font Metrics.

Tĩnh

Để đo chiều cao của văn bản nhiều dòng, bạn nên sử dụng a StaticLayout. Tôi đã nói về nó một số chi tiết trong câu trả lời này , nhưng cách cơ bản để có được chiều cao này là như thế này:

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

Giải thích tốt đẹp. Những ảnh chụp màn hình từ ứng dụng nào?
Micheal Johnson

1
@MichealJohnson, tôi đã thêm ứng dụng này dưới dạng dự án GitHub tại đây .
Suragch ngày

1
"GetTextSize" mang lại cho bạn điều gì?
nhà phát triển Android

1
Paint getTextSize()cung cấp cho bạn kích thước phông chữ theo đơn vị pixel (trái ngược với spđơn vị). @androiddeveloper
Suragch

2
Mối quan hệ của kích thước phông chữ theo đơn vị pixel với chiều cao đo được và kích thước FontMetrics là gì? Đó là một câu hỏi mà tôi muốn khám phá thêm.
Suragch

85

Câu trả lời của @ bramp là chính xác - một phần, trong đó không đề cập rằng các ranh giới được tính toán sẽ là hình chữ nhật tối thiểu chứa văn bản đầy đủ với tọa độ bắt đầu ngầm định là 0, 0.

Điều này có nghĩa là, chiều cao của, ví dụ "Py" sẽ khác với chiều cao của "py" hoặc "hi" hoặc "oi" hoặc "aw" bởi vì pixel-khôn họ yêu cầu độ cao khác nhau.

Điều này không có nghĩa là tương đương với FontMetrics trong java cổ điển.

Mặc dù chiều rộng của văn bản không gây nhiều đau đớn, chiều cao là.

Cụ thể, nếu bạn cần căn giữa theo chiều dọc của văn bản đã vẽ, hãy thử lấy ranh giới của văn bản "a" (không có dấu ngoặc kép), thay vì sử dụng văn bản bạn định vẽ. Làm việc cho tôi ...

Ý tôi là đây:

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

Trung tâm theo chiều dọc căn chỉnh văn bản có nghĩa là trung tâm theo chiều dọc căn chỉnh hình chữ nhật giới hạn - khác nhau cho các văn bản khác nhau (chữ hoa, chữ dài, v.v.). Nhưng những gì chúng ta thực sự muốn làm là cũng căn chỉnh các đường cơ sở của các văn bản được hiển thị, sao cho chúng không xuất hiện trên cao hoặc có rãnh. Vì vậy, miễn là chúng ta biết trung tâm của chữ cái nhỏ nhất (ví dụ "a"), thì chúng ta có thể sử dụng lại sự căn chỉnh của nó cho phần còn lại của văn bản. Điều này sẽ tập trung vào việc sắp xếp tất cả các văn bản cũng như căn chỉnh - căn chỉnh chúng.


25
Không nhìn thấy x >> 1từ lâu. Chỉ nâng cấp cho điều đó :)
keaukraine

61
Một trình biên dịch hiện đại tốt sẽ nhìn thấy x / 2và tối ưu hóa nó thànhx >> 1
intrepidis

37
@keaukraine x / 2thân thiện hơn nhiều khi đọc mã, xem xét nhận xét của Chris.
d3dave

những gì là buffertrong ví dụ này? Có phải nó canvasđược truyền cho draw(Canvas)phương thức?
AutonomousApps

@AutonomousApps vâng, đó là bức tranh
Chisko

16

Chiều cao là kích thước văn bản bạn đã đặt trên biến Paint.

Một cách khác để tìm ra chiều cao là

mPaint.getTextSize();

3

Bạn có thể sử dụng android.text.StaticLayoutlớp để chỉ định giới hạn cần thiết và sau đó gọi getHeight(). Bạn có thể vẽ văn bản (có trong bố cục) bằng cách gọi draw(Canvas)phương thức của nó .


2

Bạn có thể chỉ cần lấy kích thước văn bản cho một đối tượng Paint bằng phương thức getTextSize (). Ví dụ:

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

Nơi nào 24.0fđến từ đâu?
Erigami

24.0f nó chỉ là một ví dụ cho kích thước văn bản
moondroid 17/2/2015

1

Bạn phải sử dụng Rect.width()Rect.Height()trả lại từ getTextBounds()thay thế. Nó ổn với tôi.


2
Không, nếu bạn đang xử lý nhiều phân đoạn văn bản. Lý do là trong câu trả lời của tôi ở trên.
Nar Gar

0

Nếu bất cứ ai vẫn có vấn đề, đây là mã của tôi.

Tôi có chế độ xem tùy chỉnh là hình vuông (width = height) và tôi muốn gán một ký tự cho nó. onDraw()chỉ ra cách lấy chiều cao của nhân vật, mặc dù tôi không sử dụng nó. Nhân vật sẽ được hiển thị ở giữa chế độ xem.

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
-1 cho phân bổ trong onDraw (): Nó sẽ mang lại rất nhiều lợi ích hiệu suất nếu bạn khai báo các trường trong lớp (khởi tạo trong hàm tạo) và sử dụng lại chúng trong onDraw ().
zoltish
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.