In my main layout file, I have a RelativeLayout, with a weight of 1 (basically to display a map) above a LinearLayout with a weight of 2, declared this way :
<LinearLayout
android:id="#+id/GlobalLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<RelativeLayout
android:id="#+id/UpLayout"
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_weight="1" >
</RelativeLayout>
<LinearLayout
android:id="#+id/DownLayout"
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_weight="2"
android:orientation="vertical" >
</LinearLayout>
</LinearLayout>
DownLayout contains a list of items, when I click on an item, I would like to change the weight of DownLayout for 4, so the upper layout (the map) takes only 1/5 of the screen instead of 1/3.
I have managed to do it by changing the LayoutParams :
LinearLayout linearLayout = (LinearLayout) mActivity.findViewById(R.id.DownLayout);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
params.weight = 4.0f;
linearLayout.setLayoutParams(params);
It works but I'm not satisfied, the change is too immediate, there is no transition while I would like it to be smooth. Is there a way to use animation for that ?
I found some examples with ObjectAnimator to change the weightSum, but it does not do want I want (if I change only this property, I have some free space below my down layout) :
float ws = mLinearLayout.getWeightSum();
ObjectAnimator anim = ObjectAnimator.ofFloat(mLinearLayout, "weightSum", ws, 5.0f);
anim.setDuration(3000);
anim.addUpdateListener(this);
anim.start();
Is there a way to use ObjectAnimator (or something else) to do that ?
Thanks !
I recently came across a similar problem and solved it using a standard Animation (I have to target API 10 so couldn't use ObjectAnimator). I used a combination of the answer here with slight alterations to take into account weight instead of height.
My custom animation class looks as follows...
private class ExpandAnimation extends Animation {
private final float mStartWeight;
private final float mDeltaWeight;
public ExpandAnimation(float startWeight, float endWeight) {
mStartWeight = startWeight;
mDeltaWeight = endWeight - startWeight;
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mContent.getLayoutParams();
lp.weight = (mStartWeight + (mDeltaWeight * interpolatedTime));
mContent.setLayoutParams(lp);
}
#Override
public boolean willChangeBounds() {
return true;
}
}
And its called by this method...
public void toggle() {
Animation a;
if (mExpanded) {
a = new ExpandAnimation(mExpandedWeight, mCollapsedWeight);
mListener.onCollapse(mContent);
} else {
a = new ExpandAnimation(mCollapsedWeight, mExpandedWeight);
mListener.onExpand(mContent);
}
a.setDuration(mAnimationDuration);
mContent.startAnimation(a);
mExpanded = !mExpanded;
}
Hopefully this will help you out, if you need more details or have questions about something let me know.
Related
I am making a productivity app with events that are represented as buttons holding basic information about the event.
The button is contained in a RelativeLayout alongside TextViews, an options menu and a switch. The parent layout's height is set to WRAP_CONTENT. It looks like this:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<Button
android:id="#+id/event_button"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignBottom="#+id/relativeLayout"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="2dp"
android:layout_marginEnd="10dp"
android:layout_marginStart="10dp"
android:minHeight="130dp"
android:elevation="0dp"
android:textSize="#dimen/event_item_title_fontSize"
android:visibility="visible"
/>
<include
android:id="#+id/relativeLayout"
layout="#layout/_event_layout_content"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</include> </RelativeLayout>
You can click the button to expand it and show additional information. The expand animation is achieved using a custom animation which updates the button's getLayoutParams().height and calls getParent().requestLayout() every time applyTransformation(...) is called. This works well on an API level 19 (KitKat) device.
Animation code:
//The current state of the button which gets updated on animation finish
boolean expanded = false;
#Override
public void onClick(final View v) {
//Getting references to views
final RelativeLayout layout = (RelativeLayout) button.getParent();
final View separator = layout.findViewById(R.id.separator), list =
layout.findViewById(R.id.notes_list);
final Button button = (Button) v;
final RelativeLayout content = (RelativeLayout) layout.findViewById(R.id.relativeLayout);
final RelativeLayout.LayoutParams lpl = (RelativeLayout.LayoutParams) list.getLayoutParams(),
lpsw = (RelativeLayout.LayoutParams) layout.findViewById(R.id.event_switch).getLayoutParams(),
lp = (RelativeLayout.LayoutParams) button.getLayoutParams();
//Finished getting references to views
final int startHeight = lp.height = button.getHeight();
//Prevents button from automatically resizing to fit its parent when requestLayout is called
lp.removeRule(RelativeLayout.ALIGN_BOTTOM);
layout.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View view, int i, int i1, int i2, int i3, int i4, int i5, int i6, int i7) {
layout.removeOnLayoutChangeListener(this);
final int goalHeight = GetExpectedHeight(expanded);
Animation animation = new Animation() {
float alpha1 = (expanded ? startHeight : goalHeight) - separator.getTop(),
alpha2 = (expanded ? startHeight : goalHeight) - list.getTop();
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
if (interpolatedTime < 1) {
//Updates the button's height
lp.height = (int) (startHeight + (goalHeight - startHeight) * interpolatedTime);
separator.setAlpha((lp.height - separator.getTop()) / alpha1);
list.setAlpha((lp.height - list.getTop()) / alpha2);
list.setScaleX(list.getAlpha());
list.setScaleY(list.getAlpha());
layout.requestLayout();
return;
}
//Animation finish
if (expanded) {
separator.setVisibility(View.GONE);
list.setVisibility(View.GONE);
}
lp.height = lp.MATCH_PARENT;
lp.addRule(RelativeLayout.ALIGN_BOTTOM, R.id.relativeLayout);
//A custom method that basically requests layout for whole activity
instance.get().Invalidate();
expanded = !expanded;
this.cancel();
}
};
animation.setInterpolator(new DecelerateInterpolator());
animation.setDuration(400);
layout.startAnimation(animation);
}
});
float alpha = expanded ? 1 : 0;
separator.setAlpha(alpha);
list.setAlpha(alpha);
//Kicks off the onLayoutChange method above
if (!expanded) {
separator.setVisibility(View.VISIBLE);
list.setVisibility(View.VISIBLE);
}
else
layout.requestLayout();
}
However, on an Android 7.0 Nougat (API 24), there is an accompanying ripple effect which overlays the button with a rectangle that doesn't update its height to match the button's height during the animation. I don't know what it looks like on other Android versions because those are the only two devices I can test on. Is there a way to update the ripple effect's rectangle to scale with the button? I would prefer to keep the ripple effect if at all possible. So far I have tried calling performClick() on the button each frame of the animation but to no avail.
Here is a screenshot of the button during expand animation:
And a video of the animation:
https://streamable.com/vehf3
I need to do two things with a view:
Move top dimension to the very top of the window
Move bottom dimension to the very bottom of the window.
In short, I need the view to cover 100% of the parent view.
Translation animation didn't work because it moves the view but it doesn't increase the size.
Scale animation works but it stretches the content of the view and I don't want that. I want to increase the visible area, not stretch the content to fit the new dimensions.
What's the correct way to do this?
That can be easily achieved with Transitions API.
With Transitions API you do not take care of writing animations, you just tell what you want the end values be and Transitions API would take care of constructing animations.
Having this xml as content view (a view in the center of the screen):
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:id="#+id/view"
android:layout_width="120dp"
android:layout_height="80dp"
android:layout_gravity="center"
android:background="#color/colorAccent" />
</FrameLayout>
In activity:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.item)
val root = findViewById(R.id.root) as ViewGroup
val view = findViewById(R.id.view)
view.setOnClickListener {
// After this line Transitions API would start counting the delta
// and will take care of creating animations for each view in `root`
TransitionManager.beginDelayedTransition(root)
// By default AutoTransition would be applied,
// but you can provide your transition with the second parameter
// val transition = AutoTransition()
// transition.duration = 2000
// TransitionManager.beginDelayedTransition(root, transition)
// We are changing size of the view to match parent
val params = view.layoutParams
params.height = ViewGroup.LayoutParams.MATCH_PARENT
params.width = ViewGroup.LayoutParams.MATCH_PARENT
view.requestLayout()
}
}
Here's the output:
Platform's Transitions API (android.transition.TransitionManager) is available from API 19, but support libraries backport the functionality upto API 14 (android.support.transition.TransitionManager).
I like to keep everything as simple as it can be.
so my suggestion would be using a android Animating Layout Changes
Here is a sample:
activity_main.xml
<?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:animateLayoutChanges="true"
android:animationCache="true">
<TextView
android:id="#+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/app_name"
android:background="#color/colorPrimary"
android:layout_gravity="center" />
</LinearLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
TextView textView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.textView);
}
#Override
protected void onResume() {
super.onResume();
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
View view = getWindow().getDecorView();
int height = getWindow().getDecorView().getHeight();
int width = getWindow().getDecorView().getWidth();
textView.setLayoutParams(new LinearLayout.LayoutParams(width, height));
LayoutTransition layoutTransition = ((ViewGroup) textView.getParent()).getLayoutTransition();
layoutTransition.enableTransitionType(LayoutTransition.CHANGING);
}
}, 2000);
}
}
You can try using ValueAnimator as shown in this answer:
https://stackoverflow.com/a/32835417/3965050
Note: I wanted to write this as a comment, but I don't have the reputation. This should not be considered as a full answer.
animateLayoutChanges="true" in the parent xml
+
.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
does the trick most of the times and it won't stretch the existing child views
Using ConstraintLayout with ConstrainSet should match your need in the most efficient way.
public class MainActivity extends AppCompatActivity {
ConstraintSet mConstraintSet1 = new ConstraintSet(); // create a Constraint Set
ConstraintSet mConstraintSet2 = new ConstraintSet(); // create a Constraint Set
ConstraintLayout mConstraintLayout; // cache the ConstraintLayout
boolean mOld = true;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Context context = this;
mConstraintSet2.clone(context, R.layout.state2); // get constraints from layout
setContentView(R.layout.state1);
mConstraintLayout = (ConstraintLayout) findViewById(R.id.activity_main);
mConstraintSet1.clone(mConstraintLayout); // get constraints from ConstraintSet
}
public void foo(View view) {
TransitionManager.beginDelayedTransition(mConstraintLayout);
if (mOld = !mOld) {
mConstraintSet1.applyTo(mConstraintLayout); // set new constraints
} else {
mConstraintSet2.applyTo(mConstraintLayout); // set new constraints
}
}
}
Source https://developer.android.com/reference/android/support/constraint/ConstraintSet.html
All you need is to define a second layout.xml with your expanded constraints and apply the second ConstraintSet to your view or activity when necessary.
ValueAnimator anim = ValueAnimator.ofInt(viewToIncreaseHeight.getMeasuredHeight(), -100);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
int val = (Integer) valueAnimator.getAnimatedValue();
ViewGroup.LayoutParams layoutParams = viewGroup.getLayoutParams();
layoutParams.height = val;
viewGroup.setLayoutParams(layoutParams);
}
});
anim.setDuration(DURATION);
anim.start();
I have two image views, one on top of the other. Behind imageView displays user's image while the top one is cover image (just face area is fully transparent like following screenshot).
My layout is like this:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/rlContainer">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/ivUserImage"
android:contentDescription="#string/content_description"/>
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/ivCoverImage"
android:contentDescription="#string/content_description"
android:scaleType="fitXY"/>
</RelativeLayout>
I'm using OnSwipeTouchListener class in order to adjust user's shape in transparent (face) area. I have following code in onCreateView() of my fragment:
mrlContainer = (RelativeLayout) view.findViewById(R.id.rlContainer);
mUserImage = (ImageView) view.findViewById(R.id.ivUserImage);
mUserImage.setImageURI(mImgUri);
mCoverImage = (ImageView) view.findViewById(R.id.ivCoverImage);
mCoverImage.setOnTouchListener(new OnSwipeTouchListener(mContext) {
public void onSwipeTop() {
moveImageToTop();
}
public void onSwipeRight() {
moveImageToRight();
}
public void onSwipeLeft() {
moveImageToLeft();
}
public void onSwipeBottom() {
moveImageToBottom();
}
public boolean onTouch(View v, MotionEvent event) {
return getGestureDetector().onTouchEvent(event);
}
});
And my movement methods are these:
private void moveImageToTop() {
LayoutParams layoutParams = (LayoutParams) mUserImage.getLayoutParams();
layoutParams.topMargin -= 20;
mUserImage.setLayoutParams(layoutParams);
}
private void moveImageToBottom() {
LayoutParams layoutParams = (LayoutParams) mUserImage.getLayoutParams();
layoutParams.bottomMargin -= 20;
mUserImage.setLayoutParams(layoutParams);
}
private void moveImageToRight() {
LayoutParams layoutParams = (LayoutParams) mUserImage.getLayoutParams();
layoutParams.rightMargin -= 20;
mUserImage.setLayoutParams(layoutParams);
}
private void moveImageToLeft() {
LayoutParams layoutParams = (LayoutParams) mUserImage.getLayoutParams();
layoutParams.leftMargin -= 20;
mUserImage.setLayoutParams(layoutParams);
}
Now, moveImageToTop() and moveImageToBottom() are working fine when I touch screen and move my finger top or bottom. However, image scales up when I move left or right.
What you think? Where is my mistake? Any suggestion would be appreciated. Thanks
As I know, default image scaleType is FIT_CENTER. You didn't change only position since you set MATCH_PARENT in both axis. Also you change View boundaries. By changing vertical boundaries you didn't change image size inside ImageView if your image is fit in horizontal axis.
If I were you, I will use Animation framework or change position during onLayout change and ask for relayout after every swipe.
I changed xml code of behind image to following code and left/right movement fixed:
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/ivUserImage"
android:contentDescription="#string/content_description"
android:scaleType="center"/>
Anyway, I decided to use PhotoView library by chrisbanes which is very easy to use.
I have two views in a linear layout, I programmatically change their layout_weight property. Is there a way I could animate this change in weight so when the weight is changed views slides towards a new size?
You can simply use ObjectAnimator.
ObjectAnimator anim = ObjectAnimator.ofFloat(
viewToAnimate,
"weight",
startValue,
endValue);
anim.setDuration(2500);
anim.start();
The one problem is that View class has no setWeight() method (which is required by ObjectAnimator). To address this I wrote simple wrapper which helps archive view weight animation.
public class ViewWeightAnimationWrapper {
private View view;
public ViewWeightAnimationWrapper(View view) {
if (view.getLayoutParams() instanceof LinearLayout.LayoutParams) {
this.view = view;
} else {
throw new IllegalArgumentException("The view should have LinearLayout as parent");
}
}
public void setWeight(float weight) {
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) view.getLayoutParams();
params.weight = weight;
view.getParent().requestLayout();
}
public float getWeight() {
return ((LinearLayout.LayoutParams) view.getLayoutParams()).weight;
}
}
Use it in this way:
ViewWeightAnimationWrapper animationWrapper = new ViewWeightAnimationWrapper(view);
ObjectAnimator anim = ObjectAnimator.ofFloat(animationWrapper,
"weight",
animationWrapper.getWeight(),
weight);
anim.setDuration(2500);
anim.start();
I have been looking at this as well. Eventually I solved it by animating the weightsum property of the parent, which works very nice if you have two views in a LinearLayout.
see:
Animating weightSum property using ObjectAnimator
In the example below, if you animate the weightSum from 1.0 to 2.0, Screen 2 will animate nicely into view.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/dual_pane"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal"
android:weightSum="1.0">
<!-- Screen 1 -->
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:background="#ff0000"
android:layout_weight="1">
</LinearLayout>
<!-- Screen 2 -->
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:background="#ff6600"
android:layout_weight="1">
</LinearLayout>
</LinearLayout>
Note: I am not sure that this is the best way, but I tried it and it's working fine
Simply using ValueAnimator
ValueAnimator m1 = ValueAnimator.ofFloat(0.2f, 0.5f); //fromWeight, toWeight
m1.setDuration(400);
m1.setStartDelay(100); //Optional Delay
m1.setInterpolator(new LinearInterpolator());
m1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
((LinearLayout.LayoutParams) viewToAnimate.getLayoutParams()).weight = (float) animation.getAnimatedValue();
viewToAnimate.requestLayout();
}
});
m1.start();
More About ValueAnimator
Another way is to use old Animation class, as described in https://stackoverflow.com/a/20334557/2914140. In this case you can simultaneously change weights of several Views.
private static class ExpandAnimation extends Animation {
private final View[] views;
private final float startWeight;
private final float deltaWeight;
ExpandAnimation(View[] views, float startWeight, float endWeight) {
this.views = views;
this.startWeight = startWeight;
this.deltaWeight = endWeight - startWeight;
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
float weight = startWeight + (deltaWeight * interpolatedTime);
for (View view : views) {
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) view.getLayoutParams();
lp.weight = weight;
view.setLayoutParams(lp);
}
views[0].getParent().requestLayout();
}
#Override
public boolean willChangeBounds() {
return true;
}
}
All of the answers above weren't working for me (they would simply "snap" and not animate), but after I added weight_sum="1" to the parent layout, it started working. Just in case someone else comes up with the same issue.
I have the following layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent">
<FrameLayout android:id="#+id/viewgroup_left"
android:layout_height="match_parent"
android:layout_weight="2"
android:layout_width="0dp">
... children ...
</FrameLayout>
<LinearLayout android:id="#+id/viewgroup_right"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_width="0dp"
android:orientation="vertical">
... children ...
</LinearLayout>
</LinearLayout>
I end up with something like this:
+------------------------+------------+
| | |
| | |
| Left | Right |
| | |
| | |
+------------------------+------------+
When a certain toggle is toggled, I want to animate Left so that its width expands to fill the entire screen. At the same time, I would like to animate the width of Right so that it shrinks to zero. Later, when the toggle is toggled again, I need to restore things to the above state.
I've tried writing my own Animation that calls View.getWidth() but when I animate back to that value (by setting View.getLayoutParams().width) it is wider than when it began. I suspect I'm just doing it wrong. I have also read all the documentation on the Honeycomb animation stuff, but I don't want to translate or scale... I want to animate the layout width property. I can't find an example of this.
What is the correct way to do this?
Since noone helped you yet and my first answer was such a mess I'll try to give you the right answer this time ;-)
Actually I like the idea and I think this is a great visual effect which might be useful for a bunch of people. I would implement an overflow of the right view (I think the shrink looks strange since the text is expanding to the bottom).
But anyway, here's the code which works perfectly fine (you can even toggle while it's animating).
Quick explanation:
You call toggle with a boolean for your direction and this will start a handler animation call loop. This will increase or decrease the weights of both views based on the direction and the past time (for a smooth calculation and animation). The animation call loop will invoke itself as long it hasn't reached the start or end position.
The layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:weightSum="10"
android:id="#+id/slide_layout">
<TextView
android:layout_weight="7"
android:padding="10dip"
android:id="#+id/left"
android:layout_width="0dip"
android:layout_height="fill_parent"></TextView>
<TextView
android:layout_weight="3"
android:padding="10dip"
android:id="#+id/right"
android:layout_width="0dip"
android:layout_height="fill_parent"></TextView>
</LinearLayout>
The activity:
public class TestActivity extends Activity {
private static final int ANIMATION_DURATION = 1000;
private View mSlidingLayout;
private View mLeftView;
private View mRightView;
private boolean mAnimating = false;
private boolean mLeftExpand = true;
private float mLeftStartWeight;
private float mLayoutWeightSum;
private Handler mAnimationHandler = new Handler();
private long mAnimationTime;
private Runnable mAnimationStep = new Runnable() {
#Override
public void run() {
long currentTime = System.currentTimeMillis();
float animationStep = (currentTime - mAnimationTime) * 1f / ANIMATION_DURATION;
float weightOffset = animationStep * (mLayoutWeightSum - mLeftStartWeight);
LinearLayout.LayoutParams leftParams = (LinearLayout.LayoutParams)
mLeftView.getLayoutParams();
LinearLayout.LayoutParams rightParams = (LinearLayout.LayoutParams)
mRightView.getLayoutParams();
leftParams.weight += mLeftExpand ? weightOffset : -weightOffset;
rightParams.weight += mLeftExpand ? -weightOffset : weightOffset;
if (leftParams.weight >= mLayoutWeightSum) {
mAnimating = false;
leftParams.weight = mLayoutWeightSum;
rightParams.weight = 0;
} else if (leftParams.weight <= mLeftStartWeight) {
mAnimating = false;
leftParams.weight = mLeftStartWeight;
rightParams.weight = mLayoutWeightSum - mLeftStartWeight;
}
mSlidingLayout.requestLayout();
mAnimationTime = currentTime;
if (mAnimating) {
mAnimationHandler.postDelayed(mAnimationStep, 30);
}
}
};
private void toggleExpand(boolean expand) {
mLeftExpand = expand;
if (!mAnimating) {
mAnimating = true;
mAnimationTime = System.currentTimeMillis();
mAnimationHandler.postDelayed(mAnimationStep, 30);
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.slide_test);
mLeftView = findViewById(R.id.left);
mRightView = findViewById(R.id.right);
mSlidingLayout = findViewById(R.id.slide_layout);
mLeftStartWeight = ((LinearLayout.LayoutParams)
mLeftView.getLayoutParams()).weight;
mLayoutWeightSum = ((LinearLayout) mSlidingLayout).getWeightSum();
}
}
Just adding my 2 cents here to Knickedi's excellent answer - just in case someone needs it:
If you animate using weights you will end up with issues with clipping/non-clipping on contained views and viewgroups. This is especially true if you use viewgroups with weight as fragment containers. To overcome it, you might as well need to animate margins of the problematic child views and viewgroups / fragment containers.
And, to do all these things together, its always better to go for ObjectAnimator and AnimatorSet (if you can use them), along with some utility classes like MarginProxy
A different way to the solution posted by #knickedi is to use ObjectAnimator instead of Runnable. The idea is to use ObjectAnimator to adjust the weight of both left and right views. The views, however, need to be customised so that the weight can be exposed as a property for the ObjectAnimator to animate.
So first, define a customised view (using a LinearLayout as an example):
public class CustomLinearLayout extends LinearLayout {
public CustomLinearLayout(Context context) {
super(context);
}
public CustomLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setMyWeight(float value) {
LinearLayout.LayoutParams p = (LinearLayout.LayoutParams)getLayoutParams();
p.weight = value;
requestLayout();
}
}
Then, update the layout XML to use this custom linear layout.
Then, when you need to toggle the animation, use ObjectAnimator:
ObjectAnimator rightView = ObjectAnimator.ofFloat(viewgroup_right, "MyWeight", 0.5f, 1.0f);
ObjectAnimator leftView = ObjectAnimator.ofFloat(viewgroup_left, "MyWeight", 0.5f, 0.0f);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.setDuration(1000); // 1 second of animation
animatorSet.playTogether(rightView, leftView);
animatorSet.start();
The above code assumes both views are linear layout and are half in weight to start with. The animation will expand the right view to full weight (so the left one is hidden). Note that ObjectAnimator is animated using the "MyWeight" property of the customised linear layout. The AnimatorSet is used to tie both left and right ObjectAnimators together, so the animation looks smooth.
This approach reduces the need to write runnable code and the weight calculation inside it, but it needs a customised class to be defined.