Set Fragment height programmatically according to existing elements - android

Pre:
I am extending the relativelayout class and inflating the below reduced xml sample
I am adding the fragment programmatically in the container layout "fragment_container"
What I want to achieve is to add a fragment in a layout-container (in this case fragment_container) where the height is already ruled by the three textviews
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<TextView
android:layout_marginTop="10dp"
android:id="#+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="New Text 1" />
<TextView
android:id="#+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/textView"
android:text="New Text 2" />
<TextView
android:layout_marginBottom="10dp"
android:id="#+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/textView2"
android:text="New Text 3" />
</RelativeLayout>
To set the height of the fragment programmatically I am doing this:
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (!hasFocus) return;
ViewGroup.LayoutParams params = fragment.getView().getLayoutParams();
params.height = getMeasuredHeight();
params.width = getMeasuredWidth();
fragment.getView().setLayoutParams(params);
}
This solution works sometimes (sometimes getMeasuredHeight returns 0 && getMeasuredWidth returns 0), so it's not the correct solution.
Is this a problem that can be solved through the use of the xml only?

As I see you are stacking the views vertically, isn't it better to use LinearLayout with a vertical orientation, and give each view a proper weight (0 for height, and proper value for weight)?

Related

How can I place two views depending on their width?

In our project we have such a case: we have two textviews (let's say, #id/text_view_1 and #id/text_view_2). We should place them horizontally (#id/text_view_1 and then #id/text_view_2) if their width combined is less than the width of their parent or vertically (text_view_2 above text_view_1) if they are too wide.
Right now the best solution I've come up with looks something like this:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/parent"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/text_view_above_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true" />
<TextView
android:id="#+id/text_view_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="#+id/text_view_2_right"
android:layout_alignBottom="#+id/text_view_2_right"
android:layout_alignParentStart="true" />
<TextView
android:id="#+id/text_view_right_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/text_view_2_above"
android:layout_toEndOf="#+id/text_view_1" />
</RelativeLayout>
Here is the logic of toggling visibility of text_views
private void toggleVisibility() {
TextView textView1 = (TextView) findViewById(R.id.text_view_1);
TextView textViewAbove2 = (TextView) findViewById(R.id.text_view_above_2);
TextView textViewRight2 = (TextView) findViewById(R.id.text_view_right_2);
textView1.measure(0, 0);
textViewAbove2.measure(0, 0);
textViewRight2.measure(0, 0);
View parent = findViewById(R.id.parent);
parent.measure(0, 0);
if (textView1.getMeasuredWidth() + textViewRight2.getMeasuredWidth() < parent.getMeasuredWidth()) {
textViewAbove2.setVisibility(View.GONE);
textViewRight2.setVisibility(View.VISIBLE);
} else {
textViewRight2.setVisibility(View.GONE);
textViewAbove2.setVisibility(View.VISIBLE);
}
}
Is there a solution more "beautiful" and shorter than the one I've described? I guess there is a way to do it with ConstraintLayout instead of RelativeLayout but I'm not sure.
EDIT 1: probably I have to provide the result I want to see. Here is what an activity supposed to look like if both views are short:
And here is what it should look like if views are too long:
Take a look at FlexboxLayout.
Here is a solution using FlexboxLayout:
<com.google.android.flexbox.FlexboxLayout
android:id="#+id/parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:flexWrap="wrap">
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:text="This is a short string." />
<TextView
android:id="#+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:text="This is another short string." />
</com.google.android.flexbox.FlexboxLayout>
Using the same XML with a longer string for the first text view yields the following:
Solution:If you want to set TextView as per their width requirements then you will simply use LinearLayout as parent with width wrap_content and for both child TextViews also give width 'wrap_content'
try using wrap_content and put these child text views inside a parent LinearLayout , give wrap_content as width for both of the child textviews. It will place according to the content in those textviews.
If You want to put Views Horizontally --
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/parent"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:id="#+id/text_view1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<TextView
android:id="#+id/text_view2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</LinearLayout>
If You want to put Views Vertically --
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="#+id/text_view1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<TextView
android:id="#+id/text_view2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</LinearLayout>
Try using percentagelayout . This might help. For more details provide desired output.

Adapt child's width to parent's width

I'm facing an issue with an xml layout.
This is the full xml:
<FrameLayout
android:id="#+id/root"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="match_parent">
<ImageView
android:id="#+id/image"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:adjustViewBounds="true"
tools:src="#drawable/segment_cultura"/>
<LinearLayout
android:id="#+id/message_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="#color/black_divider"
android:padding="4dp">
<TextView
android:id="#+id/message"
style="#style/TextAppearance.AppCompat.Caption"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:ellipsize="end"
android:maxLines="2"
android:textColor="#color/white_secondary_text"
/>
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="end"
android:src="#drawable/ic_message"/>
</LinearLayout>
<ImageView
android:id="#+id/cancel"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="top|end"
android:layout_margin="4dp"
android:src="#drawable/ic_close"/>
</FrameLayout>
The goal: Use this as an item in a fixed height horizontal RecyclerView, using ONLY the image to determine the size of the item. Other views, such message_container or cancel will need to adapt to this size.
The problem: message_container needs to fill the width of the item, but it shouldn't modify the item width when there is a long text in message. It should go to the 2nd line and then get ellipsized. What happens instead is message never goes to the second line and makes the parents (message_container and root) enlarge to fit its text.
I'm looking for a solution that only involves xml, if I can't find it a custom view is preferred to some logic in the adapter.
Thank you for your time.
I solved this by using:
image.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
if (root != null && image.getWidth() != 0)
root.getLayoutParams().width = image.getWidth();
});

