onAnimationEnd is not getting called for imageview rotation animation - android

I have two ImageViews, one is rotating clockwise & other anti-clockwise.
Same code is working for other animation but for rotation, onAnimationEnd is not getting called.
onAnimationEnd is not getting called here.
public class ObcActivity extends AppCompatActivity implements Animation.AnimationListener {
ImageView circularImageView1;
ImageView circularImageView2;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView( R.layout.activity_obc);
// setContentView( new HeartbeatView(this));
clockAnimation=AnimationUtils.loadAnimation(this, R.anim.rotate_clockwise);
antiClockAnimation =AnimationUtils.loadAnimation(this, R.anim.rotate_anticlockwise);
clockAnimation.setAnimationListener(this);
antiClockAnimation.setAnimationListener(this);
clockAnimation.setRepeatCount(-1);
antiClockAnimation.setRepeatCount(-1);
clockAnimation.setRepeatMode(Animation.INFINITE);
antiClockAnimation.setRepeatMode(Animation.INFINITE);
circularImageView1= (ImageView) findViewById(R.id.circularImageView1);
circularImageView2= (ImageView) findViewById(R.id.circularImageView2);
circularImageView1.setAnimation(clockAnimation);
circularImageView1.startAnimation(clockAnimation);
circularImageView2.setAnimation(antiClockAnimation);
}
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
Toast.makeText(this,""+System.currentTimeMillis(),Toast.LENGTH_SHORT).show();
if(animation==clockAnimation){
circularImageView1.setVisibility(View.INVISIBLE);
circularImageView2.setVisibility(View.VISIBLE);
circularImageView1.clearAnimation();
circularImageView2.startAnimation(clockAnimation);
}else {
circularImageView2.setVisibility(View.INVISIBLE);
circularImageView1.setVisibility(View.VISIBLE);
circularImageView1.startAnimation(antiClockAnimation);
circularImageView2.clearAnimation();
}
}
#Override
public void onAnimationRepeat(Animation animation) {
}
Animation clockAnimation, antiClockAnimation;
}
XML:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#71bf44"
android:orientation="vertical">
<ImageView
android:id="#+id/circularImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_gravity="center"
android:src="#drawable/ic_circle"
/>
<ImageView
android:id="#+id/circularImageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_gravity="center"
android:src="#drawable/outer_ring_2_white"
/>
<ImageView
android:id="#+id/circularImageView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_gravity="center"
android:src="#drawable/outer_ring_3_white"
/>
<ImageView
android:id="#+id/circularImageView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_gravity="center"
android:src="#drawable/outer_ring_2_white_out"
/>
<ImageView
android:id="#+id/circularImageView4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_gravity="center"
android:src="#drawable/outer_ring_3_white_out"
/>
</RelativeLayout>
Update:
As per the answers, I set following code which is not working, not calling onAnimationEnd. I need to get event of when animation of first image ends!
clockAnimation.setRepeatCount(100);
antiClockAnimation.setRepeatCount(100);
clockAnimation.setRepeatMode(100);
antiClockAnimation.setRepeatMode(100);

Because you have set INFINITE in Animation
clockAnimation.setRepeatMode(Animation.INFINITE);
it will start with infinite mode means never End
Your animation iteration updatation will notify in onAnimationRepeat
#Override
public void onAnimationRepeat(Animation animation) {
}
What document saying of method onAnimationEnd
Notifies the end of the animation. This callback is not invoked for
animations with repeat count set to INFINITE.
as per comment:
But I need animation end event. for example after 2 rotations.
for that add this lines in code
clockAnimation.setRepeatCount(2);
clockAnimation.setRepeatMode(Animation.RESTART);

Have a look at the documentation for INFINITE animations:
void onAnimationEnd (Animation animation)
Notifies the end of the animation. This callback is not invoked for
animations with repeat count set to INFINITE.
In any event I suggest holding two Animation.AnimationListener objects and implementing one in your activity. This would make your code clearer and remove the need to ask questions like if(animation==clockAnimation).

Related

Animations get into infinite loop if they are started in the onAnimationEnd of AnimationListener

