ViewGroup AnimationListener not firing - android

I'm removing a child from a parent with animateLayoutChanges on. I want to know when the animation is complete, but can't get the listener to fire:
<LinearLayout
android:id="#+id/parent"
android:animateLayoutChanges="true">
<TextView android:id="#+id/child" />
</LinearLayout>
void testRemove() {
ViewGroup parent = (ViewGroup)findViewById(R.id.parent);
parent.setLayoutAnimationListener(listener);
TextView child = findViewById(R.id.child);
child.setVisibility(View.GONE); // this triggers the animation.
}
Animation.AnimationListener listener = new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
// None of these callback methods fire.
}
};
Any ideas why the listener wouldn't fire?
Thanks
http://developer.android.com/reference/android/view/ViewGroup.html#setLayoutAnimationListener(android.view.animation.Animation.AnimationListener)
-------- Update ------------------
A TransitionListener seems to work:
LayoutTransition layoutTransition = parent.getLayoutTransition();
layoutTransition.addTransitionListener(new TransitionListener());

Use the layout animation attribute in your LinearLayout xml,
Docs here : http://developer.android.com/reference/android/view/ViewGroup.html#attr_android:layoutAnimation
add a file to your res/anim folder of fade_in.xml, this will be your fade animation
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="#android:anim/decelerate_interpolator"
android:fromAlpha="0.0" android:toAlpha="1.0"
android:duration="#android:integer/config_mediumAnimTime" />
Then add a file to your res/anim folder of layout_fade_in.xml that has a reference to the fade animation #anim/fade_in you just made, this file will be used for android:layoutAnimation values
<?xml version="1.0" encoding="utf-8"?>
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
android:delay="0.5"
android:animation="#anim/fade_in" />
Now, change your LinearLayout xml slightly to include the android:layoutAnimation attribute and value
<LinearLayout
android:id="#+id/parent"
android:layoutAnimation="#anim/layout_fade_in"
android:animateLayoutChanges="true">
<TextView android:id="#+id/child" />
</LinearLayout>

Related

How to achieve this effect in Snackbar

Here is the gif:
There are several features:
Fade in and immediately out.
It has margin. Because the default effect is match parent width.
Now I have already solved the margin problem. But I don't know how to achieve the animation effect. And here's my code:
snackbar_animation.xml:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="500"
android:fillAfter="true"
android:shareInterpolator="true">
<alpha
android:fromAlpha="0.1"
android:toAlpha="1" />
<scale
android:fromXScale="0.5"
android:fromYScale="0.0"
android:toXScale="1"
android:toYScale="1"/>
</set>
MainActivity:
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private Button snackbar;
private CoordinatorLayout coordinatorLayout;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.snackbar_test);
coordinatorLayout = findViewById(R.id.coordinator);
snackbar = findViewById(R.id.show_snackbar);
snackbar.setOnClickListener(this);
}
#Override
public void onClick(View view) {
switch (view.getId()){
case R.id.show_snackbar:
showSnackbar();
break;
}
}
private void showSnackbar(){
Snackbar snackbar = Snackbar.make(coordinatorLayout,"i am a snack bar",Snackbar.LENGTH_SHORT);
View sbView = snackbar.getView();
// add animation
Animation animation = AnimationUtils.loadAnimation(this,R.anim.snackbar_animation);
sbView.setAnimation(animation);
// modify margin
CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) sbView.getLayoutParams();
params.setMargins(params.leftMargin + 50,
params.topMargin,
params.rightMargin + 50,
params.bottomMargin + 50);
sbView.setLayoutParams(params);
// show snackbar
snackbar.show();
}
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
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:id="#+id/coordinator"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<Button
android:id="#+id/show_snackbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:paddingTop="10dp"
android:text="show snackbar" />
</android.support.design.widget.CoordinatorLayout>
If there are something else I need to know about this question, just comment below this question.such as: value animation or other things.I am new to the animation, so if you know the great learning materials, tell me, Thanks.
Read this. setAnimation() just queues an animation. It doesn't actually start anything. SnackBar doesn't call startAnimation when it's show, either.
However, even if you use startAnimation(), your code probably won't work as-is. You need to initially set the scale of your SnackBar to 0, so it's at the proper starting value.
You should use SnackBar#setCallback() and then start your show animation inside the onShow() method. Set your dismiss animation inside the onDismiss() method.

