Paint.measureText: Is the sum of the parts equal to the whole? - android

If I have a string like this
String myString = "This is some text.";
Will I get the same lengths if I measure the entire string as when I take the sum of measuring each character?
At first thought, I assumed the answer to be yes, but then I was reading this answer, which said measureText adds an AdvanceX value before and after each measurement. If that is true, then it would add a subtle error into measuring summed lengths (because there would be double padding between each character).
I also know that there is kerning with some fonts that changes how close letters are placed together depending on the surrounding text. This is making me think more and more that a sum of the parts will be different than the whole.
The image comes from Wikipedia.
This is important because I am making my own custom text view that needs to handle line wrapping. If a single word is too long for the line then I measure each character until I discover where I need to break. However, I assumed that summing the measure of the individual character widths would be the same as measuring the whole string.
I have set up this question so that I can answer it below after I am finished with my tests.

The whole is not always equal to the sum of the parts.
This appears to be a result of kerning and not related to any AdvanceX double padding that you mentioned.
Test code
Here is the test code:
String myString = "This is some text.";
Paint myPaint = new Paint();
myPaint.setAntiAlias(true);
myPaint.setTextSize(100);
float theWhole = myPaint.measureText(myString);
float sumOfTheParts = 0;
for (int i = 0; i < myString.length(); i++) {
sumOfTheParts += myPaint.measureText(myString, i, i + 1);
}
Results
In my first test, the measures are the same:
String myString = "This is some text.";
theWhole // 787.0
sumOfTheParts // 787.0
However, when there are letters that need kerning, the measures are different. This is the test again with a different string:
String myString = "AV Wa";
theWhole // 291.0
sumOfTheParts // 297.0
Implications
You cannot assume that measuring all the characters of a string and then summing the total will give you the actual length of the string. Thus, when doing text wrapping you will need to measure entire string ranges.

Related

How to displaying Float/Double in meaningful form in Android

