TLDR version:
When I resize the parent:
Explanation:
in an android application, I have a parent layout and inside it multiple 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 some texts and pictures in it. What I'm doing is the following:
When the user clicks on a button, I want to collapse the parent linear layout (animate width until it is 0). I have implemented this function to do it:
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 am calling the above function on the parent view. This is working correctly, and the parent view is resizing in width until it reaches 0, then I set his visibility as gone.
My Problem
While the parent view is re-sizing (collapsing) the child views are also resizing with it. what I mean is that when the width of the parent reaches one of the child, the child element will also re-size with it and it will cause the textviews in it to re-size too.
Here is some screenshots for an example:
Here you can see the initial layout, there is the parent layout in green, and the child layout (with the date and text and icon).
Then when I start to reduce the size of the parent layout, the child layout size will also be affected and it will reduce the size of the textview in it, causing the words to wrap as seen in the following 2 images:
As you notice the text in the child is being wrapped as the width of the parent is reduced.
Is there a way I can have the child element to not resize with the parent view, but remain as it is, even if the parent view size is reducing? I need the size of the chil element to stay fixed during the whole resizing animation nad get cut by the parent instead of resizing with it.
Thank you very much for any help
Your problem comes from several issues.
singleLine="false"
parameter on your TextViews
>
With this parameter, you can't ask it to wrap its content, since the text can use multiple lines and thus don't have a proper width. If there are more than one line it will fill its parent.The second issue can be solved by using fixed size but its not the case with the first point :
You could set singleLine="true"
, with android:width="wrap_content"
to see it working on a single line. (set android:ellipsize="none"
in order to hide the '...' moving around)
But a single line text isn't what you need right ?
I've made tests with a extend of TextView
in order to avoid those views from resizing, 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 extend 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>
That's not really beautiful and I don't advise to use it too much since it could break android's layout mecanism. But in your particular case, I guess it does the job.
Logic :
When you extend a view to create a custom one, you are responsible for the implementation of onMeasure
. This is what give its size to your view. So that's how it works : you compute once the size ( or let the default function do it for you thanx to getMeasuredW/H
) and save it, then on each request for size, you just return the same values so your view size won't change.
Limitations :
Sine your view size won't change, it can have a bad behaviour when changing screen orientation / parent size or again, when the text inside change (As Vikram said). That's what I called "break android layout mecanism". If you need to change the text dynamically, you would have to extend the setText method to allow it to resize at this point ...
<Edit>
to support the requirements (see comments):
From what I can tell, the following is what you're after. One change that is needed to support your requirements is the use of FrameLayout
as the parent container.
The layout now is:
<?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, this is quite straightforward in fact. 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 app's minSdkVersion
is lower than 11, use NineOldAndroids library to support > api 8.
I am using 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 viewgroup that is being animated is rlToAnimate
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With