Android Animation: How to trigger it automatically and make continuous in and out zooming

I am learning about animation in Android and have following code (based on Android-Developer tutorial) which works when I click on the ImageButton. On click, it will zoom-in and then on next click, it will zoom-out.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.container_layout);
final View thumb1View = findViewById(R.id.thumb_button_1);
thumb1View.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
zoomImageFromThumb(thumb1View, R.mipmap.ic_launcher);
}
});
mShortAnimationDuration = getResources().getInteger(android.R.integer.config_longAnimTime);
}
And this is the layout:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:padding="16dp">
<ImageButton
android:id="#+id/thumb_button_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:src="#mipmap/ic_launcher"
android:scaleType="centerCrop"
android:layout_gravity="center"
android:background="#null"
android:contentDescription="#string/description_image_1" />
</LinearLayout>
<!-- This initially-hidden ImageView will hold the expanded/zoomed version of
the images above. Without transformations applied, it takes up the entire
screen. To achieve the "zoom" animation, this view's bounds are animated
from the bounds of the thumbnail button above, to its final laid-out
bounds.
-->
<ImageView
android:id="#+id/expanded_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="100dp"
android:visibility="invisible"
android:contentDescription="#string/description_zoom_touch_close" />
</FrameLayout>
However, I am trying to make it to keep zooming in/out once activity is loaded without need to click to activate it, then on click stop the zooming.
Much appreciated,
I figured it out by using animation xml in res/anim directory. Add new xml called my_alpha_animation.xml in your res/anim directory, if anim does not existe, create it:
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="500"
android:fromXScale="0.1"
android:fromYScale="0.1"
android:interpolator="#android:anim/accelerate_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="1.0"
android:toYScale="1.0"
android:repeatCount="infinite"
android:repeatMode="reverse">
</scale>
and loading it on Activities onResume():
#Override
protected void onResume(){
super.onResume();
doAlpha(btnAlpha);
}
And here is the method that does it:
private void doAlpha(View v){
Animation alphaAnimation = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.my_alpha_animation);
image.startAnimation(alphaAnimation); //image is my ImageView
}
Above, "image" is my ImageView for which I set image called "scanweb" from my mipmap directory and clear any animation that might be in progress in my Activity onCreate():
image = (ImageView) findViewById(R.id.imageView);
image.setImageResource(R.mipmap.scanweb);
image.clearAnimation();
The end result is that once Activity is created, my image "scanweb" starts pulsating (zooming in/out) infinitely.

Animation opens menu incorrectly

