When to use setWidth, setHeight, setLayoutParams(new LinearLayout.LayoutParams(w,h))? - android

I am confused on when to use setWidth, and setHeight? It usually don't work.
What always work is setLayoutParams.
This will work.
sampleButton = new Button(this);
sampleButton.setLayoutParams(new LinearLayout.LayoutParams(65, 65));
This will not work.
sampleButton = new Button(this);
sampleButton.setHeight(65);
sampleButton.setWidth(65);
Or maybe there are some initialisation for this code to work?

So, just looked at the Button's source code (which is a subclass of TextView):
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.1_r2/android/widget/TextView.java#TextView.setHeight%28int%29
here is the method for setHeight
public void setHeight(int pixels) {
mMaximum = mMinimum = pixels;
mMaxMode = mMinMode = PIXELS;
requestLayout();
invalidate();
}
now in the onMeasure method, mMaximum and mMaxMode are used here
int unpaddedHeight = height - getCompoundPaddingTop() - getCompoundPaddingBottom();
if (mMaxMode == LINES && mLayout.getLineCount() > mMaximum) {
unpaddedHeight = Math.min(unpaddedHeight, mLayout.getLineTop(mMaximum));
}
It looks like the setheight doesn't really overwrite the internal height parameters, more just sets flags for layout.
Changing the layout params (which are what are actually referenced when the view is laying itself out) seem to inform the view that it actually needs to be that hight
TLDR; setHeight has more to do with the line height of the text than the height of the view

Related

How to know what size a TextView WOULD be with certain string

Maybe I'm not entering the right keywords, but I'm not finding an answer. I want to know what the dimensions of a TextView would be if I were set it with a certain string. However, I want to know before everything gets laid out in the activity.
My TextView has a fixed width and a variable height. I can get the height like this:
myTextView.setText(myString);
// ... UI gets laid out ...
myTextView.getHeight()
I want to change the width of the TextView if the height gets past a certain point. (But not before then.) And rather than waiting until after the UI gets laid out, I want to know beforehand what the height would be if it had myString and then change the width if I needed to.
I looked at the Layout class but I couldn't figure out what to do. I wonder if it might have something to do with overriding the TextView's onMeasure but I really don't know how to attempt that. Any help is appreciated.
Update
Thanks to both #user3249477 and #0xDEADC0DE for their answers. I'm marking #user3249477's answer as the solution for now (although since I need multiple resizes of the view I'm not sure about repeatedly turning the visibility on and off) but also +1 to #0xDEADC0DE for giving me the keywords I needed to further look into this problem.
I need to do more research and testing on this. Here are some links that I have found helpful so far:
OnLayoutChangeListener:
View.OnLayoutChangeListener
Capture Layout resize before API 11
After changing a property on a LayoutParams object, do I need to call setLayoutParams again?
measureText() and getTextBounds():
Android Paint: .measureText() vs .getTextBounds()
Paint.getTextBounds() returns to big height
Gettextbounds in android
Overriding onSizeChanged of the parent view also looks intriguing: https://stackoverflow.com/a/14399163/3681880
Set your TextView to invisible:
android:visibility="invisible"
and measure it. Once you're done set it to visible:
TextView myTextView = (TextView) findViewById(R.id.text);
final int maxHeight = 500;
myTextView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
v.removeOnLayoutChangeListener(this);
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) v.getLayoutParams();
Log.e("TAG", "H: " + v.getHeight() + " W: " + v.getWidth());
if (v.getWidth() > maxHeight) {
params.width += 100;
v.setLayoutParams(params);
}
v.setVisibility(View.VISIBLE);
}
});
You could do it without overriding. If you get the TextViews Paint with getPaint(), you can use measureText(string) the get the minimal with of the TextView when it is drawn with that Paint. I looks like this:
TextView textView = new TextView(this);
float textWidth = textView.getPaint().measureText("Some Text");
Update
To get the height, you can call getTextBounds() on the Paint object like this:
String text = "Some Text";
Rect textBounds = new Rect();
textView.getPaint().getTextBounds(text, 0, text.length(), textBounds);
float height = textBounds.height();
float width = textBounds.width();

(ANDROID) ImageView won't position correctly

