Translating a button and making it persist - android

I'm currently trying to animate the position of a button using a TranslateAnimation. I've read in multiple places that the way to make the actual button move as well as the drawable is to change its layout after the animation. I've done that, but I've run into two problems/solutions:
Either I add setFillAfter( true ). This is nice because the drawable persists after the animation, but when the layout is changed, the drawable is offset from where it should be by the translation distance, while the blank frame of the button is now where it should be.
Add setFillAfter( false ). Doing this and then setting the layout after the animation works like it should, but the icon will flash, I assume this is the delay between the ending of the animation and the refresh of the screen with the new layout parameters. I'm currently using this code, but the flash is unacceptable, so I'd like to find a solution to fix it.
Here's my code currently:
final View aniView = v; // v is some view
TranslateAnimation ani = new TranslateAnimation(0,
240 - v.getLeft() - v.getWidth() / 2,
0,
240 - v.getTop() - v.getHeight() / 2 );
ani.setDuration( 500 );
ani.setFillAfter( false );
ani.setAnimationListener( new AnimationListener() {
public void onAnimationEnd( Animation a ) {
aniView.setTag( new Boolean( true ) );
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams( aniView.getWidth(),
aniView.getHeight() );
params.leftMargin = 240 - aniView.getWidth() / 2;
params.topMargin = 240 - aniView.getHeight() / 2;
aniView.setLayoutParams(params);
}
public void onAnimationStart( Animation a ) {}
public void onAnimationRepeat( Animation a ) {}
});
v.animationStart( ani );

I've figured out the solution to my own question, and will post the code for anyone who comes across the same problem. Basically the idea is to set the layout params before hand and essentially reverse the animation. Here's the idea:
TranslateAnimation ani = new TranslateAnimation(v.getLeft() + v.getWidth() / 2 - 240 ,
0,
v.getTop() + v.getHeight() / 2 - 240,
0);
ani.setDuration( 500 );
ani.setFillAfter( true );
ani.setDuration( 1000 );
ani.setFillAfter( true );
ani.setAnimationListener( new AnimationListener() {
public void onAnimationEnd( Animation a ) {
aniView.setTag( new Boolean( true ) );
}
public void onAnimationStart( Animation a ) {}
public void onAnimationRepeat( Animation a ) {}
});
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams( v.getWidth(),
v.getHeight() );
params.leftMargin = 240 - aniView.getWidth() / 2;
params.topMargin = 240 - aniView.getHeight() / 2;
v.setLayoutParams(params);
v.startAnimation( set );

Completing the awesome answer from Pete, I had the same problem trying to animate a full activity layout (the flashing in the end of the animation), but I used view.translateX instead of Layout Params... And this solution is also applicable to my case.

Related

MPAndroid - snapping x position when scrolling

After few hours of trying I'm looking for some hints on how to add snap-scroll mechanism to MPAndroid. Basically I want the 5 visible bars to align so they are fully visible and centered. I now imported the library source code because it looks like there's no other way to change the code in computeScroll (BarLineChartTouchListener).
Edit:
To clarify - I'm showing around 20 bars but chart is zoomed so user can scroll horizontally. What bothers me it is not getting aligned automatically so first visible bar might be clipped in half. I'm looking for snapping effect where it will round the position to the nearest multiplication of the bar width, leaving 5 fully visible bars.
I ended up adding the following function in BarLineChartBase.java. I know it's far from elegant, but seems to do the job. It's limited to targetApi > 11, because of the ValueAnimator. For lower API (which I don't cater for) you might need to have a look at nineoldandroids or some other animation loop technique.
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
public void alignX() {
int count = this.getValueCount();
int xIndex = this.getLowestVisibleXIndex() + Math.round( (this.getHighestVisibleXIndex() - this.getLowestVisibleXIndex()) / 2.0f );
float xsInView = this.getXAxis().getValues().size() / this.getViewPortHandler().getScaleX();
Transformer mTrans = this.getTransformer(YAxis.AxisDependency.LEFT);
float[] pts = new float[] { xIndex - xsInView / 2f, 0 };
mTrans.pointValuesToPixel(pts);
final Matrix save = new Matrix();
save.set(this.getViewPortHandler().getMatrixTouch());
final float x = pts[0] - this.getViewPortHandler().offsetLeft();
final int frames = 20;
ValueAnimator valueAnimator = new ValueAnimator().ofInt(0, frames);
valueAnimator.setDuration(500);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
int prev = -1;
#Override
public void onAnimationUpdate(ValueAnimator animation) {
if( (int) animation.getAnimatedValue() > prev ) {
save.postTranslate( -x / (float)frames, 0);
BarLineChartBase.this.getViewPortHandler().refresh(save, BarLineChartBase.this, true);
}
prev = (int) animation.getAnimatedValue();
}
});
valueAnimator.start();
}
I trigger it at the end of computeScroll function in BarLineChartTouchListener.
I kept names of variables as I copied code from functions like MoveViewJob, ViewPortHandler etc. Since it's only aligning in x axis - I removed Y axis calculations and used zeros instead. Any optimizations welcome, especially from the author #PhilippJahoda.

