I want to set a touch event on an animated view and then stop the animation on touching view.
Here is my activity code:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
touch_me = (TextView) findViewById(R.id.touch_me);
touch_me.setOnTouchListener(new OnTouchMeListener());
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int fromX = size.x;
int fromY = size.y;
int toX = (-1) * view.getWidth();
TranslateAnimation animation = new TranslateAnimation(fromX, -100, 0, 0);
animation.setDuration(6000);
animation.setRepeatCount(5);
animation.setFillAfter(true);
touch_me.startAnimation(animation);
}
Here is my class listener:
public class OnTouchMeListener implements View.OnTouchListener{
#Override
public boolean onTouch(View v, MotionEvent event) {
touch_me.clearAnimation();
return true;
}
}
The problem is that the onTouch is detected only on the initial position(in XML file) of the view
Can anyone help me please ?
ok, here's the problem.
the view animation you are using is from older system and it only simulates the animation, not move the positioning of the view. so when you touch the area where the view has moved, there is no reading on your touch so it gets ignored. but if you touch the area of the original position of the view, it will fire the touchevent callback method and cancel the animation per your code.
there are several approach to solve this.
one is this by calling onlayout and relocating the view each time there is a change in the animation by calling the view's onlayout method.
Button is not clickable after TranslateAnimation
or another is to change the view's touch area by using touchdelegate class
http://cyrilmottier.com/2012/02/16/listview-tips-tricks-5-enlarged-touchable-areas/
but i think these two options aren't very good approaches to your problem.
i would go with this third option:
use propertyanimation instead. it's different from older system in that it updates the values needed for changes instead of doing the actual animation. the actual animation is done inside the update method with your own algorithm.
http://developer.android.com/guide/topics/graphics/prop-animation.html
here is example:
ValueAnimator animTranslation = ValueAnimator.ofFloat(valueStart, valueEnd);
animTranslation.setDuration(300);
animTranslation.removeAllUpdateListeners();
animTranslation.removeAllListeners();
animTranslation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
myView.setRotation(currValue - (Float) animation.getAnimatedValue());
}
}
});
animRotation2.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
});
Related
First of all please excuse me if this question is already asked.
I am new to android animations.
I want a view lets say an Image View (with animation i.e,smooth movement) to permanently move one position to some other position of the screen lets say if current X co-ordinate to X+50 and similarly Y co-ordinate to Y+50. I was able to move using XML translate animation file but not able to get click event after that.I am able to get click event at the original position in the layout not from the translated position.
Can anyone guide me to right track?
Thanks in advance.
The Animations you use are deprecated.. I show an example of ObjectAnimator class... And this sample is in a Fragment, so i write the whole onCreateView.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main_screen, container, false);
ImageView image = (ImageView) rootView.findViewById(R.id.image);
ObjectAnimator translationY = ObjectAnimator.ofFloat(image, "translationY", 100);
translationY.setInterpolator(new AccelerateInterpolator());
ObjectAnimator translationX = ObjectAnimator.ofFloat(image, "translationX", 100);
translationX.setInterpolator(new AccelerateInterpolator());
AnimatorSet as = new AnimatorSet();
as.playTogether(translationX, translationY);
as.setDuration(2000);
as.start();
image.setOnClickListener(new View.OnClickListener() {
int sum = 0;
#Override
public void onClick(View v) {
Toast.makeText(getActivity(), "Clicked: " + sum++, Toast.LENGTH_SHORT).show();
}
});
return rootView;
}
Do animation programatically. Try this code. It may help
TranslateAnimation translateAnimation = new TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta);
translateAnimation.setDuration(500);
translateAnimation.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
//before animation
}
#Override
public void onAnimationEnd(Animation animation) {
//after animation
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
imageView.startAnimation(translateAnimation);
You probably using view.startAnimation();. Key here is do your animation programatically.
do
Animation animation = AnimationUtils.loadAnimation (this.context, R.anim.your_translate_animation);
animation .setStartOffset (2000);
yourView.startAnimation (animation);
animation.setAnimationListener (new AnimationListener () {
#Override
public void onAnimationStart (Animation animation) {
}
#Override
public void onAnimationRepeat (Animation animation) {
}
#Override
public void onAnimationEnd (Animation animation) {
//do you click functionality here...
}
});
P.S. If you want to click view programtically
do
yourView.performClick();
I want to implement zoom feature on an ImageButton by Property Animation. For example, when I click the button, it will zoom out. And when I click it again, it will zoom in.
Here is part of my code:
OnClickListener clickPlayButtonHandler = new OnClickListener() {
#Override
public void onClick(View v) {
final ImageButton clickedButton = (ImageButton) v;
if((Boolean) v.getTag()) {
// zoom out
clickedButton.animate().setInterpolator(new AnticipateInterpolator()).setDuration(500).scaleXBy(-0.4f).scaleYBy(-0.4f).setListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
clickedButton.setImageResource(R.drawable.bg_pause);
System.out.println(clickedButton.getWidth()); // output the width of the button for checking
}
#Override
public void onAnimationEnd(Animator animation) {
clickedButton.setTag(false);
int d = clickedButton.getWidth();
System.out.println(clickedButton.getWidth());// output the width of the button for checking
}
#Override
public void onAnimationCancel(Animator animation) {}
#Override
public void onAnimationRepeat(Animator animation) { }
});
} else {
// process zoom in
}
}
};
I printed the width of the button before the animation start and the animation finished. However, I found they were the same. I thought when the zoom out animation finished the button width should be small than before. But it didn't.
Could not change view size by ViewPropertyAnimator?
There won't be changes in clickedButton.getWidth(), because the width of a view is not affected by scaling. You can think of getWidth() as a way to get unscaled width of a view. To change the width of a View requires a new measure/layout pass.
ViewPropertyAnimator doesn't change View's width/height or anything that could potentially trigger another layout pass. This is simply because layout passes are expensive and therefore could cause frame skipping, which is the last thing we want see during an Animation.
If you need to get scaled width of the button, you can do getScaleX() * clickedButton.getWidth()
Try the ObjectAnimator:
ObjectAnimator xAnimator =
ObjectAnimator.ofFloat(clickedButton, "scaleX", 1.0f, -0.4f);
ObjectAnimator yAnimator =
ObjectAnimator.ofFloat(clickedButton, "scaleY", 1.0f, -0.4f);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.setDuration(500);
animatorSet.playTogether(xAnimator, yAnimator);
animatorSet.setInterpolator(new AnticipateInterpolator());
animatorSet.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
clickedButton.setImageResource(R.drawable.bg_pause);
System.out.println(clickedButton.getWidth());
}
#Override
public void onAnimationEnd(Animator animation) {
clickedButton.setTag(false);
int d = clickedButton.getWidth();
System.out.println(clickedButton.getWidth());
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
});
animatorSet.start();
I have a Relative Layout containing some textviews which floats over a gridview. When I select an item in the grid, the layout moves down to nearly the end of the screen and only about 1/5th of it is visible. This is done using simple Translate Animation. Now when I click another button in the grid, I need to Relative Layout to move back to its original position on top of the gridview. I did this also with Translate Animation and this is happnening, but only the portion of the RelativeLayout which was visible when it was collapsed is translating (moving) and the other portion becomes visible after animation has ended. This looks really ugly.
I know that Android doesnt draw the portion of the view that is not visible on the screen. Is there a way to force draw the full portion ?
My Animation code is given below:
public void smoothExpanderAnimate(final int finalTarget,final RelativeLayout expander,final int animateToY,final boolean setExpanded,final boolean refreshGrid)
{
final RelativeLayout.LayoutParams head_params = (RelativeLayout.LayoutParams)expander.getLayoutParams();
TranslateAnimation anim = new TranslateAnimation(0f, 0f, 0f, animateToY);
anim.setDuration(400);
//timeListHeight = list.getHeight();
//anim.setFillAfter(true);
anim.setAnimationListener(new AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
//Should I try something here ?
}
#Override
public void onAnimationRepeat(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
head_params.topMargin = finalTarget;
expander.clearAnimation();
expander.setLayoutParams(head_params);
isExpanded = setExpanded;
isAnimating = false;
if(!setExpanded)
{
setChildHeight(timeListHeight, list);
}
}
});
isAnimating = true;
expander.startAnimation(anim);
}
I found a solution for this.
If you dont need to support lower API levels and are fine with API level 11 onwards, you can use ObjectAnimator for this. ObjectAnimator has a translatey method which works quite well.
If you need to support API 8 onwards, you need to make sure your view comes within the bounds of the screen and then animates. The following code modification did the trick.
I know it seems very weird, do let me know if there is a better way to do this.
public void smoothExpanderAnimate(boolean addSpecialFix,final int finalTarget,final RelativeLayout expander,final int animateToY,final boolean setExpanded,final boolean refreshGrid)
{
final RelativeLayout.LayoutParams head_params = (RelativeLayout.LayoutParams)expander.getLayoutParams();
TranslateAnimation anim = new TranslateAnimation(0f, 0f, 0f, animateToY);
anim.setDuration(400);
expander.setVisibility(View.INVISIBLE); //Make View invisible
if(isExpanded && addSpecialFix){
//THIS IS THE FIX
int old = head_params.topMargin;
head_params.topMargin = finalTarget;
expander.clearAnimation();
expander.setLayoutParams(head_params); //move invisible view to the final position
anim = new TranslateAnimation(0f, 0f, old-closedY, 0);
Log.d("asdasd", old+"");
anim.setDuration(400);
}
//timeListHeight = list.getHeight();
//anim.setFillAfter(true);
anim.setAnimationListener(new AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationRepeat(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
head_params.topMargin = finalTarget;
expander.clearAnimation();
expander.setLayoutParams(head_params);
isExpanded = setExpanded;
isAnimating = false;
expander.setVisibility(View.VISIBLE);
if(refreshGrid)
{
mCalAdapter.notifyDataSetChanged();
}
if(!setExpanded)
{
setListHeight(timeListHeight, list);
}
}
});
isAnimating = true;
expander.startAnimation(anim);
}
I had used TranslateAnimation and slide up and down a view.
However, I realize, even though after I slide down the view, and use View.GONE in its visibility, the view still able to receive touch event.
You can produce the same problem, by clicking on the button to make the orange color view disappear from the bottom of the screen. Then, when you click on the bottom of the screen, you will realize touch event of the custom view is still being triggered.
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
int color = getResources().getColor(android.R.color.holo_orange_light);
// construct the RelativeLayout
final RelativeLayout customView = new RelativeLayout(this) {
#Override
public boolean onTouchEvent(MotionEvent event) {
this.setPressed(true);
Log.i("CHEOK", "OH NO! TOUCH!!!!");
return super.onTouchEvent(event);
}
};
customView.setBackgroundColor(color);
final FrameLayout frameLayout = (FrameLayout)this.findViewById(R.id.frameLayout);
frameLayout.addView(customView, new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, 100, Gravity.BOTTOM));
customView.setVisibility(View.GONE);
Button button = (Button)this.findViewById(R.id.button1);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (customView.getVisibility() != View.VISIBLE) {
// Slide up!
TranslateAnimation anim=new TranslateAnimation(0,0,100,0);
anim.setFillAfter(true);
anim.setDuration(200);
Log.i("CHEOK", "VISIBLE!!!");
customView.setVisibility(View.VISIBLE);
customView.setAnimation(anim);
customView.setEnabled(true);
} else {
// Slide down!
TranslateAnimation anim=new TranslateAnimation(0,0,0,100);
anim.setFillAfter(true);
anim.setDuration(200);
// HELPME : Not sure why, after I hide the view by sliding it down,
// making it View.GONE and setEnabled(false), it still able to
// receive touch event.
Log.i("CHEOK", "GONE!!!");
customView.setVisibility(View.GONE);
customView.setAnimation(anim);
customView.setEnabled(false);
}
}
});
}
}
The complete source code to demonstrate this problem can be found here :
https://www.dropbox.com/s/1101dm885fn5hzq/animator_bug.zip
How can I make my custom view not to receive touch event, after I had slide it down?
It is not clear to me why you are using setAnimation() instead of startAnimation() as it does not appear that you have set a start time on your animations.
On another note, I have found that setting a view to GONE while it has an associated animation does not make it truly "GONE." Instead, you must first get rid of the animation using clearAnimation().
So, something like this instead:
public void onClick(View v) {
if (customView.getVisibility() != View.VISIBLE) {
// Slide up!
TranslateAnimation anim=new TranslateAnimation(0,0,100,0);
anim.setFillAfter(true);
anim.setDuration(200);
customView.setVisibility(View.VISIBLE);
customView.setEnabled(true);
customView.startAnimation(anim);
} else {
// Slide down!
TranslateAnimation anim=new TranslateAnimation(0,0,0,100);
anim.setFillAfter(true);
anim.setDuration(200);
anim.setAnimationListener(new AnimationListener() {
#Override
public void onAnimationEnd(Animation animation) {
customView.clearAnimation();
customView.setVisibility(View.GONE);
customView.setEnabled(false);
}
#Override
public void onAnimationRepeat(Animation animation) {
// nothing to do
}
#Override
public void onAnimationStart(Animation animation) {
// nothing to do
}
}
}
}
I don't recall, but you may have to post() the contents of onAnimationEnd() instead of running the code immediately for it to take effect properly.
I have several ImageViews in a RelativeLayout.
When the user taps any of the ImageViews, I want the ImageView to be moved to a specified location using a subtle animation.
Eg; I have initially set margins for LayoutParams associated with an ImageView as layoutparams1.setMargins(90,70,0,0); and I have then added it to the layout.
When the ImageView is tapped, I'd like its new location to be 200,200, done with animation.
So, is it possible? if yes, then how?
Note that I have both RelativeLayout and all of its child ImageViews created programmatically.
And I'm new to Android development so an elaborative answer is expected.
TranslateAnimation animation = new TranslateAnimation(0, 50, 0, 100);
animation.setDuration(1000);
animation.setFillAfter(false);
animation.setAnimationListener(new MyAnimationListener());
imageView.startAnimation(animation);
UPDATE :
The problem is that the View is actually still in it's old position. So we have to move it when the animation is finished. To detect when the animation is finished we have to create our own animationListener (inside our activity class):
private class MyAnimationListener implements AnimationListener{
#Override
public void onAnimationEnd(Animation animation) {
imageView.clearAnimation();
LayoutParams lp = new LayoutParams(imageView.getWidth(), imageView.getHeight());
lp.setMargins(50, 100, 0, 0);
imageView.setLayoutParams(lp);
}
#Override
public void onAnimationRepeat(Animation animation) {
}
#Override
public void onAnimationStart(Animation animation) {
}
}
So the onClickEvent will get fired again at it's new place.
The animation will now move it even more down, so you might want to save the x and y in a variable, so that in the onAnimationEnd() you move it not to a fix location.
It is better to use ObjectAnimator which actually moves the ImageView to the new position.
E.g.:
ImageView splash;
#Override
public boolean onTouchEvent(MotionEvent event) {
float tx = event.getX();
float ty = event.getY();
int action = event.getAction();
switch(action) {
case MotionEvent.ACTION_DOWN:
tx = event.getX();
ty = event.getY();
// findViewById(R.id.character).setX(tx-45);
// findViewById(R.id.character).setY(ty-134);
ObjectAnimator animX = ObjectAnimator.ofFloat(splash, "x", tx-45);
ObjectAnimator animY = ObjectAnimator.ofFloat(splash, "y", ty-134);
AnimatorSet animSetXY = new AnimatorSet();
animSetXY.playTogether(animX, animY);
animSetXY.start();
break;
default:
}
return true;
}
you can use this code
imageView.animate().x(80).y(212).setDuration(300);
or
for soft animation you can use this library
https://github.com/wirecube/android_additive_animations
In below code I am adding a image view in center on frame layout dynamically. After add I am increase scaling and set alpha to give zoom effect and after complete animation I am just translate my image view one position to another position.
Add image view on framelayout
imgHeart = new ImageView(getBaseContext());
imgHeart.setId(R.id.heartImage);
imgHeart.setImageResource(R.drawable.material_heart_fill_icon);
imgHeart.setLayoutParams(new FrameLayout.LayoutParams(50, 50, Gravity.CENTER));
mainFrameLaout.addView(imgHeart);
Add animation on image view
imgHeart.animate()
.scaleXBy(6)
.scaleYBy(6)
.setDuration(700)
.alpha(2)
.setListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
imgHeart.animate()
.scaleXBy(-6f).scaleYBy(-6f)
.alpha(.1f)
.translationX((heigthAndWidth[0] / 2) - minusWidth)
.translationY(-((heigthAndWidth[1] / 2) - minusHeight))
.setDuration(1000)
.setListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
// remove image view from framlayout
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
}).start();
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
}).start();
you can use this code :)
private void animeView(View imageView){
Handler handler = new Handler();
final int[] deltaX = {50};
final int[] deltaRotation = {45};
handler.postDelayed(new Runnable() {
#Override
public void run() {
imageView.animate().translationX(deltaX[0])
.rotation(deltaRotation[0]).setDuration(1000) ;
deltaX[0] *=-1 ;
deltaRotation[0] *=-1 ;
handler.postDelayed(this , 1000);
}
},1000);
}