I have an Android related issue:
I am trying to centre a logo on the screen of my device, but it won't position correctly.
I am using the following function:
public void ImageCentered(int ID){
ImageView iv = (ImageView) findViewById(ID);
int x = 0;
int y = 0;
RelativeLayout.LayoutParams position = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
x = (screenWidth/2)-(iv.getWidth()/2);
y = (screenHeight/2)-(iv.getHeight()/2);
position.setMargins(x, y, 0, 0);
iv.setLayoutParams(position);
}
This could should work,but it won't. The image is set off slightly to the right and bottom like in this image:
https://www.dropbox.com/s/tf7r1u1xqcmb9t9/2014-08-16%2018.36.04.png
Now, the strange thing is, when I use the following code:
public void ImageCentered(int ID){
ImageView iv = (ImageView) findViewById(ID);
int x = 0;
int y = 0;
RelativeLayout.LayoutParams position = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
x = (screenWidth/2)-(iv.getWidth()/2);
y = (screenHeight/2)-(iv.getHeight()/2);
position.setMargins(x, y, 0, 0);
Message(IntToStr(x)+", "+IntToStr(y));
iv.setLayoutParams(position);
}
this is the result:
https://www.dropbox.com/s/mjw80zlzkav6dzs/2014-08-16%2018.41.16.png
Side note: The text in the Message() function does not matter, nor does its position within the ImageCentered() function.
I am not calling the function in my OnCreate(), as the width and height of the image would always return 0, so I looked something up and found this:
#Override
public void onWindowFocusChanged(boolean hasFocus){
ImageCentered(R.id.image);
}
This piece of code is in my MainActivity.java file, whereas the ImageCentered() function is in my UtilLib.java file.
So, I was wondering: What's going on here? Why does the code work when I pop in a Message() but not when I leave it out?
Sure, I can try hardcoding the data, but what about smaller/bigger screens?
I hope an Android guru can help me out here, as I've been struggling with this for quite some time now.
EDIT
Just noticed something interesting when pressing "OK" on my Message:
https://www.dropbox.com/s/a7lu6588hy1opw7/2014-08-16%2018.51.55.png
My guess is that my problem lies there, but after clicking the "OK" button once more, the data is "492, 207" again. scratches head
Assuming rom your code that the ImageView is inside a RelativeLayout, You could also do:
// get imageview layout params or create new ones
RelativeLayout.LayoutParams params = imageView.getLayoutParams();
params.addRule(RelativeLayout.CENTER_HORIZONTAL);
this way the image will be automatically centered without all those manual calculations!.
You can also specify it in the XML with
<ImageView .....
android:layout_centerHorizontal="true"/>
I've ultimately decided to just hardcode the x and y coordinates, and later on use some sort of scaling-conversion to position them properly, unless someone can provide me with a better method of fixing this.
UPDATE
So, after googling after a while (again), I have finally found the answer and created two functions:
public int GetImageHeight(int ID){
ImageView iv = (ImageView)findViewById(ID);
iv.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
iv.layout(0, 0, iv.getMeasuredWidth(), iv.getMeasuredHeight());
return iv.getMeasuredHeight();
}
public int GetImageWidth(int ID){
ImageView iv = (ImageView)findViewById(ID);
iv.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
iv.layout(0, 0, iv.getMeasuredWidth(), iv.getMeasuredHeight());
return iv.getMeasuredWidth();
}
All you have to do is pass the ID of the image you made in your xml file, like so:
GetImageHeight(R.id.logo)

Why I'm getting different results when running TextView.measure() in code, from what I see rendered on screen

I'm now trying to resolve an issue with somehow overlapping text in TextView.
I've posted a question about that , but I've also tried to solve it myself. I decided to count the amount of text which causes the textview to break the line. I came up with this unpolished code, which basically should inflate the view, set it's layout params according to the displayed size and then run onmeasure and return lineCount. I plan than to use probably binary search to find exact text length which fits into the textview, but even before I've just tried to run the code and see how it behaves. It's kind of weird, because it gives me different results, than what I see on screen than. I even tried to alter the textsize according to scaled density, because I wasn't sure whether it's been taken into account.
Here is the code. It returns three,but when I render the layout to screen the text takes up only two lines.
public int getCountOfLines(Context context, int widgetDp){
LayoutInflater mLayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
LinearLayout layout = (LinearLayout) mLayoutInflater.inflate(R.layout.event_basic_large ,null, false);
TextView titleTextView = (TextView) layout.findViewById(R.id.itemTitle);
titleTextView.setText(TEST_TEXT);
float density = context.getResources().getDisplayMetrics().density;
float textDensity = context.getResources().getDisplayMetrics().scaledDensity;
titleTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, titleTextView.getTextSize()/density*textDensity);
layout.setLayoutParams(
new LinearLayout.LayoutParams(
(int) (widgetDp*density),
ViewGroup.LayoutParams.WRAP_CONTENT)
);
layout.measure(View.MeasureSpec.makeMeasureSpec(
(int) (widgetDp*density), View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
);
Log.d(TAG, titleTextView.getLineCount() + " lines, width "+titleTextView.getMeasuredWidth());
return titleTextView.getLineCount();
}
I ran into this a while back and after searching and trying finally got the following function to work:
public int getLineCount(String testString, float textSize,
float width) {
Rect bounds = new Rect();
Paint paint = new Paint();
paint.setTypeface(<font>);//set font that you are using for this
paint.setTextSize(textSize);
paint.getTextBounds(testString, 0, testString.length(), bounds);
return (int) Math.ceil(bounds.width() / width);
}
This function uses textSize and width to give number of lines.
This functions give number of line in a textview before it is displayed.