Simple task: by pressing button, it scales X value to 0, and when animation finished starts another animation on the second view which scales X from 0 to 1. After 1 second reverse animation should be played and that's all. Running code below I've got infinite animation loop of the first part of animation.
Used nineoldandroids lib, but I don't think this is something really different from native animations framework at least on jelly bean devices.
public class MainActivity extends Activity
{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final View mybutton = findViewById(R.id.mybutton);
final View myprogress = findViewById(R.id.myprogress);
mybutton.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
animate(mybutton).scaleX(0).setListener(new AnimatorListenerAdapter()
{
#Override
public void onAnimationEnd(Animator animation)
{
mybutton.setVisibility(View.INVISIBLE);
myprogress.setVisibility(View.VISIBLE);
ViewHelper.setScaleX(myprogress, 0f);
animate(myprogress).scaleX(1).setListener(new AnimatorListenerAdapter()
{
#SuppressWarnings("ConstantConditions")
#Override
public void onAnimationEnd(Animator animation)
{
mybutton.getHandler().postDelayed(new Runnable()
{
#Override
public void run()
{
animate(myprogress).scaleX(0).setListener(new AnimatorListenerAdapter()
{
#Override
public void onAnimationEnd(Animator animation)
{
myprogress.setVisibility(View.INVISIBLE);
mybutton.setVisibility(View.VISIBLE);
ViewHelper.setScaleX(mybutton, 0);
animate(mybutton).scaleX(1);
}
});
}
}, 1000);
}
});
}
});
}
});
}
}
Layout is simple:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:paddingBottom="#dimen/activity_vertical_margin"
>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/mycontainer">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="string text"
android:id="#+id/mybutton"
/>
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="#id/mybutton"
android:layout_alignTop="#id/mybutton"
android:layout_alignRight="#id/mybutton"
android:layout_alignBottom="#id/mybutton"
android:visibility="invisible"
android:id="#+id/myprogress"
/>
</RelativeLayout>
</RelativeLayout>
Where I was wrong?
Madness!
After investigation found source of it:
When animate() method called, it creates ViewPropertyAnimator and saves it to the map.
When you try to animate this view again with animate() it takes already created viewpropertyanimator from the map and immediately calls onAnimationStart() even before you set new animation parameters, and because animationlistener was set on the first animation, it was triggered! and launched first animation(second part of it). This is creates infinite loop.
To stop it you MUST clear old listener when you try to animate view second time, so
animate(mybutton).setListener(null).scaleX(1);
stops infinite loop.
Documentation should warn about it, definitely!

Splash screen involving finish() and back button