I am trying to make a BMI application. When I run the application the BMI values are displayed in numeral form that I don't understand. I have tried both Float and Double type but results are same.
For example:
Height (m): 2
Weight (Kg): 100
BMI is displayed as : 2.0E-4 instead of 25
The part of the code that effects this is:
String editText1= height_field.getText().toString();
String editText2= weight_field.getText().toString();
try { // Parse string to int
double height = Double.parseDouble(editText1);
double weight = Double.parseDouble(editText2);
double bmi_result = (weight/(height*height));
String bmi_text = Double.toString(bmi_result);
display.setText(bmi_text);
System.out.println("OnClick: computeButton is clicked");
}
catch(NumberFormatException nfe) {
alert.show(); // Show error alert
To answer your original question, you should be using java.text.DecimalFormat, something like:
DecimalFormat formatter = new DecimalFormat("##.##");
display.setText(formatter.format(bmi_result));
Will force the result to be in the format of two digits followed by two decimal points, the table in the link above shows how to generate that.
However, since 2.0E-4 is 0.0002, I think Jon Skeet's comment is correct: You're doing your math operation wrong, since the value you're printing is a very small fraction of 25 :)
I'd recommend using Log.v() to print out your math operation before you actually do it, so you can see what the values of weight and height actually are, I highly doubt they're correctly set at what you described in the question.

Getting content of a particular line in a multiline edittext in android

I have a multiline edit text in which the number of lines can grow up to any number.
Does android provide any functionality to get content of any particular line?
Suppose total number of lines are three and i want to read only second line.
A EditView in Android does wrap its text so it fits in the view. That means that you have to determine a lines start and end position to read it and then you can extract it from the full contentstring. This post already explained this issue.
So your code would be:
// change to your needs
int linenumber = 1;
int startPos = myTextView.getLayout().getLineStart(linenumber);
int endPos = myTextView.getLayout().getLineEnd(linenumber);
String theLine = myTextView.getText().toString().substring(startPos, endPos);

understanding paint.measureText

Need some help to understand paint object in android. I have a string object named myString. I load that with some content in a txt file which I have under assets.
In my code when I do myString.length() on that I get a vlue of 16708.
And when I use paint object as below to get the length, I get a value of 211050
myTextView = (TextView)findViewById(R.id.my_text_view);
Paint paint = myTextView.getPaint();
float length = paint.measureText(myString, 0, myString.length());
I get a value of length as 211050.
Now I am trying to understand that what are these values here. 16708 is obviously the total length of my string in characters. And what is 211050 here? Is it the string width in pixels?
yes, the length on the screen your text will take in terms of pixel.
Editted:
Yes, paint.breakText() helps in breaking the string according to the max width provided in the parameter.
But one must also use, setSubpixelText (true) to account for the strings containing i or l(small L) or w, as breaktext somehow fails to determine the exact length of the broken string using breakText().
Atleast I faced this problem, and got resolved using setSubpixelText() .
Hope this helps.

Android string processing from TCP stream

I have a very basic TCP socket connection to a remote device that I can poll for status.
Aside from the socket programming, which I have mostly figured out through asynctask, I'm trying to come up with a way to parse out the returning string.
I query the device with something like "VOL?"
The device responds with the Volume of 12 different audio outputs with this:
"VOL:33,0,21,12,0,43,0,0,0,0,20,0"
The ":" character always and only comes back after the echo of the initial command, so I can use whatever comes before the colon to flag what sort of answer is coming in. (VOL, BAS, MUT, TRE, BAL, etc)
In the case of VOL, I simply want to chunk out everything that comes between the commas, so I can chop up and place into an array the volumes of all zones.
The only thing I can think of is to grab the length of the string, then run a for loop through it searching for commas one by one, but it seems ridiculously messy:
int oldPos = 0; //used in the upcoming 'if clause' to mark where the last comma was found
int y = 0; //used to old the resulting value's array position
String strIncoming; = //the incoming TCP string
for(int x = 0; x <= strIncoming.length(); x++){
if(",".equals(strIncoming[x]){
volzoneVal[y] = strIncoming.subString(oldPos,x);
oldPos = x;
y++;
}
}
there has GOT to be a better way, (and I'm not even sure this is going to work, I'm typing it here for the first time as I brainstorm this problem, so it's not been run or compiled)
Is there a better way to scan through a string looking for hits?
strIncoming.split(":")[0] will give you what was before first colon
strIncoming.split(":")[1].split(",") will give you array of individual strings
First, split the string on the colon, and then split[0] is your type. Then take split[1] and split it on the comma, and you'll have all your 12 different outputs ready to go (just convert them to integers).
Use Java's string split function and split on the comma as the delimiter. You will then have an array of your parameters. If you append some kind of "end string" character to each response, you will know the start and end based on the colon for the start and your end character for the end.

ANDROID How to reduce String allocations

I've managed to get my allocations down to next to nothing using DDMS (great tool), this has drastically reduced my GCs to about 1 or 2 every 3 minutes. Still, I'm not happy because those usually cause a noticeable delay in the game (on some phones) when you interact with it.
Using DDMS, I know what the allocations are, they are Strings being converted from integers used to display game information to the HUD.
I'm basically doing this:
int playerScore = 20929;
String playerScoreText = Integer.toString(playerScore);
canvas.drawText(playerScoreText, xPos, yPos);
This happens once each frame update and the HUD system is modular so I plug things in when I need and this can cause 4 or 5 hud elements to allocate Strings and AbstractStringBuilders in DDMS.
Any way to reduce these further or eliminate all the String allocations and just reuse a String object?
Thanks,
Albert Pucciani
Reading your question reminded me of one of Robert Greens articles that I read quite some time ago. It discusses your problem almost identically. http://www.rbgrn.net/content/290-light-racer-20-days-32-33-getting-great-game-performance . Skip down to day 33 and start reading.
Remember the last int score and its string representation. On a new frame check if the score is the same. If the same, then no need to create a new string - just use the old one.
Here's what I've done in the past. This will eliminate string allocations.
I create a char[] of a size that will be at least as large as the maximum number of characters you will need to display on the screen. This means that you should select a maximum high score that is achievable in the game. The way you have it now let's you display a score as high as 2^31-1 which is insanely huge, it's not practical with respect to the game. Keep in mind, this is your game, so it's ok to limit the max score to something more reasonable in the context of the game. Pick a number that will virtually be impossible to achieve. Setting this limit will then set you up to be able to not have to muck around with converting large integers to String objects.
Here's what's required:
First, you need to be able to separate the digits in an integer and convert them to char without creating String objects. Let's say you want to convert the integer of 324 into three separate characters '3','2','4' to be placed in the text char[]. One way you can do this is by taking the value 324 and do a mod 10 to get the lowest digit. So 324%10 = 4. Then divide the value by ten and do another mod 10 to get the next digit. So (324/10)%10 = 2, and (324/100)%10 = 3.
int score = 324;
int firstPlaceInt = score%10; // firstPlace will equal 4
int tensPlaceInt = (score/10)%10; // tensPlace will equal 2
int hundresPlaceInt = (score/100)%10; // hundredsPlace will equal 3
You will have to do the above in a loop, but this expresses the idea of what you're trying to do here.
Next, with these digits you can then convert them to chars by referencing a character map. One way to do this is you can create this character map by making a char[] of size 10 and placing values 0 - 9 in indexes 0 - 9.
char[] charMap = {'0','1','2','3','4','5','6','7','8','9',};
So doing this:
int score = 324;
char firstPlace = charMap[score%10];
char tenslace = charMap[(score/10)%10];
char hundredsPlace = charMap[(score/100)%10];
Will create the chars you need for the 3 digits in score.
Now, after all that, I would limit the highest score to say 99,999 (or whatever makes sense in your game). This means the largest "string" I would need to display is "Score: xx,xxx". This would require a char[] (call it text for this example) of size 13. Initialize the first 7 characters with "Score: ", these will never need to change.
char[] text = new char[13];
text[0] = 'S';
text[1] = 'c';
text[2] = 'o';
text[3] = 'r';
text[4] = 'e';
text[5] = ':';
text[6] = ' ';
The next 6 will vary based on the score. Note, that you may not necessarily fill in all 6 of those remaining characters, therefore you need to create an int (call it scoreCount for this example) which will tell you how many characters in the text char[] are actually relevant to the current score in the game. Let's say I need to display "Score: 324", this only takes 10 chars out of the 13. Write the 3 chars for the score of 324 into char[7] to char[9], and set scoreCount to 10 to indicate the number of valid characters in the char[].
int scoreCount = 7;
text[9] = charMap[score%10]; // This is firstPlace
text[8] = charMap[(score/10)%10]; // This is tensPlace
text[7] = charMap[(score/100)%10]; // This is hundredsPlace
scoreCount = 10;
You will probably have to do the above in a loop, but this should express the general idea of what you're trying to do here.
After that, you can just use drawText (char[] text, int index, int count, float x, float y, Paint paint). index will be 0, and count will be scoreCount which indicates how many characters in text should be drawn. In the example above, it doens't matter what's in text[10] to text[12], it's considered invalid. You can continue to update text[] using the character map, and this should not create any objects.
I hope this helps. The code above isn't very robust, but I wrote it out as more of an expression of the ideas I'm trying to convey. You will have to create your own loops and manage the data properly within your code, but this sums up the mechanics of what needs to happen to avoid the use of Strings/StringBuilder/StringBuffer/etc.

Categories

Resources