Best solution to draw responsive areas on image

I'm wondering what would be the best solution to get to the result shown below.
Here is what i've found so far:
an ImageView for the forest and a transparent surfaceView (to handle touch) on which I would draw the rectangles?
Or...
Just One SurfaceView with the image set as background and rectangles directly drawn on...?
For those 2 I've already chosen a RelativeLayout.
Which of those 2 would be the most efficient and easiest to do?
Or maybe there is another way which I haven't think about.
In any case thanks for your advice, here is what I tend to...
I've implemented this by placing the image in a RelativeLayout (FrameLayout would work too), and then adding each outlined view programatically. If you know the x and y origin (perhaps as a ratio to the image) and the size for each area, you can easily inflate each view/area (with a black border, transparent center), make it clickable and set a listener, and then set it's origin by adjusting it's margins. You may want to perform all of this after the image has finished laying out:
I put this in onActivityCreated of my Fragment, but other lifecycle methods would work too...
ViewTreeObserver vto = image.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
if (image.getMeasuredHeight() > 0) {
addHotSpots();
ViewTreeObserver obs = image.getViewTreeObserver();
obs.removeGlobalOnLayoutListener(this);
}
}
});
And this is how I actually place all the hotspots/areas:
protected void addHotSpots() {
HotSpot[] hotSpots = res.hotspots;
for (HotSpot hs : hotSpots) {
addHotSpotToImage(hs);
}
private void addHotSpotToImage(HotSpot hs) {
int height = image.getMeasuredHeight();
int width = image.getMeasuredWidth();
//this piece will probably be different for you
//depending on what you know about the area's intended size/position
double hsHeightRatio = hs.lr.y - hs.ul.y;
double hsWidthRatio = hs.lr.x - hs.ul.x;
double leftMargin = hs.ul.x * width;
double topMargin = hs.ul.y * height;
double hsHeight = height * hsHeightRatio;
double hsWidth = width * hsWidthRatio;
LayoutInflater vi = (LayoutInflater) image.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View newSpot = vi.inflate(R.layout.question_hotspot, null);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams((int) hsWidth, (int) hsHeight);
newSpot.setTag(hs.key);
newSpot.setFocusable(true);
newSpot.setClickable(true);
newSpot.setFocusableInTouchMode(true);
newSpot.setOnTouchListener(this);
params.topMargin = (int) topMargin;
params.leftMargin = (int) leftMargin;
image.addView(newSpot, params);
}

Android - ImageView margins not appearing in LinearLayout

Right now, I'm struggling to accomplish something as simple as adding margin space between my child ImageViews within a custom LinearLayout (modified RadioGroup that is designed to take in a custom ImageView that implements Checkable, didn't override onMesarue). Long story short, these images are of a fixed dimension (60x60dip), and since they are dynamic (from the web), I had to add them dynamically like so:
for(int i = 0; i < num; i++){
ImageViewRadioButton childImage = new ImageViewRadioButton(mContext);
float imagehWidthHeight = getResources().getDimension(R.dimen.image_width_and_height);
LinearLayout.LayoutParams imageParams = new LinearLayout.LayoutParams((int) imageWidthHeight, (int) imageWidthHeight);
int imageSpacing = Utils.dipsToPixels(10, mContext);
int innerPadding = Utils.dipsToPixels(5, mContext);
imageParams.leftMargin = imageSpacing;
imageParams.rightMargin = imageSpacing;
childImage.setAdjustViewBounds(true);
childImage.setLayoutParams(imageParams);
childImage.setBackgroundDrawable(getResources().getDrawable(R.drawable.blue_pressed));
childImage.setPadding(innerPadding, innerPadding, innerPadding, innerPadding);
childImage.setClickable(true);
//other non-image properties...
imageContainer.addView(childImage);
}
The only thing that does work is the padding, which it spaces it out properly. However, I am not seeing any space between the padding of each child (margins). Am I doing this correctly, or is there a better way of doing it short of overriding onMeasure to factor in each child's margins?
You had create imageParams but you are not using that parameters in your code instead of imageParams you are using swatchParams parameter. And you had not put a code of swatchParams parameter.

Categories

Resources