Animate imageView from one side of screen to the other - android

I am trying to animate an ImageView using TranslateAnimation from it's current position (0,Yscreensize/2) to the other side of the screen (Xscreensize,imageview.getY()) but I can't manage it to work. here is my code:
dart = (ImageView) findViewById(R.id.dart);
Display disp = getWindowManager().getDefaultDisplay();
Point poi = new Point();
disp.getSize(poi);
sizex = poi.x;
sizey = poi.y;
ViewTreeObserver vto = dart.getViewTreeObserver();
vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
public boolean onPreDraw() {
dart.getViewTreeObserver().removeOnPreDrawListener(this);
finalHeight = dart.getMeasuredHeight();
dart.setY(sizey / 2 - finalHeight);
return true;
}
}); // till now - got screen size and set dart imageview to Y middle.
Now when I am trying to use the animation:
TranslateAnimation animation = new TranslateAnimation(dart.getX(), sizex, dart.getY(), dart.getY());
animation.setDuration(10000);
dart.startAnimation(animation); // from current x to screen size, from currenty to currenty.
this will not work and the dart just dissappear. what can I do?
Make the variable for the animation a class variable (to ensure it won't be garbage collected to soon). In order to make the ImageView stop its movement before it leaves the screen, you need an additional class variable
float finalWidth;
Then change your OnPreDrawListener like this:
vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener()
{
public boolean onPreDraw()
{
dart.getViewTreeObserver().removeOnPreDrawListener(this);
float finalHeight = dart.getMeasuredHeight();
// if you want the view to stop before it leaves the screen
float finalWidth = dart.getMeasuredWidth();
animation = new TranslateAnimation(0, sizex - finalWidth, 0, 0);
animation.setDuration(10000);
// use this if you want the changes to persist
// animation.setFillAfter(true);
dart.setY(sizey / 2 - finalHeight);
return true;
}
});
I used a button to call 'startAnimation()'. The only problem when testing with an emulator is that the ImageView keeps running until it reaches the emulator window egde. So it disappears below the hardware controls region on the right side. In order to make it stop earlier, I tested with
Rect myRect = new Rect();
dart.getWindowVisibleDisplayFrame(myRect);
sizex = myRect.width() * 0.9f;
sizey = myRect.height();
which almost did the trick. It's just an approximation, all right, but getting the exact dimensions of the display - taking into account paddings or insets - seems to be difficult. At least below API 20.
Hope this helps anyway :)

How to make rotate animation smooth?

I am trying to move a needle image on meter with the help of RotateAnimation.
I am using the following code
mNeedleAnimation = new RotateAnimation(mTransformedDegree, mTransformedDegree + degreeToRotate,x, y);
mTransformedDegree += degreeToRotate;
mNeedleAnimation.setAnimationListener(this);
mNeedleAnimation.setDuration(200);
mNeedleAnimation.setFillEnabled(true);
mNeedleAnimation.setFillAfter(true);
mNeedle.startAnimation(mNeedleAnimation);
Problem with this code is that needle jumps directly from 1 value to other. But i want the needle movement to be smooth. I also tried to use the setInterPolator as linear for the animation but it didn't worked as expected.
Can anyone help me for the same?
Updating the needle view code
final ViewTreeObserver viewTreeObserver = mNeedle.getViewTreeObserver();
viewTreeObserver.addOnPreDrawListener(new OnPreDrawListener() {
#Override
public boolean onPreDraw()
{
mNeedleHeight= mNeedle.getHeight();
mNeedleWidth = mNeedle.getWidth();
return true;
}
});
You can use animation interporlators and instantiate any sub-class and set it to the animation.
mNeedleAnimation.setInterpolator(new AccelerateDecelerateInterpolator());
I am doing this :
rotanim = new RotateAnimation(
(float) (((currenspeed) * 360) / 180),
(float) (((nextspeed) * 360) / 180),
(float) imgNeedle.getWidth() / 2,
(float) imgNeedle.getHeight());
rotanim.reset();
rotanim.setDuration(400);
rotanim.setFillAfter(true);
imgNeedle.startAnimation(rotanim);
and it works good for me..

