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!
Related
I have to set background image of ImageView and after time to set imageSource of this ImageView.
So what I did is to set image view background with setBackgroundImage().
Also I set
adjustViewBounds = true and scaleType = "fitCenter", and height = 55dp and width = wrap content.
So I expected at first time(when I set only set background) to have this scenario - the view will be with the same ration and will fit the smallest - width or height (of course if height is larger than 55dp, it will be scaled). But the result is scalled image (when the image should be larger than 55 dp i.e. height is scaled). The image is in RelativeLayout. Unfortunately, I cannot post the whole code.
Thanks in advance.
You have to subclass that ImageView and override onLayout() to keep width/height ratio like so:
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
double ratio = ...
int width = right - left;
int height = ratio * width;
ViewGroup.LayoutParams params = this.getLayoutParams();
params.height = height;
params.width = width;
this.setLayoutParams(params);
this.setMeasuredDimension(width, height);
super.onLayout(changed, left, top, right, top + height);
}
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
}
I have a bitmap and below it is a time line.
As an example consider the right side layout of the FIGURE.
All the bottom timelines (1, 2, 3...) are in the same height from top.
The timeline is a textview which has fixed layout height and width as it is defined in xml
like timeline 1 is defined as:
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="#+id/HView"
android:layout_marginLeft="18dp"
android:layout_marginTop="345dp"
android:textSize="14sp"
android:text="1"
android:textColor="#000000" />
However the bitmap height and width can vary as it is done programatically.
So in certain cases, the bitmap height increases enough to overlap the timeline. In other words,
the vertical position of bitmap increases with respect to the vertical position of the timeline.
I want to get:
1) the ended vertical position of bitmap with respect to top of the screen.
2) the ended vertical position of timeline with respect to top of the screen.
I tried to do the following:
TextView bottomTimeLine = (TextView) view.findViewById(R.id.textView1);
bottomTimeLine.getHeight(); //returns 0.
bottomTimeLine.getBottom(); //returns 0.
ImageView img = new ImageView(getActivity());
img.setImageDrawable(getResources().getDrawable(R.drawable.disp_bg));
img.getHeight(); //returns 0.
img.getBottom(); //returns 0.
As seen from the code, both the methods, getHeight() and getBottom() are returning height as 0.
How to get the height (view end position) of both with respect to top of the cell display ?
Hope this helps
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
this.setMeasuredDimension(
parentWidth / 2, parentHeight);
}
This is how it can be done:
final TextView bottomTimeLine = (TextView) view.findViewById(R.id.textView1);
final int[] timelineCoord = new int[2];
final int[] imgCoord = new int[2];
ViewTreeObserver vto = bottomTimeLine.getViewTreeObserver();
vto.addOnGlobalLayoutListener((new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
bottomTimeLine.getLocationOnScreen(timelineCoord);
Log.d(" bottomTimeLine H ", ""+timelineCoord[1]);
timelineHeight = timelineCoord[1];
}
}));
ViewTreeObserver vt1 = img.getViewTreeObserver();
vt1.addOnGlobalLayoutListener((new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
img.getLocationOnScreen(imgCoord);
imgHeight = imgCoord[1] + img.getHeight();
Log.d("Img H ", ""+imgHeight);
if(imgHeight < timelineHeight)
{
int heightDiff = imgHeight - timelineHeight ;
heightDiff = heightDiff + 3;
img.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, heightDiff));
}
}
}));
I have to develop a UI like below:
I want to show this type of image and show hotspot on that image. The position of hotspot will be dynamic, as per x,y and radius provided the circle will be drawn on the original picture. The user can click on the hotspots and onclick action will be defined on the specific hotspot on which the user will click.
What is best process to develop this type of UI?
Make your main layout a RelativeLayout and then you can add programmatically a ImageView with an onClickListener to your layout with the following code:
private void addImageView(RelativeLayout mainLayout, int x, int y, int width, int height, OnClickListener onClickListener){
ImageView imageView = new ImageView(this);
imageView.setAdjustViewBounds(false);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
params.height = height;
params.width = width;
imageView.setLayoutParams(params);
imageView.setScaleType(ImageView.ScaleType.FIT_XY); //remove this if you want to keep aspect ratio
imageView.setImageDrawable(getResources().getDrawable(R.drawable.ic_launcher)); //here goes your drawable
params.leftMargin = x - width/2;
params.topMargin = y - height/2;
imageView.setOnClickListener(onClickListener);
mainLayout.addView(imageView);
}
to use it you call:
RelativeLayout mainLayout = (RelativeLayout) findViewById(R.id.relativeLayout); //this is your main layout
addImageButton(mainLayout, 200, 300, 200, 200, new OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "clicked", Toast.LENGTH_SHORT).show();
}
});
You can also use a ImageButton to achive the same porpose, although the image size will be affected by button border:
private void addImageButton(RelativeLayout mainLayout, int x, int y, int width, int height, OnClickListener onClickListener){
ImageButton imageButton = new ImageButton(this);
imageButton.setAdjustViewBounds(true);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
params.height = height;
params.width = width;
imageButton.setLayoutParams(params);
imageButton.setScaleType(ImageView.ScaleType.FIT_XY);
imageButton.setImageDrawable(getResources().getDrawable(R.drawable.ic_launcher));
params.leftMargin = x - width/2;
params.topMargin = y - height/2;
imageButton.setOnClickListener(onClickListener);
mainLayout.addView(imageButton);
}
Try it.
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);
//...
}