RelativeLayout, setting width of TextView - android

I have a ListView and each row is a RelativeLayout with 2 TextViews side-by-side (call them textViewLeft and textViewRight).
The textViewRight text can change between "xxxxxxx" and "yyy" (both different lengths). I want to set the textViewRight width to be able to fit the "xxxxxxx", and then if I change the text to "yyy", I DON'T want the textViewRight width to change.
I know that in my xml I can specify layout_width to a particular value, but devices have lots of different fonts. So is there a way that I can programmatically set the width of textViewwRight to ensure it always fits the string "xxxxxxx"?

Well, there is no easy way to answer that, you could set the width through code, using the width available, you coul change the text size depending on the screensize, there are several different solutions.
It really depends on how you are handling your app, do you already have different layout folders? If so why not try some sizes that will work well for the intended range?
Either way, you should calculate it to fit the bigger text from the start and not change it when the text changes.
To be more specific. One solution would be to have the text on the Textview be xxxxxx, and have the layout's width in xml be "wrap_content".
Now what you do is, get the view's width after it has been laid out, or else you get a 0 return.
One way to do exactly that is to add a viewtreeobserver to your TextView on your onCreate method:
ViewTreeObserver vto = yourTextView.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
this.yourTextView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
int width = layout.getMeasuredWidth();
int height = layout.getMeasuredHeight();
}
});
Now you have the width, all you have to do is change the width to that fixed value, the following should work:
yourTextView.getLayoutParams().width = theWidthYouGot;

Related

Android Row: How to make first and third views' widths equal to the widest of them, with the middle view expanding to fill the row

I am designing a title row that has the item title in the middle, with a status description on the left and expiration date description on the right. The title should be centered at all times and expand width until it runs into either of the side views, at which time it should get ellipsized. I'm having trouble figuring out how to keep the title view centered while allowing for the two side views to have different sizes, while also trying to show as much of the text as possible in cases where the side views have only two or three characters, or as many as 20.
This is NOT a basic question of having the center view fill the space. The cases will outline the additional requirements I have that make it more tricky.
I tried two main approaches:
LinearLayout
I started with a horizontal LinearLayout. The first and third TextViews had widths of WRAP_CONTENT while the center title view had a width of 0dp and weight of 1, allowing it to fill the middle section of the row. The issue here was that the first and third views wouldn't have the same width with WRAP_CONTENT, so the title view would be shifted one way or the other. See here how the title is shifted to the left towards the smaller text:
After that, I assigned a weightSum of 20 to the LinearLayout, gave weights of 4 to the first and third views, a weight of 12 to the middle view, and width of 0dp for all 3. This worked better but wouldn't allow for longer text in the end views, even when there was plenty of room in the row for the view to expand:
Changing the views to use layout_weight and widths of WRAP_CONTENT didn't work either.
ConstraintLayout
I switch the row to a ConstraintLayout to try that as well.
I started this route with the end views using WRAP_CONTENT as their width and the title view using 0dp. The title view had its start linked to the end of the first view, and its end linked to the start of the last view. Of course, this would shift the title view towards whichever end text was shorter. Changing the end views to a width of 0dp yielded the same result.
From here I got closer. I linked the middle view's start and end to the parent LinearLayout so that it stretched the entire width. Then I linked the first view's end to the middle view's start, and the last view's start to the middle view's end, and set both of their widths to 0dp. By setting the middle view's width to 0dp and adding a marginStart and marginEnd of 64dp, I allowed some space for the first and last views to be shown. Still, this was like the LinearLayout solution with weights, as the gaps on each side of the middle view was of the fixed 64dp size, and didn't allow for expansion.
My last effort was so close! Changing the middle view's width to WRAP_CONTENT, and start and end both linked to the parent, allowed the end views to be of equal widths, and fully expanded. That is, UNTIL the title view became very long. With a width of WRAP_CONTENT, the middle view would push the end views off the screen with a very long text, even with a min_width set on the end views.
Changing the end views to have widths of WRAP_CONTENT didn't work either.
What I want to accomplish is most like this last try, except that I want the middle view to stop expanding and become ellipsized instead of pushing the end views out. Any other ideas to try?
The problem is if you want the first and third views equal width I don't think it is possible with just standard xml layouts as it's too late once a view has been sized, it won't be resized again without extra code.
Something like TableLayout has the extra code to resize all the rows to match, one it's sized them individually.
But you can to this yourself programmatically
e.g
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LinearLayout mainLayout = findViewById(R.id.content);
// Wait until mainLayout has it's size calculated
ViewTreeObserver.OnGlobalLayoutListener mGlobalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
// Don't listen anymore
mainLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
TextView col1 = new TextView(getApplicationContext());
TextView col2 = new TextView(getApplicationContext());
TextView col3 = new TextView(getApplicationContext());
col1.setText("Much Long");
col1.setBackgroundColor(Color.CYAN);
col3.setText("Long");
col3.setBackgroundColor(Color.CYAN);
col3.setGravity(Gravity.RIGHT);
col2.setText("Really Really Really Really Really Really Really Really Really Long");
col2.setGravity(Gravity.CENTER_HORIZONTAL);
col2.setSingleLine(true);
// Set it to have Ellipsizes if too long
TextUtils.TruncateAt truncate = TextUtils.TruncateAt.END;
col2.setEllipsize(truncate);
int measureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); // As big as the view wants to be
// Measure all the views
col1.measure(measureSpec, measureSpec);
col2.measure(measureSpec, measureSpec);
col3.measure(measureSpec, measureSpec);
// work which side is bigger
int maxWidth = Math.max(col1.getMeasuredWidth(), col3.getMeasuredWidth());
int parentWidth = mainLayout.getWidth();
// Probably should have some checks to make sure centre width does not go negative
int centreWidth = parentWidth - (2 * maxWidth);
col1.setWidth(maxWidth);
col2.setWidth(centreWidth);
col3.setWidth(maxWidth);
mainLayout.addView(col1);
mainLayout.addView(col2);
mainLayout.addView(col3);
}
};
mainLayout.getViewTreeObserver().addOnGlobalLayoutListener(mGlobalLayoutListener);
}
}
Produces some examples
The centre of the middle column is always at the centre of the screen