I have an issue where my menu in the applications opens in a slightly strange way, I have put a screenshot below to show what I mean. As you can see the menu is showing and moving the fragment out of the way. I want it so that it appears over the top of the existing fragment. I show this using an animation.
The animation code is as follows:
public static void open(final Context context, final RelativeLayout layout){
Animation in = AnimationUtils.loadAnimation(context, R.anim.push_right_out_80);
layout.startAnimation(in);
in.setAnimationListener(new Animation.AnimationListener(){
#Override
public void onAnimationStart(Animation arg0) {
}
#Override
public void onAnimationRepeat(Animation arg0) {
}
#Override
public void onAnimationEnd(Animation arg0) {
TranslateAnimation anim = new TranslateAnimation(0.0f, 0.0f, 0.0f, 0.0f);
layout.startAnimation(anim);
display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
int left = (int) (display.getWidth() * 1.0);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(layout.getLayoutParams());
params.setMargins(left / 2, 0, -left / 2, 0);
params.addRule(RelativeLayout.ABOVE, R.id.container);
layout.setLayoutParams(params);
}
});
}
This calls an xml file within the anim folder which looks like this:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="250"
android:fromXDelta="0"
android:fromAlpha="0.0"
android:toAlpha="0.3"
android:toXDelta="+50%p"/>
</set>
The menu is a listview which is within activity_home.xml and that looks like this:
<RelativeLayout
android:id="#+id/drawer_layout"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/White">
<ListView
android:id="#+id/listMenu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#ffffff"
android:choiceMode="singleChoice"
android:divider="#android:color/darker_gray"
android:dividerHeight="1dp"
android:listSelector="#android:color/darker_gray"/>
<RelativeLayout
android:id="#+id/fragmentspace"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/White">
</RelativeLayout>
</RelativeLayout>
The menu goes into the ListView and my fragment goes within the Relative Layout.
To close the menu I have this code (not too sure if this will need to be adapted too?)
public static void close(final Context context, final RelativeLayout layout){
Animation out = AnimationUtils.loadAnimation(context, R.anim.push_left_in);
layout.startAnimation(out);
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) layout.getLayoutParams();
params.setMargins(0, 0, 0, 0);
layout.setLayoutParams(params);
}
The close code calls another xml file also in the anim folder, that looks like this:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="250"
android:fromAlpha="0.3"
android:fromXDelta="+50%p"
android:toAlpha="0.0"
android:toXDelta="0"/>
</set>

Delays within the Animation (TranslateAnimation)

Is there a way to have the Animation pause for a half a second?
I am trying to make an infinite animation using the TranslateAnimation API. So, I use the RepeatCount as Infinite. I also noticed that there's a setStartOffset(...) method that covers the case when I'd like to have a delay in starting the animation. However, I can't find a way to have a delay before each 'restart'. Since animation is gonna happen infinite amount of times, every time the animation restarts I need to put a delay in.
Any ideas?
Thanks!!
Here is an example:
First the layout (main.xml) with an image we would like to animate:
<?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" >
<ImageView
android:id="#+id/imageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_launcher" />
</LinearLayout>
Next one is the animation. Placed in res/anim and is called anim_img.xml. The file contains the translation animation with android:startOffset="500" (in millisec). This will set the offset, which is used every time animation starts:
<?xml version="1.0" encoding="utf-8"?>
<set>
<translate
xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:fromXDelta="0%"
android:fromYDelta="0%"
android:toXDelta="0%"
android:toYDelta="100%"
android:zAdjustment="top"
android:repeatCount="infinite"
android:startOffset="500"/>
</set>
And last but not least - the activity. Which starts the animation:
public class StackOverflowActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ImageView iv_icon = (ImageView) findViewById(R.id.imageView1);
Animation a = AnimationUtils.loadAnimation(this, R.anim.anim_img);
a.setFillAfter(true);
a.reset();
iv_icon.startAnimation(a);
}
}
To achieve a pause of x milliseconds between each restart:
myAnimation.setAnimationListener(new AnimationListener(){
#Override
public void onAnimationStart(Animation arg0) {
}
#Override
public void onAnimationEnd(Animation animation) {
}
#Override
public void onAnimationRepeat(Animation animation) {
myAnimation.setStartOffset(x);
}
});
myanimation.setStartDelay(int);

An easy way to make GONE animation work

