Android Question Canvas.DrawText Positioning issue/formula

Cableguy

Expert
Licensed User
Hi Guys

I'm trying to place a single character dead center on a label using canvas and drawtext.
The issue is, even thou I have set the padding to zero on all four sides, I still get my character a bit off-centre vertically. Horizontal Center is easy to get, as we just set the draw text alignment to "CENTER", but to center it vertically is proving challenging to say the least...
so any help would be very welcomed!!

what I got that "almost" works...

B4X:
Sub SetlabelText(v As Label, TFace As Typeface, txt As String)
    SetPadding(v, 0, 0, 0, 0) ' we set the padding
   
    Private vHeight As Int = v.Height ' the view's Height
    Private vWidth As Int = v.Width ' the view's Width
    Private tBase As Int ' the Text Base value
   
    If vHeight > vWidth Then 'if it's taller than it is wide
        tBase = vWidth ' the text size is calculated relatively to the view's Width
    Else 'if it's wider than it is tall
        tBase = vHeight ' the text size is calculated relatively to the view's Height
    End If
   
    'Now we get the max FontSize possible for tBase
    Private CV As Canvas
    CV.Initialize(v)
    Private x As Int = 0
    Do Until CV.MeasureStringHeight("+", TFace, x)>= tBase
        x=x+1
    Loop
   
    ' I want it to be about 70% of tBase
    x = Floor(x * 0.7)
   
    'Now I get the final sizes for text height with the calculated font size
    Private tHeight As Int = CV.MeasureStringHeight(txt, TFace, x)

    'and try to calculate the vertical origin point (x?) for the string
    Private yBase As Int = Floor (vHeight - ((vHeight-tHeight)/2)) ' wich clearly eludes me!!!!
    Private xBase As Int = Floor(vWidth/2)'this is horizontal origin point, center!

    CV.DrawText(txt,xBase, yBase, TFace, x, Colors.Black, "CENTER")     
End Sub
This should be as easy as subtracting the text height from the view's height, and divide that by two... then subtract that value from the views height, right???? apparently not, so... Please Help!!!
 

RandomCoder

Well-Known Member
Licensed User
I'm pretty sure that the problem will be with the Canvas.MeasureStringHeight it is not reliable. Erel advised me in a previous post somewhere on the Forum to use StringUtils instead.
 

LucaMs

Expert
Licensed User
I would try using Float variables (DrawText requires Float) and changing:

B4X:
Private yBase As Int = Floor (vHeight - ((vHeight-tHeight)/2))
to:
B4X:
Private yBase As Float = (vHeight - tHeight) / 2
 

Cableguy

Expert
Licensed User
I'm about to blow a fuse!!!!!

This should be so dead simple!!!
All I what to accomplish is to place a single character dead center on a label, set to the max fontsize possible within the bounderies of the label.
I have been trying to do it with a canvas and stringutils but it just never gets dead center!!!

since the characters I want to draw are the plus and minus signs, would I be better of drawing them myself instead of using a font????

Can the Math and view gurus give me a hand here???
 

strat

Active Member
Licensed User
This code write a text at center of an image. Reflector lib is required. It can be modified for another object like label, button.. etc



B4X:
Sub Button1_Click
    Dim iv As ImageView
    Dim c As Canvas
    Dim rect1 As Rect
    Dim tf As Typeface
    Dim rx1,ry1,rw,rh As Int
    Dim r_,g_,b_,clr As Int
    Dim Height_, Width_,fs As Int
    Dim lbl As Label
    Dim obj As Reflector
    fs=23 ' Font size
    tf=Typeface.DEFAULT
    lbl.Initialize("lbl")
    Activity.AddView(lbl,0,0,1,1)
    lbl.Typeface=tf
    lbl.TextSize=fs
    lbl.Text=Rnd(1,99)
    lbl.Width=-2
    lbl.Height=-2
    DoEvents
    obj.Target = lbl
    Width_ = obj.RunMethod("getWidth")
    Height_ = obj.RunMethod("getHeight")
  
    iv.Initialize("iv")
    rx1=Rnd(0,90%x)
    ry1=Rnd(0,80%y)
    rw=10%x
    rh=10%x
    r_=Rnd(0,255)
    g_=Rnd(0,255)
    b_=Rnd(0,255)
  
    Activity.AddView(iv,rx1,ry1,rw,rh)
    c.Initialize(iv)
  
    rect1.Initialize(0,0,rw,rh)
  
    c.DrawOval(rect1,Colors.RGB(r_,g_,b_),True,5dip)
    c.DrawText(lbl.Text,(rw-Width_)/2,(rh+fs)/2,tf,fs, Colors.Black,"LEFT")
End Sub
 

RandomCoder