Align Android seekbar with thumb

I'm trying to align the seekbar to the top of a view, but i can't center it with the thumb.
Is there some kind of "alignCenter" with the RelativeLayout children.
Here's a sample of my xml code :
<RelativeLayout
android:id="#+id/rl_fragment_audio_player"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="#color/black_transparent"
android:padding="5dp"
android:visibility="gone" >
<TextView
android:id="#+id/tv_fragment_audio_begin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:textColor="#color/white" />
<TextView
android:id="#+id/tv_fragment_audio_end"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:textColor="#color/white" />
<Button
android:id="#+id/btn_fragment_audio_play"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_centerInParent="true"
android:background="#drawable/btn_play"
android:visibility="gone" />
</RelativeLayout>
<SeekBar
android:id="#+id/sb_fragment_audio"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:layout_above="#id/rl_fragment_audio_player" />
For example, Google Play Music does it.
As you said the height of your seekbar might vary, unless you specify it different. Here is how you can make sure it fits all, always.
android:layout_above="#id/rl_fragment_audio_player"
is setting your seekbar bottom above rl_fragment_audio_player top. There is no direct method to specify centerAlignWith but I would remove the space it occupies within it's parent. To know the height of the seekbar you must wait until the global layout has changed. This code should be placed inside your onCreateView
sb = (SeekBar) rootView.findViewById(R.id.sb_fragment_audio);
sb.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener(){
#Override
public void onGlobalLayout() {
sb.getViewTreeObserver().removeOnGlobalLayoutListener(this);
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) sb.getLayoutParams();
params.setMargins(0, - sb.getHeight() / 2, 0, - sb.getHeight() / 2);
sb.setLayoutParams(params);
// I suggest you set the seekbar visible after this so that it won't jump
}
});
The last view in the xml will be in the front. Therefore make sure your seekbar is added after the the other views. Like this
<RelativeLayout
android:id="#+id/rl_fragment_audio_player"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true" >
// your bottom panel
</RelativeLayout>
<View
android:id="#+id/image_above"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="#id/rl_fragment_audio_player" />
<SeekBar
android:id="#+id/sb_fragment_audio"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#id/image_above" />
Have you tried adding a negative margin to the seekbar?
<SeekBar
android:id="#+id/sb_fragment_audio"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:layout_marginBottom="-15dp"
android:layout_above="#id/rl_fragment_audio_player" />

Text being truncated in TextView with width set to wrap_content

I'm trying to create a simple component in my layout, where there are two TextViews horizontally next to each other. The one on the right should start where the one on the left finishes. My code for this is as follows:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<TextView
android:id="#+id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:singleLine="true"
android:textColor="#android:color/white"
android:textIsSelectable="false"
android:textSize="25sp" />
<TextView
android:id="#+id/text2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:textColor="#android:color/white"
android:textSize="20sp" />
</LinearLayout>
I programmatically set the text on each TextView after the view has rendered. However, sometimes the text does not display correctly in the first TextView- I can see that the width has been set correctly, as the second TextView is not next to it, but the text is truncated rather than using the space. If I lock/unlock the device to refresh the screen then the text displays correctly (without the widths of the TextViews changing).
I've tried changing this to use a RelativeLayout, but I see the same issue.
Any ideas?
Although i dont understant what exactly you mean, would suggest you to use weightSum property in the parent view and android:layout_weight in child views. The same allows to put many child views inside a parent view with respect to ratio (like navigation tabs).
for eg :
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="1" >
<TextView
android:id="#+id/text1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ellipsize="end"
android:singleLine="true"
android:textColor="#android:color/white"
android:textIsSelectable="false"
android:textSize="25sp"
android:layout_weight="0.4" /> //60% width
<TextView
android:id="#+id/text2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:textColor="#android:color/white"
android:textSize="20sp"
android:layout_weight="0.6" /> //40% width
</LinearLayout>
also, dont forget to put the width if child views as 0dp. as that will result in ignoring the calculations regarding the width of view. or you can set the width of child view as "match_parent" as well. any other property to width will not work. (and if you want half matchparent for both child views set layout_width to 0.5 both views.. ithink thats obvious to note)
Hopw it helps.

