Android horizontal LinearLayout with wrapped text in TextView - android

I've observed a behavior with layout_weight that I can't explain. The following is a trivial example:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="This is a very very very very very very very very very very very very very very very long string."
android:layout_weight="1"
/>
<View
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="center_vertical"
android:background="#ffffffff"
/>
</LinearLayout>
In a QVGA display, the TextView wraps the text. The white square is displayed to the right of the text.
However, if I remove android:layout_weight="1" from the TextView, the TextView now takes up the entire display width. The white square is no longer displayed.
Why would layout_weight in the TextView affect whether or not the white square is displayed? Shouldn't the View with the white background always be assigned 32dpx32dp first? (It makes no difference if the view were any other types - ImageView or TextView).
The problem I was working on is that I want the white square to always be displayed to the right of the TextView (whether or not the text is wrapped), but I don't want any empty space between the TextView and the white square. (If I add android:layout_weight="1" to the TextView, then there is a gap if the text is not wrapped.)
Any help would be appreciated!

To answer my question #1: One thing I learned by looking at the source for LinearLayout: Not only does layout_weight assign unused space to a child, it also shrinks a child with layout_weight if the child extends beyond the bounds of the LinearLayout. That explains why a TextView with wrapped text is shrunk in my layout.
As for the answer to my question #2, I think you meant android:toRigthOf instead of android:layout_alignRight. Using a RelativeLayout instead of a LinearLayout doesn't change the layout behavior. The tricky part is placing a view immediately to the right of a TextView, without gaps, whether or not the text is wrapped. Setting a maxWidth would limit the TextView's width, but that solution doesn't scale across portrait/landscape and different display dimensions.
Solution - Looks like Dyarish's solution is the best available. My layout problem exists regardless of the layout you use. The key is to set a maxWidth for the TextView so that it doesn't take up the all of the horizontal space in the layout. Because hardcoding a android:maxWidth value in the TextView doesn't scale across different displays, setting the maxWidth at runtime, as Dyarish suggested, is a good solution.

Hopefully this is what you are looking for.
First off, here is a great resource I found for Creating UI's.
layout_weight - Specifies how much of the extra space in the layout to be allocated to the View.
If you want to ensure that the white square is always to the right of the textview, you can use a Relative View, and add the parameter to the view. android:layout_alignRight="+id#yourTextViewID". This should always make the box appear right beside the textView area. You should probably also add something like android:maxWidth="250px" This will ensure that you don't push the white box completely out of the screen.
Here is a code sample:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<TextView
android:layout_width="wrap_content"
android:maxWidth="250px"
android:id="#+id/TextForWhiteBox"
android:layout_height="wrap_content"
android:layout_gravity="center|left"
android:text="This is a very very very very very very very very very very very very very very very long string."
/>
<View android:background="#ffffffff" android:layout_width="32dp" android:layout_height="32dp" android:id="#+id/view1" android:layout_toRightOf="#+id/TextForWhiteBox"></View>
</RelativeLayout>
You could also add to the View:
android:layout_alignTop="#+id/TextForWhiteBox" android:layout_alignBottom="#+id/TextForWhiteBox"
to make the white box the same size as the TextView.

Firstly I've tested the code from my other answer and it does exactly what you've described you've wanted. (unless I'm misunderstanding what you are asking for). You definitely do not want to use the android:layout_alignRight which is not what is in the code sample. That would simply keep the box on the right hand of the screen and not be affected by the textview at all. This sample uses android:layout_toRightOf="#+id/TextForWhiteBox" which is possible due to it being a relative layout. Since the Relative Layout allows you to place objects in relation to others. That line will always place the box just to the right of the textview with no gaps.
As for the screen orientation changes:
When the orientation changes it creates a new instance of the view.
Here is a simple solution.
//Add to oncreate in your Activity
private TextView textStatus;
textStatus = (TextView) findViewById(R.id.TextForWhiteBox);
// This get's the width of your display.
DisplayMetrics displaymetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
int width = displaymetrics.widthPixels;
// Now you know the screen orientation, and it's width. So just set the maxwidth of the text view to match the display width - the pixels of your white box.
textStatus.setMaxWidth(width - 32); // 32 is here because you already know the size of the white box. More logic is needed to dynamically get this value, because you would need to wait for the activity to be fully created.
}
Here is the main.xml I used:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<TextView
android:layout_width="wrap_content"
android:id="#+id/TextForWhiteBox"
android:layout_height="wrap_content"
android:layout_gravity="center|left"
android:text="This is a very very very very very very very very very very very very very very very very very very very long string."
/>
<View android:background="#ffffffff" android:layout_width="32px" android:layout_height="32px" android:id="#+id/view1" android:layout_toRightOf="#+id/TextForWhiteBox"></View>
</RelativeLayout>
You might need some additional logic to keep screen values.
This code has been tested, you should be able to literally copy and paste this to work as you asked.
Also depending on your logic you could use something like this to return the screen orientation.
int orient = getResources().getConfiguration().orientation;
Hope this helps!
If this helped you, please click the accepted button. =) Cheers!