Alright, so I have a splash screen on my app. I was wondering if there was a way for me to have it where it only appears the first initial time the app opens (or if they force close it and such), and so that way if they press the back key, it doesn't take them to the splash screen.
I can make it so the user cant go back to the splash screen by just typing finish() and, though I cant remember it right now, I can make it where it only loads once. Once I call finish(), the splash will reappear upon reentering the app. Is there any way to fix this?
Yes, you can do that by calling finish() on the onPause method of your splashActivity.
Or another way to do this is to add android:noHistory="true" on splashActivity of your manifest.xml
With a SplashScreenActivity you're wasting time because they pause the initialization for 2-3seconds before they continue.
I prefer adding a Splash Screen Layout on top of my main_activity.xml. I detect the first start of the application by extending Application. If it´s the first start, I show my Splash-Screen while the UI is build in the background... (Use background threads if the ProgressBar lags!)
//Extend Application to save the value. You could also use getter/setter for this instead of Shared Preferences...
public class YourApplication extends Application {
public static final String YOUR_APP_STARTUP = "APP_FIRST_START";
#Override
public void onCreate() {
super.onCreate();
//set SharedPreference value to true
SharedPreferences mPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
SharedPreferences.Editor editor = mPreferences.edit();
editor.putBoolean(YOUR_APP_STARTUP, true);
editor.apply();
...
}
Check for your first start in your MainActivity
public class YourMainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//hide actionbar and other menu which could overlay the splash screen
getActionBar().hide();
setContentView(R.layout.activity_main);
Boolean firstStart = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getBoolean(TVApplication.YOUR_APP_STARTUP, true);
if (firstStart) {
//First app start, show splash screen an hide it after 5000ms
final RelativeLayout mSplashScreen = (RelativeLayout) findViewById(R.id.splash_screen);
mSplashScreen.setVisibility(View.VISIBLE);
mSplashScreen.setAlpha(1.0f);
final FrameLayout mFrame = (FrameLayout) findViewById(R.id.frame_container);
mFrame.setAlpha(0.0f);
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
Animation fadeOutAnimation = AnimationUtils.loadAnimation(getApplicationContext(),
R.anim.fade_out_animation);
fadeOutAnimation.setDuration(500);
fadeOutAnimation.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
mFrame.setAlpha(1.0f);
getActionBar().show();
}
#Override
public void onAnimationEnd(Animation animation) {
mSplashScreen.setVisibility(View.GONE);
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
mSplashScreen.startAnimation(fadeOutAnimation);
}
}, 5000); //<-- time of Splash Screen shown
} else {
((RelativeLayout) findViewById(R.id.splash_screen)).setVisibility(View.GONE);
getActionBar().show();
}
Insert the SplashScreen at top in your main.xml. I prefer RelativeLayout for that. In the example, SplashScreen is placed on to of a layout with the Navitgation Drawer, which we really love, don`t we?
//main_activity.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<android.support.v4.widget.DrawerLayout
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<!-- The main content view -->
<FrameLayout
android:id="#+id/frame_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<!-- The navigation drawer list -->
<ListView
android:id="#+id/slider_list"
android:layout_width="240dp"
android:layout_height="match_parent"
android:layout_alignParentTop="true"
android:layout_gravity="start"
android:background="#color/tvtv_background"
android:choiceMode="singleChoice"
android:divider="#drawable/nav_bar_divider"
android:dividerHeight="1dp"
android:listSelector="#android:color/transparent" />
</android.support.v4.widget.DrawerLayout>
<RelativeLayout
android:id="#+id/splash_screen"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentTop="true"
android:background="#color/tvtv_white"
android:visibility="visible" >
<ImageView
android:id="#+id/splash_screen_logo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:paddingLeft="50dp"
android:paddingRight="50dp"
android:scaleType="fitCenter"
android:src="#drawable/ic_launcher" />
<TextView
android:id="#+id/splash_screen_text"
style="#style/TVTextBlueContent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/splash_screen_logo"
android:layout_centerHorizontal="true"
android:padding="10dp"
android:text="Awesome splash shiat" />
<ProgressBar
android:id="#+id/splash_screen_loader"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/splash_screen_text"
android:layout_centerHorizontal="true"
android:clickable="false"
android:indeterminate="true" />
</RelativeLayout>
</RelativeLayout>

Setting LinearLayout's Visibility After Animation

I have a LinearLayout which I create in my xml file with its visibility set to Gone. I then showIt using an Animation and thats fine.
I fade out my LinearLayout using an AlphaAnimation and this works fine.
My Problem
The problem however is when I listen to the animation using an AnimationListener and the onAnimationEnd method is called, it doesn't set the visibility of my LinearLayout to GONE as I can still click the ImageButtons that are inside it which is how I know they're still there.
here is my xml with my LinearLayout.
<LinearLayout
android:id="#+id/photoOptionBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:alpha="1"
android:background="#color/gpsBackground"
android:orientation="horizontal"
android:visibility="gone">
<ImageButton
android:id="#+id/displayShareBtn"
style="#android:style/Widget.Holo.Button.Borderless.Small"
android:background="#android:color/transparent"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|center_horizontal"
android:padding="20dp"
android:src="#drawable/share"
android:contentDescription="Share the picture"/>
<ImageButton
android:id="#+id/displayDiscardBtn"
style="#android:style/Widget.Holo.Button.Borderless.Small"
android:background="#android:color/transparent"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|center_horizontal"
android:padding="20dp"
android:src="#drawable/contentdiscard"
android:contentDescription="Discard Picture"/>
</LinearLayout>
Here is my fade out method along with the listener.
AlphaAnimation alpha = new AlphaAnimation(1.0F, 0.0F);
alpha.setDuration(PhotoDisplayFragment.FADE_DURATION); // Make animation instant
alpha.setFillAfter(true); // Tell it to persist after the animation ends
// And then on your layout
final LinearLayout temp = this._photoOptionsBar;
alpha.setAnimationListener(new AnimationListener() {
#Override
public void onAnimationEnd(Animation animation) {
// TODO Auto-generated method stub
temp.setVisibility(View.GONE);
}
#Override
public void onAnimationRepeat(Animation animation) {
// TODO Auto-generated method stub
}
#Override
public void onAnimationStart(Animation animation) {
// TODO Auto-generated method stub
}
});
this._photoOptionsBar.startAnimation(alpha);
My Question
How do I set the visibility of the my LinearLayout to GONE after the animation ends?
Thanks in advance
The issue is related to setFillAfter which let the animation persist when it is finished. Remove and all will work as expected. If if you need the fade in animatio check the _photoOptionsBar visibilty and if it is gone apply the inverse animation:
new AlphaAnimation(0.0F, 1.0F);
final LinearLayout temp = (LinearLayout) findViewById(R.id.photoOptionBar);
temp.setVisibility(View.GONE);

Android Fade Out LinearLayout Never Starts

I am new to animations on Android and I seem to be having a simple issue... I have a splash/loading screen, which I want to fade out when it's done, then show the app.
My layout looks something like this (the styles just set a background image):
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/homeParentContainer"
style="#style/LayoutWithBgStyle"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<LinearLayout
android:id="#+id/homeSplashLayout"
style="#style/LayoutWithSplashStyle"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true" >
</LinearLayout>
<LinearLayout
android:id="#+id/homeMainLayout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:orientation="vertical"
android:visibility="gone" >
</LinearLayout>
</RelativeLayout>
Then I tried two different approaches to fading the splash screen out and setting the main screen visible:
final Animation fadeOut = AnimationUtils.loadAnimation(this, android.R.anim.fade_out);
final View splash = findViewById(R.id.homeMainLayout);
fadeOut.setAnimationListener(new AnimationAdapter()
{
#Override
public void onAnimationEnd(final Animation animation)
{
splash.setVisibility(View.GONE);
findViewById(R.id.homeMainLayout).setVisibility(View.VISIBLE);
}
/** And the other two methods */
});
splash.startAnimation(fadeOut);
Then I tried my own animation:
final AlphaAnimation fadeOut = new AlphaAnimation(1.0F, 0.0F);
fadeOut.setDuration(1000);
final View splash = findViewById(R.id.homeMainLayout);
fadeOut.setAnimationListener(new AnimationListener()
{
#Override
public void onAnimationEnd(final Animation animation)
{
splash.setVisibility(View.GONE);
findViewById(R.id.homeMainLayout).setVisibility(View.VISIBLE);
}
/** And the other two methods */
});
splash.startAnimation(fadeOut);
And I get to the startAnimation code, but the animation never seems to start, and I never get the onAnimationEnd() call. What have I forgotten to include to get the animation to actually run?
I have been a careless programmer.
final View splash = findViewById(R.id.homeMainLayout);
should actually read:
final View splash = findViewById(R.id.homeSplashLayout);
because fading out something that is invisible is not what I had intended.

View over Canvas Visibility issue

I have this layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<com.components.game.GameView
android:id="#+id/game_id"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
<RelativeLayout
android:id="#+id/ChatLayout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:visibility="invisible"
android:focusable="true"
android:focusableInTouchMode="true" >
<Button
android:id="#+id/ChatCancelButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="X" />
<Button
android:id="#+id/ChatOkButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:text="OK" />
<EditText
android:id="#+id/ChatEditText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="#+id/ChatOkButton"
android:layout_toRightOf="#+id/ChatCancelButton"
android:maxLength="50"
android:singleLine="true" />
</RelativeLayout>
</RelativeLayout>
It's a RelativeLayout over a canvas. At start time it's invisible but when a user clicks a button the layout should become visible.
The problem is that it's not becoming visible. The layout is there but it's just not drawing it. If I press the position where the layout should appear it receives the event and opens the keyboard but it's not drawing the whole layout.
What is the problem?
If I set the RelativeLayout to visible at the beginning it works fine. it shows the layout and if I toggle between invisible and visible it works fine.
I made a workaround that almost always works.
I start the layout visible and than do that in the oncreate:
chatLayout.postDelayed(new Runnable() {
#Override
public void run() {
chatLayout.setVisibility(View.INVISIBLE);
}
}, 50);
But I don't like it and want to understand what's the problem.
The code:
It starts from a canvas button which send a message to a handler:
public void showInputLayout() {
Message.obtain(gameHandler, SHOW_INPUT_LAYOUT).sendToTarget();
}
In the handler:
case SHOW_INPUT_LAYOUT:
gameActivity.setChatVisibility(true);
break;
setChatVisibility:
public void setChatVisibility(boolean isVisible) {
int visible = isVisible ? View.VISIBLE : View.INVISIBLE;
chatLayout.setVisibility(visible);
if(isVisible){
chatEditText.setFocusable(true);
chatEditText.requestFocus();
}
}
Add a click listener to RelativeLayout and switch the visibility between GONE and VISIBLE. Try something like this:
int visibility = View.VISIBLE;
RelativeLayout layout = (RelativeLayout)findViewById(R.id.ChatLayout);
layout.setVisibility(visibility);
layout.setOnClickListener(new View.OnClickListener{
public void onClick(View v)
{
if(visibility == View.VISIBLE)
visibility = View.GONE;
else
visibility = View.VISIBLE;
v.setVisibility(visibility);
}
})
I ran into a similar issue recently, and for my case the problem was actually in the onDraw() method of the view underneath (should be com.components.game.GameView in your case). See if you can add calls to Canvas' getSaveCount(), save() and restoreToCount() in your drawing code, similar to this:
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int saveCount = canvas.getSaveCount();
canvas.save();
// custom drawing code here ...
// use Region.Op.INTERSECT for adding clipping regions
canvas.restoreToCount(saveCount);
}
I believe what happened was that sometimes the framework set the clipping regions for the elements on top of our Canvas-drawing widget before our onDraw() method is called so we need to make sure that those regions are preserved.
Hope this helps.

Categories

Resources