Textview autofit with multiline support - android

i've looked for solutions of auto-fitting the font of the textView according to its size , and found many , but none support multi-lines and does it correctly (without truncating the text and also respect the gravity values) .
has anyone else conducted a solution as such?
is it also possible to set the constraint of how to find the optimal number of lines ? maybe according to max font size or max characters numbers per line?

The following is working for me, IMHO better, than using a ellipsize based solution.
void adjustTextScale(TextView t, float max, float providedWidth, float providedHeight) {
// sometimes width and height are undefined (0 here), so if something was provided, take it ;-)
if (providedWidth == 0f)
providedWidth = ((float) (t.getWidth()-t.getPaddingLeft()-t.getPaddingRight()));
if (providedHeight == 0f)
providedHeight = ((float) (t.getHeight()-t.getPaddingTop()-t.getPaddingLeft()));
float pix = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getResources().getDisplayMetrics());
String[] lines = t.getText().toString().split("\\r?\\n");
// ask paint for the bounding rect if it were to draw the text at current size
Paint p = new Paint();
p.setTextScaleX(1.0f);
p.setTextSize(t.getTextSize());
Rect bounds = new Rect();
float usedWidth = 0f;
// determine how much to scale the width to fit the view
for (int i =0;i<lines.length;i++){
p.getTextBounds(lines[i], 0, lines[i].length(), bounds);
usedWidth = Math.max(usedWidth,(bounds.right - bounds.left)*pix);
}
// same for height, sometimes the calculated height is to less, so use §µ{ instead
p.getTextBounds("§µ{", 0, 3, bounds);
float usedHeight = (bounds.bottom - bounds.top)*pix*lines.length;
float scaleX = providedWidth / usedWidth;
float scaleY = providedHeight / usedHeight;
t.setTextSize(TypedValue.COMPLEX_UNIT_PX,t.getTextSize()*Math.min(max,Math.min(scaleX,scaleY)));
}

Later I've asked a similar question and got an answer to this one:
https://stackoverflow.com/a/17786051/878126
for the custom behaviour, one need to change the code accordingly .

Related

Finding coordinates of a character?

I have a multi-line TextView. Is there a way to get the x coordinate of the pixel to the left of a character e.g. the 3rd character? In my case it will always be the 3rd character, but a general solution would be nicer.
I have the y coordinate of the pixel just above the character using:
Layout textViewLay = mTextView.getLayout();
int posY = textViewLay.getLineTop(0);
but the x coordinate has me stumped. Any ideas? I'm probably missing something really simple.
Try to use this code:
Rect bounds = new Rect();
Paint tpaint = textView.getPaint();
tpaint.getTextBounds(text,0,2,bounds);
int height = bounds.height();
int width = bounds.width();

final float scale = getContext().getResources().getDisplayMetrics().density is always returning a 1

I have posted a message before about setting the text size in pexcles. I found some post that had using the following line to set a scale facter.
final float scale = getContext().getResources().getDisplayMetrics().density;
The text was still drawn t the same size on my acer tables, and vergin mobile phone.
My vergin phone has a resultion around 320 by 400 and the tables is something like 800 by 1100..
When I traced the code with the debuuger, scale is always pne..
code listing.
paint.setColor(Color.WHITE);
final float scale = getContext().getResources().getDisplayMetrics().density;
int size = (int) (18 * scale + 0.5f);
paint.setTextSize(18); // cGlobals.TranslateX
canvas.drawText("SETTINGS:", cGlobals.TranslateX(900),cGlobals.TranslateY(150), paint);
Try this below code..
final int scale = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
(float) 123.4, getResources().getDisplayMetrics());

how to get bounds of a scaled view?