Related

Android UI: "Smart" centering?

So I have a UI element (a single line of text) that I want horizontally centered with respect to the overall device -- unless/until it collides with other UI elements in the given view group / layout. At that point I'd like it to be either centered in the space remaining or pegged as close to being centered overall as possible without colliding. [When there's finally not enough space, then I want to use ellipses.]
Is there any way to achieve this using just standard Android layouts?
I'm currently achieving this via code that adjusts layout constraints when the view group's width changes, the text changes, or related UI elements become visible/invisible. It works fine, but I can't help thinking that some layout should just do this for me.
You can use a weighted horizontal LinearLayout like this:
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:gravity="center_horizontal"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="i am centered"
android:ellipsize="end"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="another widget"/>
.
.
.
</LinearLayout>
The TextView with width 0dp and weight 1 will use the remaining horizontal space.
You can add additional widgets to the LinearLayout, and the TextView will always take the remaining space.
For example, if you change the Visibility of the Button to GONE, you'll see the TextView will expand to use the whole width. Similarly, if you programmatically add new widgets to the LinearLayout, the available space for the TextView will adjust.
You can further add ellipsize options to control what happens when the text does not fit in the TextView size.

View without Activity - Placement and Layout

I am using the Code from JawsWare - Overlay View to create a View that lays above everything.
This works fine, but I failed to adapt the given layout to my needs.
What I want: Two Phases.
First Phase: A button in the upper left corner that is always there above all other apps.
When this button is pressed, we enter the Second Phase:
The button is replaced by two images which are to be horizontally aligned at a certain distance from the top, again floating above everything else.
I have to admit that I dont really understand Android Layouting, but I've tried fiddling with a RelativeLayout and an ImageView but it seems buggy.
Additionally, I encountered a very strange behaviour: Even if the Image is only about 200x200 px in the upper left corner, nearly the whole screen is "blocked" by the View (it receives all TouchEvents, even though there shouldt be anything). It seems that if I position the Layout using Margins to float in the middle of the screen, everything to the left and top of the visible button is also receiving touch events and not letting them pass through to the underlying app (even though there is no visible content).
Any ideas to why this happens and how to circumvent that?
Secondly: How can I achieve the two layouts from earlier?
Edit 1: My Layout. (please keep in mind that I just copied this and then did what I thought was right)
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="0dp"
android:onClick="overlayTextClicked"
android:padding="0dp"
android:id="#+id/overlay_layout_id"
>
<ImageView
android:id="#+id/imageview_info"
android:layout_width="200px"
android:layout_height="200px"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:scaleType="fitXY"
android:src="#drawable/picture_010512233437384578"
android:contentDescription=""/>
<TextView
android:id="#+id/textview_info"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:text="info"
android:textColor="#FFF"
android:orientation="vertical"
android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
Afterwards I am trying to set the RelativeLayouts Gravity to TOP | CENTER_VERTICAL with a Top-Margin of 200px.
I believe what's going on here is your TextView is filling out the entire screen.
wrap_content bounds the size of a View with its content. match_parent fills a view to as big as it can get (i.e., whatever size the container is bound to).
So in this case, your RelativeLayout is does not have a max size it's bound to. Your TextView is going to try to get as big as it can get, so it's going to fill the screen. Likewise, the RelativeLayout is going to blow up to that size to wrap around the TextView.
Also, RelativeLayout doesn't really respond to Gravity well. That is used in LinearLayout and FrameLayout containers a lot, but RelativeLayout relational rules like "CENTER_IN_PARENT" are going to override whatever Gravity you set (if you set Gravity.RIGHT and "CENTER_IN_PARENT", then one has to win out I guess).

Why does edittext with empty text still have some height?