Adding Endless parallax background in cocos2d android

I am working on CoCos2d with android.I want to add an endless scrolling background to my Screen by using CCParallaxNode.
I am able to add background and move it but after the completion of that move action the screen goes black.
Can someone help me out?
My code is
CCParallaxNode parallaxNode;
CCSprite spacedust1;
CCSprite spacedust2;
CCSprite planetsunrise;
CCSprite galaxy;
CCSprite spacialanomaly;
CCSprite spacialanomaly2;
parallaxNode = CCParallaxNode.node();
spacedust1 = CCSprite.sprite("bg_front_spacedust.png");
spacedust2 = CCSprite.sprite("bg_front_spacedust.png");
planetsunrise = CCSprite.sprite("bg_planetsunrise.png");
galaxy = CCSprite.sprite("bg_galaxy.png");
spacialanomaly = CCSprite.sprite("bg_spacialanomaly.png");
spacialanomaly2 = CCSprite.sprite("bg_spacialanomaly2.png");
// 3) Determine relative movement speeds for space dust and background
// CGPoint cgPoint = CGPoint.ccp(0.1, 0.1);
CGPoint dustSpeed = CGPoint.ccp(10, 10);
CGPoint bgSpeed = CGPoint.ccp(5, 5);
// CGPoint bgSpeed = ccp(0.05, 0.05);
parallaxNode.addChild(spacedust1, 0, dustSpeed.x, dustSpeed.y, 0,
winSize.height / 2);
parallaxNode.addChild(spacedust2, 0, dustSpeed.x, dustSpeed.y,
spacedust1.getContentSize().width, winSize.height / 2);
parallaxNode.addChild(galaxy, -1, bgSpeed.x, bgSpeed.y, 0, 10);
parallaxNode.addChild(planetsunrise, -1, bgSpeed.x, bgSpeed.y, 600, 5);
parallaxNode
.addChild(spacialanomaly, -1, bgSpeed.x, bgSpeed.y, 900, 20);
parallaxNode.addChild(spacialanomaly2, -1, bgSpeed.x, bgSpeed.y, 1500,
30);
CCIntervalAction go = CCMoveBy.action(4, CGPoint.ccp(winSize.width, 0));
CCIntervalAction goBack = go.reverse();
CCIntervalAction seq = CCSequence.actions(go, goBack);
CCRepeatForever action = CCRepeatForever.action(goBack);
parallaxNode.runAction(action);
I see that since not a single answer worked for you. I will provide a simple code which will help you for your parralax scrolling background.
Add this code in your game layers constructor
background1 = CCSprite.sprite("bg2.png");
background2 = CCSprite.sprite("bg2.png");
background1.setPosition(CGPoint.ccp(winSize.width*0.5f,winSize.height*0.5f));
addChild(background1);
background2.setPosition(CGPoint.ccp(winSize.width+winSize.width*0.5f,winSize.height*0.5f));
addChild(background2);
and a scroll method which is scheduled every millisecond.
add this in constructor
this.schedule("scroll");
and now the scroll method.
public void scroll(float dt) {
CGPoint pos1 = background1.getPosition();
CGPoint pos2 = background2.getPosition();
pos1.x -= 5.0f;
pos2.x -= 5.0f;
if(pos1.x <=-(winSize.width*0.5f) )
{
pos1.x = pos2.x + winSize.width;
}
if(pos2.x <=-(winSize.width*0.5f) )
{
pos2.x = pos1.x + winSize.width;
}
background1.setPosition(pos1);
background2.setPosition(pos2);
}
mark my answer if it worked.
Call this method from the class Constructor. I found this trick from Example : "shotingblock-master" available on github...
private void endlessBackground() {
// Create the two background sprites which will alternate
_oddBackground = CCSprite.sprite("blue_background.png");
_evenBackground = CCSprite.sprite("blue_background.png");
// One starts dead centre and one starts exactly one screen height above
oddBackground.setPosition(_winSize.width / 2, _winSize.height / 2);
evenBackground.setPosition(_winSize.width / 2, _winSize.height
+ (_winSize.height / 2));
// Schedule the scrolling action
schedule("scroll");
// Add sprites to the layer
addChild(_oddBackground).addChild(_evenBackground);
}
public void scroll(float dt) {
// move them 100*dt pixels down
_oddBackground.setPosition(_oddBackground.getPosition().x,
_oddBackground.getPosition().y - 150 * dt);
_evenBackground.setPosition(_evenBackground.getPosition().x,
_evenBackground.getPosition().y - 150 * dt);
// reset position when they are off from view.
if (_oddBackground.getPosition().y < -_winSize.height / 2) {
_oddBackground.setPosition(_winSize.width / 2, _winSize.height / 2);
_evenBackground.setPosition(_winSize.width / 2, _winSize.height
+ (_winSize.height / 2));
}
}
}
IT works excellent in my case. May be it'll help full for you.
try using this:
CCTexture2D *texture = CCTextureCache::sharedTextureCache()->addImage("pic.png");
ccTexParams params = {GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT};
texture->setTexParameters(&params);
CCSprite *sprite = CCSprite::spriteWithTexture(texture, CCRectMake(0, 0, 90, 90));
and make sure that The image's height and width must be power of 2.
It looks like the CCRepeatForever action is only running it goBack, which means that it's not reversing. Try the following:
CCIntervalAction go = CCMoveBy.action(4, CGPoint.ccp(winSize.width, 0));
CCIntervalAction goBack = go.reverse();
CCIntervalAction seq = CCSequence.actions(go, goBack);
CCRepeatForever action = CCRepeatForever.action(seq); // change to seq instead of goBack
parallaxNode.runAction(action);
This is trick for make it happen. You can Use the large png and working on it or check the sample test code which is available in coocs2d-android library
CCSprite background = CCSprite.sprite("background.png");
// create a void node, a parent node
CCParallaxNode voidNode = CCParallaxNode.node();
// background image is moved at a ratio of 0.4x, 0.5y
voidNode.addChild(background, -1, 0.4f, 0.5f, 0, 0);
// write your own code for the parallax node
CCIntervalAction goUp = CCMoveBy.action(4, CGPoint.make(0,-200));
CCIntervalAction goDown = goUp.reverse();
CCIntervalAction go = CCMoveBy.action(8, CGPoint.make(-1000, 0));
CCIntervalAction goBack = go.reverse();
CCIntervalAction seq = CCSequence.actions(goUp, go, goDown, goBack);
voidNode.runAction(CCRepeatForever.action(seq));
addChild(voidNode);
Please check out below link for Parallax vertical endless background:
http://kalpeshsantoki.blogspot.in/2014/07/create-vertical-endless-parallax.html
CGSize winSize = CCDirector.sharedDirector().displaySize();
//I made graphics for screen 720*1200....so I made this dynamic scale to support multiple screens
float sX = winSize.width / 720.0f;
float sY = winSize.height / 1200.0f;
background = CCVerticalParallaxNode.node(sX, sY, true);
background.addEntity(1f, "background.png", 0);
background.addEntity(3, "road_simple.png", winSize.width / 2);
background.addEntity(1.7f, "road_side.png", 0);
addChild(background);

