Could not find any good solution calculating textview height where text was set before rendering textview to layout. Any help please
2 solutions
Used solution 1 at first and found solution 2 later on. Both work, it's really what you prefer.
Important is to make sure you got all the dimensions right since mixing font sizes in sp or px will give quite a difference depending on what screen you test on.
A very basic example project is available at https://github.com/hanscappelle/SO-3654321
Solution 1 using TextView and MeasureSpec
Main issue with original question is TextView in below method should be configured as our TextView which should be rendered to layout. I think this solution is valuable for many people who faced this problem.
public static int getHeight(Context context, CharSequence text, int textSize, int deviceWidth, Typeface typeface,int padding) {
TextView textView = new TextView(context);
textView.setPadding(padding,0,padding,padding);
textView.setTypeface(typeface);
textView.setText(text, TextView.BufferType.SPANNABLE);
textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, textSize);
int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(deviceWidth, View.MeasureSpec.AT_MOST);
int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
textView.measure(widthMeasureSpec, heightMeasureSpec);
return textView.getMeasuredHeight();
}
And an example of how to use this:
// retrieve deviceWidth
int deviceWidth;
WindowManager wm = (WindowManager) textView.getContext().getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2){
Point size = new Point();
display.getSize(size);
deviceWidth = size.x;
} else {
deviceWidth = display.getWidth();
}
// the text to check for
String exampleTextToMeasure = "some example text that will be long enough to make this example split over multiple lines so we can't easily predict the final height";
// some dimensions from dimes resources to take into account
int textSize = getContext().getResources().getDimensionPixelSize(R.dimen.text_size);
int padding = getContext().getResources().getDimensionPixelSize(R.dimen.text_padding);
// final calculation of textView height
int measuredTextHeight = getHeight(getContext(), exampleTextToMeasure, textSize, deviceWidth, TypeFace.DEFAULT, padding);
Solution 2 using TextPaint and StaticLayout
This method relies on a TextPaint and StaticLayout which also gives reliable results on all API levels I've tested so far. Pay good attention to units of dimensions; all should be in pixels!
Source: Measuring text height to be drawn on Canvas ( Android )
public static int method1UsingTextPaintAndStaticLayout(
final CharSequence text,
final int textSize, // in pixels
final int deviceWidth, // in pixels
final int padding // in pixels
) {
TextPaint myTextPaint = new TextPaint();
myTextPaint.setAntiAlias(true);
// this is how you would convert sp to pixels based on screen density
//myTextPaint.setTextSize(16 * context.getResources().getDisplayMetrics().density);
myTextPaint.setTextSize(textSize);
Layout.Alignment alignment = Layout.Alignment.ALIGN_NORMAL;
float spacingMultiplier = 1;
float spacingAddition = padding; // optionally apply padding here
boolean includePadding = padding != 0;
StaticLayout myStaticLayout = new StaticLayout(text, myTextPaint, deviceWidth, alignment, spacingMultiplier, spacingAddition, includePadding);
return myStaticLayout.getHeight();
}
From support_ms answer, there is a more simple method that take only a TextView as parameter.
/**
* Get the TextView height before the TextView will render
* #param textView the TextView to measure
* #return the height of the textView
*/
public static int getTextViewHeight(TextView textView) {
WindowManager wm =
(WindowManager) textView.getContext().getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
int deviceWidth;
if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2){
Point size = new Point();
display.getSize(size);
deviceWidth = size.x;
} else {
deviceWidth = display.getWidth();
}
int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(deviceWidth, View.MeasureSpec.AT_MOST);
int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
textView.measure(widthMeasureSpec, heightMeasureSpec);
return textView.getMeasuredHeight();
}
Good answer from #support_ms, but I'm not sure of the point of creating a new TextView and working out all of this input params when you could just format your TextView first and then call the static method with just one parameter, the TextView itself!
Also I'm not sure why one parameter was labelled deviceWidth I just use the width of the Textview itself. Mine was match_parent and I suppose any TextView with wrap_content may not work at all. But that's what you get.
public static int getHeight(TextView t) {
int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(screenWidth(t.getContext()), View.MeasureSpec.AT_MOST);
int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
t.measure(widthMeasureSpec, heightMeasureSpec);
return t.getMeasuredHeight();
}
public static int screenWidth(Context context)
{
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
return display.getWidth();
}
Here is my easy solution its get the size before be painted
https://stackoverflow.com/a/40133275/1240672
Get line of TextView before rendering
This is my code base on idea above. It's working for me.
private int widthMeasureSpec;
private int heightMeasureSpec;
private int heightOfEachLine;
private int paddingFirstLine;
private void calculateHeightOfEachLine() {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int deviceWidth = size.x;
widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(deviceWidth, View.MeasureSpec.AT_MOST);
heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
//1 line = 76; 2 lines = 76 + 66; 3 lines = 76 + 66 + 66
//=> height of first line = 76 pixel; height of second line = third line =... n line = 66 pixel
int heightOfFirstLine = getHeightOfTextView("A");
int heightOfSecondLine = getHeightOfTextView("A\nA") - heightOfFirstLine;
paddingFirstLine = heightOfFirstLine - heightOfSecondLine;
heightOfEachLine = heightOfSecondLine;
}
private int getHeightOfTextView(String text) {
// Getting height of text view before rendering to layout
TextView textView = new TextView(context);
textView.setPadding(10, 0, 10, 0);
//textView.setTypeface(typeface);
textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getResources().getDimension(R.dimen.tv_size_14sp));
textView.setText(text, TextView.BufferType.SPANNABLE);
textView.measure(widthMeasureSpec, heightMeasureSpec);
return textView.getMeasuredHeight();
}
private int getLineCountOfTextViewBeforeRendering(String text) {
return (getHeightOfTextView(text) - paddingFirstLine) / heightOfEachLine;
}
Note: This code also must be set for real textview on screen
textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getResources().getDimension(R.dimen.tv_size_14sp));
Kotlin extension
fun TextView.calculateHeight(text: CharSequence = getText()): Int {
val alignment = when(gravity) {
Gravity.CENTER -> Layout.Alignment.ALIGN_CENTER
Gravity.RIGHT -> Layout.Alignment.ALIGN_OPPOSITE
else -> Layout.Alignment.ALIGN_NORMAL
}
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
StaticLayout.Builder.obtain(text, 0, text.length, TextPaint(paint), width)
.setLineSpacing(lineSpacingExtra, lineSpacingMultiplier)
.setAlignment(alignment)
.setIncludePad(true).build()
} else {
#Suppress("DEPRECATION")
StaticLayout(
text, TextPaint(paint), width, alignment,
lineSpacingMultiplier, lineSpacingExtra, true
)
}.height
}
Related
My application is cut off from the top side by the notch
Is there any specific solution to overcome this problem.
I am using the emulator Pixel 3 XL API 27
image for illustrstion
What should I do any code is there to increase the height of the top bar or status bar or should I increase the height of the title bar, but the title bar is still cut off
Any suggestion or code explanation
Try below solution
It is get size of notch as per device and set margin top to view So,it's fit in all device.
#Override
protected void onCreate(Bundle savedInstanceState) {
this.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
this.getWindow().setStatusBarColor(Color.TRANSPARENT);
super.onCreate(savedInstanceState);
setContentView(MainActivity.this, R.layout.activity_main);
/*------------ Check display cutout size and give top margin to toolbar -------------*/
setMarginTopAccordingDisplayCutout(context, yourTopMainView, convertDpToPixel(34), 0);
/*-----------------------------------------------------------------------------------*/
...
//your Code
}
private void setMarginTopAccordingDisplayCutout(Context context, View view, int extraTopWithoutCutout, int extraTopWithCutout) {
int statusBarHeight = 0;
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
statusBarHeight = context.getResources().getDimensionPixelSize(resourceId);
}
if (statusBarHeight > convertDpToPixel(24)) {
final ViewGroup.MarginLayoutParams[] layoutParams = new ViewGroup.MarginLayoutParams[1];
layoutParams[0] = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
layoutParams[0].topMargin = statusBarHeight + extraTopWithCutout;
view.setLayoutParams(layoutParams[0]);
//topbarlp.setMargins(0, statusBarHeight, 0, 0);
//Set above layout params to your layout which was getting cut because of notch
///topbar.setLayoutParams(topbarlp)
Log.e(TAG, "onCreate statusBarHeight :: " + statusBarHeight);
} else {
final ViewGroup.MarginLayoutParams[] layoutParams = new ViewGroup.MarginLayoutParams[1];
layoutParams[0] = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
layoutParams[0].topMargin = extraTopWithoutCutout;
view.setLayoutParams(layoutParams[0]);
}
}
private int convertDpToPixel(float dp) {
DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
float px = dp * (metrics.densityDpi / 160f);
return Math.round(px);
}
I hope this can help you!
Thank You.
I have an imageview that moves around the screen. So, I've set the LayoutParams to do it. But, the android insists on resize my imageview. How can I prevent this behavior?
public void move(int x, int y)
{
width = this.getWidth();
height = this.getHeight();
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(WIDTH, HEIGHT);
int t = validateMin(calcTopPos(y));
int l = validateMin(calcLeftPos(x));
int r = validateMaxWidth(calcRightPos(x))-1;
int b = validateMaxHeight(calcBottomPos(y));
params.setMargins(l, t, r, b);
this.setLayoutParams(params);
requestLayout();
}
I've found the answer: android:adjustViewBounds = "true" in the xml file. But, I still don't know why android was resizing my view. It's a mystery!
I have an activity with
android:theme="#android:style/Theme.Dialog"
to show it as float window. Also I have a line
getWindow().setLayout(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
And I need a width/height of this window to show content correctly.
I've tested four options and did not get an answer:
//1
int width = getWindowManager().getDefaultDisplay().getWidth();
//2
int width = getWindow().getDecorView().getWidth();
//3
getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
//4
int width = getWindow().getAttributes().width;
Thanks!
use this function:
private int getting_screen_width(Dialog dialog)
{
DisplayMetrics dm = new DisplayMetrics();
dialog.getWindow().getWindowManager().getDefaultDisplay().getMetrics(dm);
String width = ""+dm.widthPixels ;
dm = null;
return (Integer.parseInt(width));
this return the value of the screen width
I want to save(export) contents of MyView, which extends TextView, into a bitmap.
I followed the code: [this][1].
It works fine when the size of the text is small.
But when there are lots of texts, and some of the content is out of the screen, what I got is only what showed in the screen.
Then I add a "layout" in my code:
private class MyView extends TextView{
public MyView(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
public Bitmap export(){
Layout l = getLayout();
int width = l.getWidth() + getPaddingLeft() + getPaddingRight();
int height = l.getHeight() + getPaddingTop() + getPaddingBottom();
Bitmap viewBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(viewBitmap);
setCursorVisible(false);
layout(0, 0, width, height);
draw(canvas);
setCursorVisible(true);
return viewBitmap;
}
}
Now the strange thing happened:
The first time I invoke "export"(I use an option key to do that), I got contents only on the screen.
When I invoke "export" again, I got complete contents, including those out of the screen.
Why?
How to "export" a view, including contents cannot be showed on the screen?
Thank you!
[1]: http://www.techjini.com/blog/2010/02/10/quicktip-how-to-convert-a-view-to-an-image-android/ this
I found out a simpler way:
Put the TextView in a ScrollView.
Now myTextView.draw(canvas) will draw all of the text.
I think you should be subtracting the padding from the width in the height instead of adding it. Adding it will give you an area larger than the screen.
I solved this issue this way(strange but works):
public Bitmap export(){
//...
LayoutParams lp = getLayoutParams();
int old_width = lp.width;
int old_height = lp.height;
int old_scroll_x = getScrollX();
int old_scroll_y = getScrollY();
lp.width = width;
lp.height = height;
layout(0, 0, width, height);
scrollTo(0, 0);
draw(canvas);
lp.width = old_width;
lp.height = old_height;
setLayoutParams(lp);
scrollTo(old_scroll_x, old_scroll_y);
//...
}
this is a really simple question on which I've found no answer :/
How can I quickly access the screen resolution (width, height) as integer values?
I've tried this one, but it always shows zero on my emulator:
DisplayMetrics dm = new DisplayMetrics();
int width = dm.widthPixels / 2;
In my case I want to dynamically create a table with tableRows, each containing two cols. This cols all shall fill half of the screen in width.
Can someone give me a hint?
The trashkalmar answer is correct.
However, the results will be specific to the activity context. If you want the whole device screen resolution:
DisplayMetrics displayMetrics = new DisplayMetrics();
WindowManager wm = (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE); // the results will be higher than using the activity context object or the getWindowManager() shortcut
wm.getDefaultDisplay().getMetrics(displayMetrics);
int screenWidth = displayMetrics.widthPixels;
int screenHeight = displayMetrics.heightPixels;
Note that the results also depend on the current device orientation.
You can get screen metrics in this way:
Display d = ((WindowManager)getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
int width = d.getWidth();
int height = d.getHeight();
1.Simply use This inside activity to get screen width and height pixels.
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay()
.getMetrics(metrics);
int width = metrics.widthPixels;
int height = metrics.heightPixels;
2.This can also be used But requires Api level inlined check
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
display.getSize(size);
int width2 = size.x;
int height2 = size.y;
} else {
int width2 = display.getWidth();
int height2 = display.getHeight();
}
3.Use This when you are not in activity but having context
WindowManager wm = (WindowManager) context.getSystemService(
Context.WINDOW_SERVICE);
Display display1 = wm.getDefaultDisplay();
Point size1 = new Point();
int width1;
int height1;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
display1.getSize(size1);
width1 = size1.x;
height1 = size1.y;
} else {
width2 = display.getWidth();
height2 = display.getHeight();
}
I use the following:
int scrWidth = getWindowManager().getDefaultDisplay().getWidth();
int scrHeight = getWindowManager().getDefaultDisplay().getHeight();
No muss, no fuss, just 2 class variables
The easiest way will be to implement onSizeChanged. You are probably getting a value of 0 because the view hasn't initialized yet. You can't call the above code in the View constructor for example.