It seems that setScaleX or setScaleY don't actually change left,top,right,bottom properties. getX and getY remain unchanged too.
So if I scale a view whats the easiest way to get 4 corner coordinates of the newly scaled view?
I tried getHitRect but that doesn't give me the right answer. I am trying to avoid manually calculating the new bounds based on existing transformations (rotation and scale with pivots factored in).
After exploring the view api, it looks like there is no direct API method that does this.
However you can easily get the new points by grabbing the transform matrix of the view and using that to get the new bounds.
Something like this:
Matrix m = view.getMatrix();
Rect bbox = new Rect();
view.getDrawingRect(bbox);
m.mapRect(bbox);
If you want to operate on (x,y) coordiantes directly there is a matrix.mapPoints that will achieve the same result.
I believe if you get the width and height and multiply it by the scales, you'll get the scaled width and height.
int scaledWidth = getWidth() * getScaleX();
int scaledHeight = getHeight() * getScaleY();
int newLeft = getLeft() + (scaledWidth / 2);
int newRight = newLeft + scaledWidth;
int newTop = getTop() + (scaledHeight / 2);
int newBottom = newTop + scaledHeight;
This is assuming that you scaled with a pivot x and y at the center of the view. Things gets far more complicated if you have pivots in strange areas.

Scrollable ImageView Android Problem (overscrolling)

I got my image to at least SCROLL, but I don't want it to scroll past the image itself. I have variables called maxLeft, maxRight, etc that I have that I currently just set to
ImageView img = (ImageView)findViewById(R.id.mapimg);
DisplayMetrics dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
int maxX = (int)????;
int maxY = (int)????;
// set scroll limits
final int maxLeft = (maxX * -1);
final int maxRight = maxX;
final int maxTop = (maxY * -1);
final int maxBottom = maxY;
I've been messing around with what I could in place of the question marks I put there, but I seem to be stuck, especially when I try on different emulators. Any help would really be appreciated! Thanks
If I understand your question correctly, you want to extend ImageView to add scrolling. If that's the case, you don't want to use getWindowmanager() as that returns the dimensions of the entire screen (including the title bar). Rather, you want to extend ImageView and get the view's dimensions from onMeasure. You can check out my answer here where I added zoom functionality and panning to ImageView.
I used setImageMatrix and postTranslate to set a matrix equal to the image and move it. To track the image's location, I used the following:
float f[] = new float[9];
matrix.getValues(m); //see matrix documentation. inserts matrix values into f.
float x = m[Matrix.MTRANS_X];
float y = m[Matrix.MTRANS_Y];
Float x and y will track the top left corner of the image. Make sure if the next event will cause the image to scroll out of bounds, you adjust postTranslate to equal the border of the image. The answer i linked above should give you a good place to start, and if you also want zoom functionality, then you're in luck, because you don't have to do any additional work.

Android: measureText() Return Pixels Based on Scaled Pixels

So I use Paint's measureText() method to measure the width of a segment of text, but I wanted to measure text based on a certain text size. Say I wanted to get the width of a text segment that will be 20 scaled pixels when it occupies a certain TextView. I tried the following:
Paint paint = new Paint();
paint.setTextSize(20);
paint.measureText("sample text");
However, it does not seem to be working. I believe it is returning a width with respect to a smaller text size. I feel like I'm missing something that will make me slap myself in the face and yell herp derp.
You need to get the densityMultiplier like so:
final float densityMultiplier = getContext().getResources().getDisplayMetrics().density;
final float scaledPx = 20 * densityMultiplier;
paint.setTextSize(scaledPx);
final float size = paint.measureText("sample text");
I do something like this when I have to.
int textSize = 20;
for(int i = 2; i<18 && curTextSize< textSize;i+=2)
{
this.label.setTextSize(i);
curTextSize = this.label.getPaint().measureText(this.label.getText().toString());
}
I don't have enough reputation points to comment on answers but in reference to the comments by #schwiz and #AndreasEK on the accepted answer:
measureText(), along with getTextBounds(), does not include padding so it's possible that the solution to their problem is to add the left and right padding (or start and end padding)
final float scaleFactor = getContext().getResources().getDisplayMetrics().density;
final float scaledPx = 20 * scaleFactor;
paint.setTextSize(scaledPx);
final float padding = scaleFactor * (textView.getPaddingStart() + textView.getPaddingEnd());
final float size = paint.measureText("sample text") + padding;
Try
Paint.SetLinearText(true)
That solved the problem for me.

Categories

Resources