I have a custom search panel, which is a part of main layout. Most of time the panel is hidden. I would like to add appearing/disappearing animation to the panel. Here is the simplified layout excerpt:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<RelativeLayout
android:id="#+id/layoutSearch"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:visibility="gone" >
<EditText
android:id="#+id/editSearch"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<<Other inner views to be animated>>
</RelativeLayout>
<<Other views, which should not be affected by the animation>>
</LinearLayout>
Try 1: I added animation resources and attach them to the #id/layoutSearch with this line in XML:
android:layoutAnimation="#anim/search_in_layout"
anim/search_in.xml:
<translate
xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="#android:anim/overshoot_interpolator"
android:fromYDelta="-100%p"
android:toYDelta="0"
android:duration="#android:integer/config_longAnimTime" />
anim/search_in_layout.xml:
<layoutAnimation
xmlns:android="http://schemas.android.com/apk/res/android"
android:animation="#anim/search_in" />
The animation works fine, but only for the panel appearing. The panel disappears in a moment without animation when I hide it with:
mSearchLayout.setVisibility(View.GONE);
Try 2: I guess the above solution does not work as the animation destination parameters match the current panel position. OK, I created two more animation resources: anim/search_out.xml and anim/search_out_layout.xml. The only differences are exchanged "fromYDelta" and "toYDelta" values and updated "android:animation" value. Then I load the resources in the code and set them to the #id/layoutSearch like this:
LayoutAnimationController controller =
AnimationUtils.loadLayoutAnimation(this, R.anim.search_out_layout);
mSearchLayout.setLayoutAnimation(controller);
The "out" animation triggered on calling of setLayoutAnimation(). After the animation the search panel returns to it original position on the screen it had before "out" animation. If I try to call mSearchLayout.setVisibility(View.GONE) just after setLayoutAnimation(), I see no animation, the panel disappears at once.
Try 3: I guess I need to create the animation in the code and then set a listener on it. Then I should call mSearchLayout.setVisibility(View.GONE) in the onAnimationEnd() handler to hide the panel after the animation played. I did not tried this yet. I think it's over complicated.
I guess I missed something important. Is there a way to implement GONE animation a bit easy?
To follow on to the answers above : here is how I solved this problem.
Please note that setting setFillBefore and setFillAfter in your animation will lead to the following bug! Issue 5272: View with visibility View.GONE still generates touch events
http://code.google.com/p/android/issues/detail?id=5272
File : MyWebViewActivity.java
private View mBottomOverlay;
private View mTopOverlay;
private boolean mControlsOverlayVisible;
private Animation mSlideBottomUpAnimation;
private Animation mSlideBottomDownAnimation;
private Animation mSlideTopDownAnimation;
private Animation mSlideTopUpAnimation;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.reader_layout);
// load the overlay resources
mTopOverlay = findViewById(R.id.reader_overlay_top_toolbar);
mBottomOverlay = findViewById(R.id.reader_overlay_bottom_toolbar);
initAnimations();
}
private void initAnimations() {
final AnimationListener makeTopGone = new AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {}
#Override
public void onAnimationRepeat(Animation animation) {}
#Override
public void onAnimationEnd(Animation animation) {
Log.d(TAG, "onAnimationEnd - makeTopGone");
mTopOverlay.setVisibility(View.GONE);
}
};
final AnimationListener makeBottomGone = new AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {}
#Override
public void onAnimationRepeat(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
Log.d(TAG, "onAnimationEnd - makeBottomGone");
mBottomOverlay.setVisibility(View.GONE);
}
};
final AnimationListener makeTopVisible = new AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
Log.d(TAG, "onAnimationStart - makeTopVisible");
mTopOverlay.setVisibility(View.VISIBLE);
}
#Override
public void onAnimationRepeat(Animation animation) {}
#Override
public void onAnimationEnd(Animation animation) {}
};
final AnimationListener makeBottomVisible = new AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
Log.d(TAG, "onAnimationStart - makeBottomVisible");
mBottomOverlay.setVisibility(View.VISIBLE);
}
#Override
public void onAnimationRepeat(Animation animation) {}
#Override
public void onAnimationEnd(Animation animation) {}
};
mSlideTopUpAnimation = AnimationUtils.loadAnimation(this, R.anim.slide_top_up);
mSlideBottomDownAnimation = AnimationUtils.loadAnimation(this, R.anim.slide_bottom_down);
mSlideTopUpAnimation.setAnimationListener(makeTopGone);
mSlideBottomDownAnimation.setAnimationListener(makeBottomGone);
mSlideTopDownAnimation = AnimationUtils.loadAnimation(this, R.anim.slide_top_down);
mSlideBottomUpAnimation = AnimationUtils.loadAnimation(this, R.anim.slide_bottom_up);
mSlideTopDownAnimation.setAnimationListener(makeTopVisible);
mSlideBottomUpAnimation.setAnimationListener(makeBottomVisible);
}
private void hideControlOverlays() {
Log.d(TAG, "hideControlOverlays");
mTopOverlay.startAnimation(mSlideTopUpAnimation);
mBottomOverlay.startAnimation(mSlideBottomDownAnimation);
mControlsOverlayVisible = false;
}
private void showControlOverlays() {
Log.d(TAG, "showControlOverlays");
mTopOverlay.startAnimation(mSlideTopDownAnimation);
mBottomOverlay.startAnimation(mSlideBottomUpAnimation);
mControlsOverlayVisible = true;
}
File : /res/layout/reader_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/reader_layout"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<WebView
android:id="#+id/webview"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/reader_overlay_layout"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:id="#+id/reader_overlay_top_toolbar"
android:layout_width="fill_parent"
android:layout_height="140dp"
android:background="#80000000" >
<include layout="#layout/toolbar_top" />
</LinearLayout>
<LinearLayout
android:id="#+id/reader_overlay_bottom_toolbar"
android:layout_width="fill_parent"
android:layout_height="140dp"
android:layout_gravity="bottom"
android:background="#80000000"
android:orientation="horizontal" >
<include layout="#layout/toolbar_bottom_left" />
</LinearLayout>
</FrameLayout>
</FrameLayout>
File : /res/anim/slide_bottom_down.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillEnabled="true"
android:interpolator="#android:anim/accelerate_interpolator" >
<translate
android:duration="#android:integer/config_shortAnimTime"
android:fromYDelta="0"
android:toYDelta="100%" />
</set>
File : /res/anim/slide_bottom_up.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="#android:anim/accelerate_interpolator" >
<translate
android:duration="#android:integer/config_shortAnimTime"
android:fromYDelta="100%"
android:toYDelta="0" />
</set>
File : /res/anim/slide_top_down.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="#android:anim/accelerate_interpolator" >
<translate
android:duration="#android:integer/config_shortAnimTime"
android:fromYDelta="-100%"
android:toYDelta="0" />
</set>
File : /res/anim/slide_top_up.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillEnabled="true"
android:interpolator="#android:anim/accelerate_interpolator" >
<translate
android:duration="#android:integer/config_shortAnimTime"
android:fromYDelta="0"
android:toYDelta="-100%" />
</set>
Try 3: I guess I need to create the animation in the code and then set a listener on it. Then I should call mSearchLayout.setVisibility(View.GONE) in the onAnimationEnd() handler to hide the panel after the animation played. I did not tried this yet. I think it's over complicated.
That's what you should do and actually that's not hard to achieve.
Sample code:
public class YourClass extends Foo implements AnimationListener {
//...
#Override
public void onAnimationEnd(Animation a) {
// Do stuff.
}
#Override
public void onAnimationRepeat(Animation a) {
}
#Override
public void onAnimationStart(Animation a) {
}
}
you need to call setFillAfter() of Animation Class to hold the animation after playing.
RelativeLayout layout = (RelativeLayout) findViewById(R.id.layoutSearch);
Animation a = AnimationUtils.loadAnimation(this, R.anim.push_down);
a=setFillAfter(true);
layout.setLayoutAnimation(new LayoutAnimationController(a));
layout.startLayoutAnimation();
Setting a View to GONE effectively removes it from the layout. Instead you should set it to INVISIBLE, and then GONE after the animation has completed if you need to.

Categories

Resources