recently I wrote a function.it's about a refresh button in each list item. what I want is click the button or List Item, the refresh button starts rotate. stops when the request finished.
I use animation as follows:
<?xml version="1.0" encoding="UTF-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:fillAfter="true"
android:fromDegrees="0"
android:interpolator="#android:anim/linear_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="infinite"
android:toDegrees="358" />
and some source code are here:
public void refresh(View v) {
Animation rotation = AnimationUtils.loadAnimation(mContext,
R.anim.rotate);
rotation.setFillAfter(true);
v.startAnimation(rotation);
}
public void completeRefresh(View v) {
v.clearAnimation();
}
when the request finished, I call notifyDataSetChanged() to refresh the LiseView.
the problem is that the button was indeed rotating. but when I click it the second time. it's rotating but a little fuzzy. just like this:
any suggestions? thanks a lot.
What is most likely happening on your second (and subsequent clicks) is that the animation is running on it again.
In your current implementation, try using setTag(...) like this:
public void refresh(View v) {
if(v.getTag() != null && (boolean)v.getTag()) {
//do nothing, since we are setting the tag to be true once pressed
} else {
//this view hasn't been clicked yet, show animation and set tag
v.setTag(true);
Animation rotation = AnimationUtils.loadAnimation(mContext, R.anim.rotate);
rotation.setFillAfter(true);
v.startAnimation(rotation);
}
}
In your Adapter, you should keep track of which list items are being animated (I'm assuming multiple items can be clicked at the same time). Once you know that the 'request' is finished, you can update the adapter with the correct items and then call notifyDataSetChanged()
Related
In a RelativeLayout I have a TextView and a Button under the TextView and an ImageView covering both of the other objects. As the user opens the activity, the ImageView should cover the other items.
As the user swipes on the image (left or right), it should dismiss so the user can operate the button and read the textfield.
Whith an onClick listener I could dismiss the Image when clicked but I would like an animation, exactly like in a RecicleView when swiping to delete an element.
You could add the desired animation to the onClick event.
1) create(or add if existing) the anim folder inside the res directory and put slide_animation.xml
<?xml version="1.0" encoding="utf-8"?>
<set
xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="#android:anim/linear_interpolator"
android:fillAfter="true">
<translate
android:fromXDelta="0%p"
android:toXDelta="75%p"
android:duration="800" />
</set>
2) Add this code to the onclick action of the image view :
// Refer the ImageView like this
imageView = (ImageView) findViewById(R.id.imageView);
// Load the animation like this
animSlide = AnimationUtils.loadAnimation(getApplicationContext(),
R.anim.slide_animation);
// Start the animation like this
imageView.startAnimation(animSlide);
You can modify the animation specs in the xml so you can get the desired effect.
And then just implement AnimatorListener so you can dismiss the image view at animation end.
animSlide.setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
//remove image view from the layout
}
});
I am currently working on adding a income animation for RecyclerView items. I want it just likes the Google+ Android App. Here is my code snippets:
anim/up_from_bottom.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="#android:anim/decelerate_interpolator">
<translate
android:fromXDelta="0%" android:toXDelta="0%"
android:fromYDelta="60%" android:toYDelta="0%"
android:duration="500" />
</set>
and in the Adapter class I add the function startAnimation(itemHolder.itemView, position); in the funcitononBindViewHolder
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
...
startAnimation(holder.rootView, position);
}
the startAnimation() is like this:
protected void startAnimation(View view, int position) {
if (position > lastPosition) {
Animation animation = AnimationUtils.loadAnimation(mContext, R.anim.up_from_bottom);
view.startAnimation(animation);
lastPosition = position;
}
}
It seems looks like the Google+ Android App's list animation, but it has a bug, when I scroll fast, it animate not perfect, such as it seems that the first position and the current postion perform the animation at the same time.
I just want the animation like Google+, how can I fix it, or there has any other way.
I hava try recyclerview-animators, it's a great lib, but I still do what I want.
Any help please! Thanks!
Your "bug" occurs when you scroll fast because the animation on the viewholder is still running when it is scrolled out of the display and you are binding a new animation to the viewholder when the recyclerview recycles it.
You should override onViewDetachedFromWindow and clear the animation on the view in the method.
This will ensure that any animation that is running on the view will be cleared before it is sent to the recycler for resuse, and you can safely run a new animation on the view.
I'm having a recyclerView with several views and one is the animation view which has an animation applied to it. Once the view is out of the screen the animation is no longer active, even though the animation still exists.
The data:
rotate_around_center_point.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false" >
<rotate
android:duration="2500"
android:interpolator="#android:anim/linear_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="infinite"
android:repeatMode="restart"
android:toDegrees="360" />
</set>
Applying animation:
animation = AnimationUtils.loadAnimation(this.getContext(),
R.anim.rotate_around_center_point);
loadingRotatingCircleIV.startAnimation(animation);
I could not find any way to catch an event when the animation is interrupted so I'm able to restart the animation once it was out of the screen.
RecyclerView is incorrectly stopping animation on the view when view goes out from screen only if you apply View Animation to that view. If you will use Property Animation everything works properly. So the following solution will work:
ObjectAnimator animator = ObjectAnimator.ofFloat(loadingRotatingCircleIV, "rotation", 0.0f, 360.0f);
animator.setDuration(1000L);
animator.setRepeatCount(ObjectAnimator.INFINITE);
animator.setInterpolator(new LinearInterpolator());
animator.start();
If you want to use a ViewAnimation for a RecyclerView item, you have to restore animation in overriden method of RecyclerView - onViewAttachedToWindow(ItemViewHolder holder), like:
#Override
public void onViewAttachedToWindow(ItemViewHolder holder) {
super.onViewAttachedToWindow(holder);
if (holder.animated)
{
Animation anim = new ScaleAnimation(0.8f, 1.2f,
0.8f, 1.2f,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
anim.setFillAfter(true);
anim.setInterpolator(new BounceInterpolator());
anim.setDuration(1100);
anim.setRepeatCount(Animation.INFINITE);
anim.setRepeatMode(Animation.REVERSE);
holder.animatedView.startAnimation(anim);
}
}
I'm doing the exact same thing right now, and having the exact same problem. So the question is how to restart the animation once the view is back on screen.
I got the problem 99% resolved just by calling "loadingRotatingCircleIV.startAnimation(animation);" inside onBindViewHolder every time it gets called for my animated view's position.
But there's just a tiny little problem left. If you scroll down by just a little, so the view gets off screen, but it's view holder doesn't get recycled, when you scroll back up, onBindViewHolder is obviously not called again, but the animation is stopped.. So I'm still trying to fix that one..
So, my current solution:
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
...
if (position=="animated_view_position") view.startAnimation(animation);
...
}
If somebody has a better solution, please help.
create **HashMap<Integer, Boolean>** for saving each items animation loaded status
construct (){
for(int c = 0; c < adapterDataList.size(); c++){
//Here is create default status of animation loaded status used Boolean to saving
}
}
//Call Start Animation with onViewAttachedToWindow
#Override
public void onViewAttachedToWindow(ItemViewHolder holder) {
super.onViewAttachedToWindow(holder);
// get HashMap of each items Animation status like this
if(!HashMap.get(position)){ //FALSE means animation not loaded yet
//Here should call start Animation method;
} else {
//Here call no animation method, like setImageDrawable etc...
}
//create an AnimationListener for each item, when an animation is End, Listener should call a method to change HashMap above which you just created, like this **HashMap.put(position, Boolean.valueOf(true))** //TRUE means animation loaded
}
//to get position value as Integer use holder.getLayoutPosition()
I have a menu option in my ActionBarSherlock Actionbar that kicks off an AsyncTask. I'm trying to get the icon to animate while the background task is running. I have it working, except when I click on the icon, the icon itself will "jump" a couple pixels over to the right, and it's very noticeable. Not exactly sure what is causing that to happen.
This is what I have so far:
private MenuItem refreshItem;
private void DoRefresh()
{
final LayoutInflater inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
final ImageView ivRefresh = (ImageView)inflater.inflate(R.layout.refresh_view, null);
final Animation rotation = AnimationUtils.loadAnimation(activity, R.anim.refresh);
ImageView ivRefresh.startAnimation(rotation);
refreshItem.setActionView(ivRefresh);
//AsyncTask is kicked off here
}
#Override
public boolean onOptionsItemSelected(final MenuItem item)
{
if (item.getItemId() == R.id.refresh) {
refreshItem = item;
this.DoRefresh();
return true;
} else {
return super.onOptionsItemSelected(item);
}
}
refresh_view.xml
<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:src="#drawable/refresh"
/>
refresh.xml
<rotate
xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="0"
android:toDegrees="360"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="infinite"
android:interpolator="#android:anim/linear_interpolator"
android:duration="1100"
/>
UPDATE:
Been fooling around with this, with no luck. It has nothing to do with the animation because if I disable the animation, the icon still jumps over to the right. If I comment out setActionView, the icon does not jump, so it has something to do with that. Have no clue, driving me nuts.
I don't know if this will have any effect but if you play around with the pivotvalues you might sort out this problem. Perhaps only pivoting on one axis?
Alex Fu, your article was very helpful, but there are some improvements that can be made.
Restarting the animation in onAnimationEnd each time is inefficient. Instead, set the animation repeatcount to infinite before you start it, then when the action finishes, set the repeatcount to 0. The animation will finish the current loop, and then call onAnimationEnd, where you can call setActionView(null).
I've also encountered a problem (in the emulator at least, haven't tested on a real device, as I don't have one) with both ActionBarSherlock and the native ICS ActionBar, that the last few frames of the animation overlap with the static button after calling setActionView(null). My solution was to wrap the ImageView that is being animated in a LinearLayout, and then call setVisibility(View.GONE) on the LinearLayout. If you animate a view, using setVisibility to hide it won't work, but hiding its parent will.
Here's all the relevant code:
layout/actionbar_progress.xml (note that I've added ids, though only the ImageView needs one):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/actionbar_progress_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/actionbar_progress_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp"
android:src="#drawable/ic_action_refresh"
style="#style/Widget.Sherlock.ActionButton"
/>
</LinearLayout>
anim/refresh_rotate.xml:
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="0"
android:toDegrees="360"
android:pivotX="50%"
android:pivotY="50%"
android:duration="1000"
android:interpolator="#android:anim/linear_interpolator"
android:repeatCount="infinite">
</rotate>
repeatCount is set to infinite, but this isn't necessary, as the java code has to set it to infinite anyway.
In onCreateOptionsMenu I have
...
menuInflater.inflate(R.menu.main, menu);
mRefreshItem = menu.findItem(R.id.menu_refresh);
...
this is just to avoid doing the findItem each time the refresh button is clicked.
In onOptionsItemSelected:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
...
case R.id.menu_refresh:
refresh();
break;
...
}
}
the refresh method is just a dummy 5 second delayed post; it calls refreshAnim to start the animation, then refreshAnimEnd when it's done to stop the animation:
private void refresh() {
refreshAnim();
getWindow().getDecorView().postDelayed(
new Runnable() {
#Override
public void run() {
refreshAnimEnd();
}
}, 5000);
}
And here are the most important parts, with comments:
private void refreshAnim() {
LayoutInflater inflater = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
//The inflated layout and loaded animation are put into members to avoid reloading them every time.
//For convenience, the ImageView is also extracted into a member
if (mRefreshIndeterminateProgressView == null || mRefreshRotateClockwise == null) {
mRefreshIndeterminateProgressView = inflater.inflate(R.layout.actionbar_progress, null);
mRefreshRotateClockwise = AnimationUtils.loadAnimation(this, R.anim.refresh_rotate);
mRefreshImage = mRefreshIndeterminateProgressView.findViewById(R.id.actionbar_progress_image);
}
//reset some stuff - make the animation infinite again,
//and make the containing view visible
mRefreshRotateClockwise.setRepeatCount(Animation.INFINITE);
mRefreshIndeterminateProgressView.setVisibility(View.VISIBLE);
mRefreshRotateClockwise.setAnimationListener(new AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {}
#Override
public void onAnimationRepeat(Animation animation) {}
#Override
public void onAnimationEnd(Animation animation) {
//This is important to avoid the overlapping problem.
//First hide the animated icon
//setActionView(null) does NOT hide it!
//as long as the animation is running, it will be visible
//hiding an animated view also doesn't work
//as long as the animation is running
//but hiding the parent does.
mRefreshIndeterminateProgressView.setVisibility(View.GONE);
//make the static button appear again
mRefreshItem.setActionView(null);
}
});
mRefreshItem.setActionView(mRefreshIndeterminateProgressView);
//everything is set up, start animating.
mRefreshImage.startAnimation(mRefreshRotateClockwise);
}
private void refreshAnimEnd() {
//sanity
if ( mRefreshImage == null || mRefreshItem == null ) {
return;
}
Animation anim = mRefreshImage.getAnimation();
//more sanity
if (anim != null) {
//let the animation finish nicely
anim.setRepeatCount(0);
} else {
//onAnimationEnd won't run in this case, so restore the static button
mRefreshItem.setActionView(null);
}
}
Just add this to your img_layout.xml if you are using the normal ActionBar:
style="?android:attr/actionButtonStyle"
Or this with the SherLock ActionBar:
style="#style/Widget.Sherlock.ActionButton"
If the menu option is an action item, you should apply the style Widget.Sherlock.ActionButton to the ImageView to keep consistency. Read this article I wrote on how to animate action items properly. http://alexfu.tumblr.com/post/19414506327/android-dev-animate-action-items
In my android project I have an image that i want to rotate it and create animation for long time.
I can run program and it works fine. but I don't know how can I stop it. if you know please tell me.
Thanks
This is my code, I have a menu with two items, When I click on Start item, startAnimation function runs and when I click on Stop item, stopAnimation runs.:
public class Hypnagogic extends Activity
{
ImageView imView;
Animation an;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
imView = (ImageView)findViewById(R.id.imView);
}
private void startAnimation()
{
an = AnimationUtils.loadAnimation(this, R.anim.spin);
imView.startAnimation(an);
}
private void stopAnimation()
{
;//there is no any stop function!
}
}
also, in res/anim I put spin.xml that is my animation
<?xml version="1.0" encoding="utf-8"?>
<set>
<rotate
android:fromDegrees="0"
android:toDegrees="-18000"
android:pivotX="50%"
android:pivotY="50%"
android:duration="50000" />
</set>
EDIT: I see. Okay, try the clearAnimation() method associated with the View class. You can call it directly on your ImageView, I'm almost certain this should work for your case:
imView.clearAnimation();
What have you tried? AnimationDrawable has a stop() method. You need to post how you're creating/starting the animation.