Reducing text size in textview if the view is covered by another view or is off screen

I am trying to make sure the text is completely visible within a fixed height view. If the textview's height is taller than the view it is in, the text size will need to shrink. Someone told me to use Paint and measure the text. I'm not sure how I am supposed to check if the view is out it's parents view or being covered by another view.
ViewTreeObserver vto = mTextView.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
ViewTreeObserver obs = mTextView.getViewTreeObserver();
obs.removeGlobalOnLayoutListener(this);
Paint paint = mTextView.getPaint();
float measurment = paint.measureText(mTextView.getText().toString());
//What to do!?
}
});
View, the superclass of TextView, has a getHeight() function which will measure the pixel size. See also TextView.getLineHeight() to return the height, in pixels, of a single line of text, and getLineCount() for the number of lines the TextView has.
I would caution against your method. It is much easier (/saner) to use different layout densities, instead of programmatically adjusting your layouts between devices. It's a bit annoying to set up, but I would say that it's way easier to set up your layouts once in a GUI preview than have to redo math and switch back and forth between running and testing.

Get measure height of a TextView without add to the view hierarchy

I get following requirement.
I need to build a tree with leaves placed left and right, from the screen top to the bottom. I can not put the leaves in a ListView because tow leaves will be in same offset.
I don't know the height of the items.
I need to pre-calculate the height of dynamic content, such as strings with different lengths.
QUESTION:
How can i pre-calculate the height of a sting which will be put in a TextView widget described as follows:
<TextView
android:id="#+id/note_content"
android:layout_width="40sp"
android:layout_height="wrap_content"
android:maxLines="5" />
Create textView, set LayoutParams, set text, call it's measure method and read measured values. BTW you can measure text, not textView (see StaticLayout).
I think you better create your custom layout where you can implement all your "leaves measument" algorithms. Check this lesson. Custom layout is very easy )
Use this for calculate height and width your textview
textView.getViewTreeObserver().addOnGlobalLayoutListener(
new OnGlobalLayoutListener() {
public void onGlobalLayout() {
h=textView.getHeight();
w=textView.getWidth();
textView.getViewTreeObserver()
.removeGlobalOnLayoutListener(this);
}
But in xml do not assign textview as a wrap content. Because it will give you value 0.

Creating dynamically sized square (equal height and width) UI elements in Android

As the title states, I am trying to set the width of a layout element to be equal to the height (which is set to match the parent). I've tried setting the width parameter programatically from the height parameter, but this shows up as -1 rather than the actual height (since it matches the parent). Anyone know how I can create square layout elements whose size is dynamic? Thanks.
So what I've been doing for things like this -- and I don't know that it's ideal, but it works quite well -- is use ViewTreeObserver.OnGlobalLayoutListener. Something like this:
View myView = findViewById(R.id.myview);
ViewTreeObserver observer = myView.getViewTreeObserver();
observer.addOnGlobalLayoutListener(new SquareLayoutAdjuster());
class SquareLayoutAdjuster
implements ViewTreeObserver.OnGlobalLayoutListener {
#Override
public void onGlobalLayout() {
int dimension = myView.getHeight();
LayoutParams params = myView.getLayoutParams();
params.width = dimension;
myView.setLayoutParams(params);
observer.removeGlobalOnLayoutListener(this);
}
}
That's just the general idea, I don't know if it compiles as is, but basically in the onGlobalLayout() method, your views are guaranteed to be measured. You remove the listener at the end to prevent it from being called multiple times (unless you need that to happen for whatever reason).
the solution of KCOPPOCK works for me, thank you. But i got "java.lang.IllegalStateException: This ViewTreeObserver is not alive, call getViewTreeObserver() again" at runtime. So i use
findViewById(R.id.serie_main_layout).getViewTreeObserver().removeOnGlobalLayoutListener(this);
instead
observer.removeGlobalOnLayoutListener(this);
and all works perfectly.

Programmatically getting the width of a TextView

I'm attempting to build a layout programmatically.
This layout is dynamic. Based on the data read from a database, it could look different every time.
I have a LinearLayout with its orientation set to vertical.
I want to fit as many TextViews with text (the data from database) as I can on a "row".
What I'm doing is building LinearLayouts that are the rows. These LinearLayouts are populated with TextViews. I build the TextView, set the text, and then check the width to see if it will fit on this row (by subtracting the sum of all TextView widths from the screen width and see if the new TextView will fit). If not, I create a new LinearLayout and start adding TextViews to that one.
The problem is myTextView.getWidth() and myTextView.getMeasuredWidth() both return 0.
Why? How can I get the width of the TextView?
Well first of all, please post your code. Second the getWidth/getHeight returning zero question gets asked A LOT so you could search SO for more answers.
Assuming your calling these methods during onCreate (which is probably the case), the UI hasn't been drawn yet so the returned value is zero. You can't get the width or height of the view until it has been drawn.
You can use this library to schedule the task of perform calculation on the width to the correct time after the view had been completely drawn
You can call it on onCreate() and from any Thread
https://github.com/Mohamed-Fadel/MainThreadScheduler
Sample of use:
MainThreadScheduler.scheduleWhenIdle(new Runnable() {
#Override
public void run() {
int width = textview.getWidth();
int height = textview.getHeight();
textview.setText( String.valueOf( width +","+ height ));
}
});

Categories

Resources