Android size layout resizing parent without resizing child elements

TL Version; DR:

When I resize the parent:

enter image description here

Explanation:

in an Android application, I have a parent layout and inside it are several relative layouts.

<LinearLayout android:id="@+id/parent" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center_vertical" android:orientation="horizontal" > <RelativeLayout android:id="@+id/child1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@color/transparent"> </RelativeLayout> <RelativeLayout android:id="@+id/child2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@color/transparent"> </RelativeLayout> </LinearLayout> 

The children of this layout have several texts and pictures. I do the following:

When the user clicks on the button, I want to collapse the parent linear layout (animate the width to 0). I executed this function for this:

 public static void collapseHorizontally(final View v) { final int initialWidth = v.getMeasuredWidth(); Animation a = new Animation() { @Override protected void applyTransformation(float interpolatedTime, Transformation t) { if(interpolatedTime == 1){ v.setVisibility(View.GONE); }else{ v.getLayoutParams().width = initialWidth - (int)(initialWidth * interpolatedTime); v.requestLayout(); } } @Override public boolean willChangeBounds() { return true; } }; // 1dp/ms long speed = (int)(initialWidth / v.getContext().getResources().getDisplayMetrics().density); a.setDuration(speed); v.startAnimation(a); } 

I call the above function in the parent view. This works correctly, and the parent view resizes in width until it reaches 0, after which I set its visibility to be gone.

My problem

While the parent view redraws (collapses), child views also change its size. I mean, when the width of the parent reaches one of the children, the child will also resize with it, and this will also lead to a resizing of the text views.

Here are some screenshots for an example:

enter image description here

Here you can see the original layout, there is a parent layout in green and a child’s layout (with date and text and icon).

Then, when I start reducing the size of the parent layout, the size of the layout of the child will also be affected, and it will reduce the size of the text image in it, causing the words to wrap, as shown in the following two images:

enter image description hereenter image description here

As you noticed, the text in the child case wraps as the width of the parent decreases.

Is there a way that the child does not change according to the parent view, but remains as it is, even if the size of the parent view is reduced? I need the chil element size to stay fixed during the resize animation, and then decrease by the parent instead of resizing.

Thanks so much for any help

+6
source share
4 answers

Your problem is related to several issues.

  • first : the singleLine="false" parameter on your TextViews > With this parameter you cannot ask him to wrap its contents, since the text can use multiple lines and therefore do not have the proper width. If there are multiple lines, it will populate the parent element.
  • second one . The behavior of "wrap_content" must match the size of the content if its size is larger than the parent. in this case, it will again match the size of the parent. (we see that of capital or marginal movements, than borders, correspond to the size of the parent.)

The second problem can be solved using a fixed size, but this does not apply to the first point:

You can set singleLine="true" , with android:width="wrap_content" , to see how it works on one line. (set android:ellipsize="none" to hide "...") But is single line text not what you need?

I ran tests with the TextView extension to avoid resizing these views even with multiple lines:

 public class NoResizeTextView extends TextView { int firstWidth = -1; int firstHeight = -1; public NoResizeTextView(Context context, AttributeSet attrs) { super(context, attrs); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); Layout layout = getLayout(); if (layout != null) { if (firstWidth == -1) firstWidth = getMeasuredWidth(); if (firstHeight == -1) firstHeight = getMeasuredHeight(); setMeasuredDimension(firstWidth, firstHeight); } } } 

Then use this TextView extension in your layout:

  <com.guian.collapsetest.NoResizeTextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:ellipsize="none" android:singleLine="false" android:text="Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit" > </com.guian.collapsetest.NoResizeTextView> 

It is not very beautiful, and I do not recommend using it too much, since it can break the mecanism of the Android layout. But in your particular case, I assume this does the job.

Logics:

When you expand the view to create a custom one, you are responsible for implementing onMeasure . This is what lends its size to your look. So, how it works: you calculate the size once (or let the default function do it for you thanx before getMeasuredW/H ) and save it, and then with each size request you just return the same values ​​so your view size wins' t.

Limitations:

Sine, the size of your view will not change, it may have bad behavior when changing the screen orientation / parent size or again when the text inside changes (as Vikram said). This is what I called "break mecanism Android layout". If you need to dynamically change the text, you will have to extend the setText method so that it can resize at that moment ...

+8
source

<Edit> to support requirements (see comments):

From what I can say, the following is what you need. One of the changes you need to support your requirements is to use FrameLayout as the parent container.

enter image description here

Now the layout:

 <?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/fl" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/white" > <RelativeLayout android:id="@+id/rl1" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/holo_blue_bright" > <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." android:textColor="@android:color/black" android:layout_marginTop="100dp" /> </RelativeLayout> <RelativeLayout android:id="@+id/rl2" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/holo_blue_dark" > <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." android:textColor="@android:color/black" android:layout_alignParentBottom="true" android:layout_marginBottom="200dp" /> </RelativeLayout> <Button android:id="@+id/bClickToAnimate" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Click to Resize" /> </FrameLayout> 