I researched the attribute value wrap_content on another thread and found its function to be to " force [the view] to expand only far enough to contain the values (or child controls) it contains. For controls -- like text boxes (TextView) or images (ImageView) -- this will wrap the text or image being shown."(What's the difference between fill_parent and wrap_content?)
Heres my code(for empty edittext
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Email"
android:padding="5dp"/>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
And the resulting layout(selected one is the edit text) http://imgur.com/6H8caKT
My question is that because the edit text contains nothing(empty text), shouldnt wrap_ content force it to have no height?
wrap_content should be different ,once you make widgets by default wrap_content ,it will adjust the widget size automatically for all the screen such as small,medium and big screens.
But you have mention dp wise, all screen follows dp values only but it will not adjust automatically.
fill_parent and match_parent will stretch with their parent layout
I believe this is another Android sdk "Feature Bug". There is android:minLines=".." and it works but setting 0 is ignored for unknown reasons to me so it always stay as 1.

Fullscreen App, Layout shifted down by height of notification bar

I have an App that has a toolbar at the bottom of the screen and the rest is filled with a custom View (see xml below). Now when I make the App full screen (I tried all possibilities, programmatically and via Manifest.xml), when it's started the whole layout seems to be shifted down by about the height of the notification bar. The buttons in the toolbar are only visible half-way. Sometimes, all of it moves up after a few seconds, or when I click a button in the toolbar.
I'm pretty sure, that it's a problem with my custom view, because I do not get this effect if I replace it with a Button or the like. I guess it must have something to do with the onMeasure method. I don't really know how to implement it, my version is shown below. The custom view is used for drawing inside, so basically it wants to be as large as possible.
Any help would be much appreciated. I searched for several hours already, but no clue yet.
layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<com.example.MyCanvasView
android:id="#+id/canvas"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
/>
<!-- Buttonbar -->
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingTop="4dp"
android:orientation="horizontal"
android:background="#android:drawable/bottom_bar"
>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="1"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="2"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="3"
/>
</LinearLayout>
</LinearLayout>
And this is my onMeasure method:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(width, height);
}
You're not taking the mode into account when you're setting your measurement.
The mode of a MeasureSpec can be one of MeasureSpec.EXACTLY, MeasureSpec.AT_MOST, or MeasureSpec.UNSPECIFIED. Simply accepting the size component and setting your measured size to that is appropriate for EXACTLY, but it isn't often the right thing for the others.
Because you're trying to use layout_weight in addition to a height of wrap_content on this custom view, the following is happening:
Your custom view is the first child of the LinearLayout with a height of wrap_content so LinearLayout measures it. Since LinearLayout has been told by the LayoutParams that it should wrap_content, it measures your custom view with a MeasureSpec mode of AT_MOST and a size of the entire available space.
But your custom view is greedy. It decides to take all of the space available. In essence, you have implemented your measurement to treat wrap_content as match_parent.
Now there's no more space left. The lower button bar gets measured accordingly but it's not done yet, there's a child with weight. In a LinearLayout any space left over after all normal measurement is complete is divided among the weighted children according to their weight values. This isn't the behavior you want.
When you use weight to fill available vertical space like you're doing in this layout, you normally want to set the layout_height to 0dip. This will make LinearLayout skip the first measure pass on that child and only use the weighted measurement pass to measure your view, giving it the remaining available space.
I found the reason for the described behaviour. I had set the view to be focusable in touchmode via setFocusableInTouchMode(true) in the onCreate() method. As soon as I removed this, it works fine. Thanks to adamp though -- your description of what goes on during layout and measuring was very interesting.
But that leaves me with the problem that I do not receive any key/button clicks any more :-(

How to create layout like this?

I have a horizontal LinearLayout, inside which I have 2 TextViews. Let's say that the LinearLayout's width is 320px. If the TextViews don't fit into the LinearLayout (they are together wider than 320px), I want to somehow achieve this:
The second TextView is fully displayed and is at the right edge of the LinearLayout
The first TextView is only shown partially, only first x characters are visible
What I mean:
[TextView1|TextView2_________________________] // this is normal
[VeryVeryL...|VeryVeryLongTextView2] // VeryVeryLongTextView1 is not fully visible
To get the effect you're requesting in the comments above, you could modify Mayra's solution to something like:
<LinearLayout ...>
<TextView android:layout_width="wrap_content"
android:maxWidth="20dp"
android:layout_height="wrap_content"/>
<TextView android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"/>
I think that will work. Weirdly, the maxWidth param is only present on a couple view classes, but TextView luckily is one of them. You'd think it'd be useful in more cases, so I'm not sure why it's not just available in the default view params.
Specify a specific width for your first textView (i.e, 20dp... note, it is better to use dp than hard coded pixels, to deal with multiple resolutions of devices), give your 2nd TextView a weight of 1. This tells it to take up the remaining space. For example:
<LinearLayout ...>
<TextView android:layout_width="20dp"
android:layout_height="wrap_content"/>
<TextView android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"/>
</LinearLayout>

Categories

Resources