Well-Known Member
Licensed User
Hi @Cableguy

It looks like you've hit on the same problem I had a while ago when trying to do the same thing... Max Text Size. Here's the original post in which Erel stated that Canvas.MeasureString is based on an lower API and he recommended to use StringUtils instead... fontsize multiple layout.
Another option is to use either the AutoTextSizeLabel Library which Erel has created... [custom view] AutoTextSizeLabel or apply a better scaling algorithm... Designer Scripts & AutoScale Tutorial

Alternatively you can ignore all the good advice and use Canvas.MeasureString with a fixed multiplier as I have (sorry Erel :D), the AutoScale tutorial just looked over complex compared with a fixed multiplier applied to the result from Canvas.MeasureString. How far out can it be??
Here's the code I use...
B4X:
Private Sub getMaxTextSize(v As View, str As String) As Float
    ' Get internal padding used by the view
    Dim jo As JavaObject = v
    Dim intPaddingLR As Int = jo.Runmethod("getPaddingLeft",Null) + jo.Runmethod("getPaddingRight",Null)
    Dim intPaddingTB As Int = jo.Runmethod("getPaddingTop",Null) + jo.Runmethod("getPaddingBottom",Null)

    ' Set initial fontsize and get font dimensions
    Dim c As Canvas
    c.Initialize(v)
    Dim fpSize As Float = 70
    Dim fpW As Float = c.MeasureStringWidth(str, Typeface.DEFAULT_BOLD, fpSize) * 1.25
    Dim fpH As Float = c.MeasureStringHeight(str, Typeface.DEFAULT_BOLD, fpSize) * 1.75
    ' Check font dimensions and reduce font size until a fit is found
    Do While (fpW > (v.Width - intPaddingLR)) Or (fpH > (v.Height - intPaddingTB))
        fpSize = fpSize - 2
        fpW = c.MeasureStringWidth(str, Typeface.DEFAULT_BOLD, fpSize) * 1.25
        fpH = c.MeasureStringHeight(str, Typeface.DEFAULT_BOLD, fpSize) * 1.75
        ' Prevent possibility of getting stuck in the loop
        If fpSize < 2 Then Exit
    Loop
    Return fpSize
End Sub
And called using...
B4X:
btnRGB_Up.TextSize   = getMaxTextSize(btnRGB_Up, "##")
btnRGB_Up.Text       = "+"
In the example above I'm actually sending two characters "##" and then only applying a "+" and therefore the size is slightly smaller than the largest size possible but this was because for some reason the text starts to move away from MiddleCenter if made too large. I don't know why and this was my little work around ;)
 

Cableguy

Expert
Licensed User
Have you tried mixing AutoTextSizeLabel with "Lbl.Gravity = Bit.Or(Gravity.CENTER_HORIZONTAL, Gravity.CENTER_VERTICAL)"
I tried gravity.Center_vertical+gravity.Center_horizontal... Seems I used the wrong syntax... I will have a go at it later on
 

sorex

Expert
Licensed User
I checked this and in my case it works as espected.

the block at the bottom is what is being cut out at the top.
there's a flu alpha pixel between the pixel & block below so it's perfectly even.

B4X:
Dim l As Label
l.Initialize("")
l.Text="+"
l.Color=Colors.Red
l.TextColor=Colors.Black
l.Gravity=Gravity.CENTER
Activity.AddView(l,0,0,100,100)
 

Attachments

Cableguy

Expert
Licensed User
it's the same and equals to CENTER
Correct me if i'm wrong (most surtenly), but what I'm trying to accomplish should be as easy as setting the padding to zero, find the required max font size, and set the gravity to Center... Right???
Seems not... But why!?
 

sorex

Expert
Licensed User
ok, my bad. I just see that you want to draw it.

I wonder why you want to draw it instead of using the normal methods?
you don't have a font file of that bitmap font?
 

Cableguy

Expert
Licensed User
ok, my bad. I just see that you want to draw it.

I wonder why you want to draw it instead of using the normal methods?
you don't have a font file of that bitmap font?
I went with drawing before I discovered padding and how to set it... Then it just stayed there...
I sometimes miss the forest because of all those dahm trees in front of it!
I will try a less complex approach to it and see how it goes
 

RandomCoder

Well-Known Member
Licensed User
ok, my bad. I just see that you want to draw it.

I wonder why you want to draw it instead of using the normal methods?
you don't have a font file of that bitmap font?
From experience trying to do exactly what CableGuy is doing, he unable to use the normal method i.e. set the font size and then the gravity because he does not know what font size is required. Therefore he must use a method to determine the required size after which he can apply it using the 'normal' method.

The problem is in determining what is the largest possible text size that will fit a certain size of view.
 
Top