Android Layout Issue:Screen not visible

I am trying to implement something like the filters in zomato app where on selecting the filters the first screen moves showing up only a part of the first screen and whole of second screen where we can select the type of filter which upon selecting gives us all the types of filters after moving the first and the second screen both.
What I have done is I have the inner screens(filter type and filter option) as two relative layouts which are invisible and then i slide them and make them visible by changing the margins of it's layout parameters and requesting the layout .
While showing up the third screen(filter options) the second screen is not visible even if i provide the correct margins .
Can anyone help?
EDIT CODE :
private class SlidingAnimation extends Animation {
private float mainViewStartX, mainViewEndX;
private float sideViewFromX, sideViewToX;
private float sideView2FromX,sideView2ToX;
private View slideInView,slideOutView,slideInView2;
public SlidingAnimation(float fromX, float toX, float sideViewFromX,
float sideViewToX,float sideView2FromX,float sideView2ToX, int duration,View slideInView,View slideOutView,View slideInView2) {
setDuration(duration);
this.slideInView = slideInView;
this.slideOutView = slideOutView;
this.slideInView2 = slideInView2;
mainViewEndX = toX;
mainViewStartX = fromX;
slideOutViewLayoutParams = (LayoutParams) slideOutView.getLayoutParams();
this.sideViewFromX = sideViewFromX;
this.sideViewToX = sideViewToX;
slideInViewLayoutParams = (LayoutParams) slideInView.getLayoutParams();
if(slideInView2!=null){
Log.w(null,"Side View two also exists");
this.sideView2FromX = sideView2FromX ;
this.sideView2ToX = sideView2ToX;
slideInView2LayoutParams = (LayoutParams)slideInView2.getLayoutParams();
}
}
#SuppressLint("NewApi")
#Override
protected void applyTransformation(float interpolatedTime,
Transformation t) {
super.applyTransformation(interpolatedTime, t);
if (interpolatedTime < 1.0f) {
// Applies a Smooth Transition that starts fast but ends slowly
if(slideInView2==null){
slideOutViewLayoutParams.rightMargin = (int) (mainViewStartX + ((mainViewEndX - mainViewStartX) * (Math
.pow(interpolatedTime - 1, 5) + 1)));
slideInViewLayoutParams.rightMargin = (int) (sideViewFromX - ((sideViewFromX-sideViewToX) * (Math
.pow(interpolatedTime - 1, 5) + 1)));
slideOutViewLayoutParams.leftMargin = (int)- mainViewEndX;
slideInView.requestLayout();
slideOutView.requestLayout();
Log.w(null,"Slide Out Right Margin:"+slideOutViewLayoutParams.rightMargin);
Log.w(null,"Slide Out Left Margin:"+slideOutViewLayoutParams.leftMargin);
Log.w(null,"Slide In Left Margin:"+slideInViewLayoutParams.leftMargin);
}
else{
slideOutViewLayoutParams.rightMargin = (int) (mainViewStartX + ((mainViewEndX - mainViewStartX) * (Math
.pow(interpolatedTime - 1, 5) + 1)));
slideInViewLayoutParams.rightMargin = (int) (sideViewFromX + ((sideViewToX-sideViewFromX) * (Math
.pow(interpolatedTime - 1, 5) + 1)));
slideOutViewLayoutParams.leftMargin = (int)- mainViewEndX;
slideInViewLayoutParams.leftMargin = (int)-sideViewToX;
slideInView2LayoutParams.rightMargin = (int) (sideView2FromX - ((sideView2FromX-sideView2ToX) * (Math
.pow(interpolatedTime - 1, 5) + 1)));
slideOutView.requestLayout();
slideInView.requestLayout();
slideInView2.requestLayout();
Log.w(null,"Slide Out Right Margin:"+slideOutViewLayoutParams.rightMargin);
Log.w(null,"Slide Out Left Margin:"+slideOutViewLayoutParams.leftMargin);
Log.w(null,"Slide In Left Margin:"+slideInViewLayoutParams.leftMargin);
Log.w(null,"Slide In Right Margin:"+slideInViewLayoutParams.rightMargin);
Log.w(null,"Slide In View 2 Right Margin:"+slideInView2LayoutParams.rightMargin);
}
}
}
}
The call :
SlidingAnimation open = new SlidingAnimation(-width+dpToPx(45), width - dpToPx(45/2), 0,width-dpToPx(45),-width+dpToPx(45),0,1000,filterTypeLayout, selectBusLayout,filterOptionLayout);
Try using two HorizontalScrollViews.
Add the HSVs to a FrameLayout.
Implement OnTouchListeners for the HSVs, handle the touch events and accordingly make the HSV smoothScroll to the specific position on the screen. You could specify positions based on screen width.
example: ScrollView.smoothScrollTo(4*width/5, 0);

Categories

Resources