Java Code:

 FrameLayout fl; RelativeLayout rl1; RelativeLayout rl2; Button b; boolean isOverlapping; int toTranslate; @Override protected void onCreate(Bundle savedInstanceState) { .... rl1.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() { @Override public void onGlobalLayout() { rl2.getViewTreeObserver().removeOnGlobalLayoutListener(this); // Get width of the viewgroup int width = rl1.getWidth(); // We will keep the second viewgroup outside bounds - we need // FrameLayout for this FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) rl2.getLayoutParams(); lp.leftMargin = width; lp.width = width; rl2.setLayoutParams(lp); // Amount to translate toTranslate = width; } }); b.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // Viewgroup `rl2` is currently not overlapping // Translate `rl2` over `rl1` if (!isOverlapping) { ObjectAnimator oa = ObjectAnimator.ofFloat(rl2, "translationX", 0, -toTranslate); oa.setDuration(2000L); oa.setInterpolator(new AccelerateInterpolator(2.0f)); oa.start(); } else { // Viewgroup `rl2` is currently overlapping // Translate `rl2` off `rl1` ObjectAnimator oa = ObjectAnimator.ofFloat(rl2, "translationX", -toTranslate, 0); oa.setDuration(2000L); oa.setInterpolator(new AccelerateInterpolator(2.0f)); oa.start(); } } }); } 

</Edit>

Well, actually it's pretty simple. Here, take a look:

All you need is the following:

 // Animate property `right` - final width is zero ObjectAnimator oa = ObjectAnimator.ofInt(YOUR_VIEWGROUP, "right", YOUR_VIEWGROUP.getWidth(), 0); // Set animation duration - 5 seconds oa.setDuration(5000L); // Set interpolator oa.setInterpolator(new AccelerateInterpolator(2.0f)); // Start animation oa.start(); 

If your minSdkVersion application minSdkVersion less than 11, use the NineOldAndroids library to support> api 8.

I use a fairly simple layout:

 <?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="match_parent" android:orientation="vertical" android:background="@android:color/white" > <Button android:id="@+id/bClickToAnimate" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Click to Resize" /> <RelativeLayout android:id="@+id/rlToAnimate" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/holo_blue_bright" > <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." android:textColor="@android:color/black" android:layout_centerVertical="true" /> </RelativeLayout> </LinearLayout> 

And the animated viewing group is rlToAnimate .

+1
source

You can use ImageView, which will act as an add-on on the layout, so instead of reducing the layout, you simply close the layout using ImageView with a black background that will slide to the left and after that, you can set each GONE.

  • Have an ImageView with parent attr matching and visibility.
  • When the user clicks the button, set the ImageView visibilty to visible and animated to shift it to the left.
  • OnAnimationEnd will configure the view on ImageView and other layouts or resize layouts that you need.
0
source

In the past, I considered the same requirements as I decided, I believe that this is the easiest and most flexible way to implement it. This is tested and works with your xml and your animation code. Two simple steps should do the trick:

  • Surrounding the parent layout with FrameLayout - this will be the layout that you are actually modifying.
  • Before doing any actual resizing, we need to force the R.id.parent template to maintain a constant size by giving it a fixed size in pixels. At runtime, after passing the layout, we update layoutParams to set a fixed size. We do this after passing the layout, so we know that the fixed size that we set is based on system calculations of the required size (i.e., after all calculations of the layout are parent / wrap content).

Here's what your layout looks like (just added FrameLayout):

 <FrameLayout android:id="@+id/parentContainer" android:layout_width="match_parent" android:layout_height="match_parent" > <LinearLayout android:id="@+id/parent" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#0f0" android:gravity="center_vertical" android:orientation="horizontal" > <RelativeLayout android:id="@+id/child1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@color/transparent"> </RelativeLayout> <RelativeLayout android:id="@+id/child2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@color/transparent"> </RelativeLayout> </LinearLayout> 

Now you need to add a few lines of code to the onCreate method of activity (or elsewhere, if that makes more sense depending on your code. But probably onCreate):

 final View fixedSizeLayout = findViewById(R.id.parent); // Here we take the requested calculated size of the layout (as calculated by the layout pass) // and apply it as a fixed size to the layout fixedSizeLayout.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() { @Override public void onGlobalLayout() { if (fixedSizeLayout.getWidth() > 0) { LayoutParams params = fixedSizeLayout.getLayoutParams(); // this will be different on the first time because the layout width will be a constant (for match_parent) and not actual size if (params.width != fixedSizeLayout.getWidth()) { // Setting a fixed size in px params.width = fixedSizeLayout.getWidth(); fixedSizeLayout.setLayoutParams(params); fixedSizeLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this); } } } }); 

Note that the view you are changing is FrameLayout (R.id.parentContainer). Although this representation decreases with animation, the R.id.parent layout will maintain its constant size.

0
source

Source: https://habr.com/ru/post/969831/


All Articles