Space (View) is not working in ListItem Layout

I've got a ListActivity with a custom Adapter. This adapter uses the following list_item.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/item_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="#+id/ll1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center">
<ImageView
android:id="#+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="#dimen/default_margin"
android:contentDescription="#string/checkbox_content_description"
android:src="#drawable/checkbox_unchecked"
android:background="#layout/transparent_background"
android:focusableInTouchMode="false"
android:adjustViewBounds="true"
android:clickable="false"
android:focusable="false" />
<TextView
android:id="#+id/tv_amount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="#dimen/default_margin"
android:layout_marginTop="#dimen/default_margin"
android:focusableInTouchMode="false"
android:clickable="false"
android:focusable="false"
android:textIsSelectable="false" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_vertical"
android:layout_marginRight="#dimen/default_margin"
android:layout_marginTop="#dimen/default_margin">
<TextView
android:id="#+id/tv_product_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:ellipsize="end"
android:focusableInTouchMode="false"
android:clickable="false"
android:focusable="false"
android:textIsSelectable="false" />
</LinearLayout>
<TextView
android:id="#+id/tv_price"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="#dimen/default_margin"
android:layout_marginTop="#dimen/default_margin"
android:focusableInTouchMode="false"
android:clickable="false"
android:focusable="false"
android:textIsSelectable="false" />
</LinearLayout>
<LinearLayout
android:id="#+id/ll2"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center"
android:layout_below="#id/ll1">
<Space
android:id="#+id/filler_space"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="#dimen/default_margin"
android:layout_marginRight="#dimen/default_margin"
android:layout_marginBottom="#dimen/default_margin"
android:focusableInTouchMode="false"
android:clickable="false"
android:focusable="false" />
<EditText
android:id="#+id/et_amount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="#dimen/default_margin"
android:layout_marginBottom="#dimen/default_margin"
android:inputType="number" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_vertical"
android:layout_marginRight="#dimen/default_margin"
android:layout_marginBottom="#dimen/default_margin"
android:padding="0dp">
<AutoCompleteTextView
android:id="#+id/actv_search_product"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:singleLine="true"
android:ellipsize="end"
android:inputType="text" />
</LinearLayout>
<EditText
android:id="#+id/et_price"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="#dimen/default_margin"
android:layout_marginBottom="#dimen/default_margin"
android:inputType="numberDecimal"
android:digits="0123456789.,-" />
</LinearLayout>
</RelativeLayout>
In the Adapter's getView-method I've added the following piece of code:
// Change the width of the Filler-Space to match the Image
// and leave the height as is
Space space = (Space) view.findViewById(R.id.filler_space);
space.setLayoutParams(new LinearLayout.LayoutParams(
imageView.getMeasuredWidth(), space.getMeasuredHeight()));
This gives the following result at first:
When I change the Visibility of my second LinearLayout (ll2) to VISIBLE, it gives the following result:
What I want instead is:
As it seems the Space-View width isn't changed at all.. While I know for fact that the getView methods are successfully called thanks to Log-messages and other things I do in the getView method.
Does anyone know what I did wrong?
PS: When I use View instead of Space I have the same result. Never used Space before, so I thought it might have to do something with that.
Solution thanks to Demand's option 4:
// Since we can't access Measured widths and heights before the View is completely loaded,
// we set up an Observer that will be called once the ListItem's layout is ready
ViewTreeObserver observer = view.getViewTreeObserver();
if(observer.isAlive()){
// In order to access the view in the onGlobalLayout, it needs to be final, so we create a final copy here
final View v = view;
observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
// This will be called once the layout is finished, prior to displaying it
#Override
public void onGlobalLayout() {
ImageView imageView = (ImageView) v.findViewById(R.id.image);
Space space = (Space) v.findViewById(R.id.filler_space);
// Change the width of the Filler-Space to match the Image and leave the height as is
space.setLayoutParams(new LinearLayout.LayoutParams(imageView.getMeasuredWidth(), space.getMeasuredHeight()));
}
});
}
You set width and height if your space to wrap_content. It's mean that space will have width as their children, but there is no children and you have width = 0. It's not about space, it's about layout measuring in android.
When you call your code:
Space space = (Space) view.findViewById(R.id.filler_space);
space.setLayoutParams(new LinearLayout.LayoutParams(
imageView.getMeasuredWidth(), space.getMeasuredHeight()));
Your imageView havn't measured yet and return width = 0. It will measured later before drawing. In getView() in adapter you only create views, but measuring, layout and drawing will be later.
You have several ways to fix it:
set width of your space in dp, instead of wrap_content.
using relative layout instead of three linear layouts.
Use TableLayout.
add GlobalTreeObserver and getMeasuredWidth() at right time.
Post your runnable to view's handler to get width after drawing.
I think the best ways is 2 and 3, because 4 and 5 will cause measuring several times.

Categories

Resources