how to implement umano/AndroidSlidingUpPanel lib - android

My App has a map fragment . And on clicking marker , an slide up animation shows from bottom to half of screen . And it slide down on clicking marker again .
I want : Slide up menu should be clickable or drag gable so that it
can move to top of screen . to be more clear , i mean either on
clicking or dragging this slide up menu which is on half of screen ,
should go to top of screen .
So far i done : On clicking marker, call the slide up animation to half of screen. :
Animation code : slide_up.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:interpolator="#android:anim/accelerate_decelerate_interpolator"
android:propertyName="yFraction"
android:valueType="floatType"
android:valueFrom="1.0"
android:valueTo="0.58"
android:duration="#android:integer/config_mediumAnimTime"/>
<objectAnimator
android:interpolator="#android:anim/accelerate_decelerate_interpolator"
android:propertyName="alpha"
android:valueType="floatType"
android:valueFrom="0.58"
android:valueTo="1.0"
android:duration="#android:integer/config_mediumAnimTime"/>
</set>
slide_down.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:interpolator="#android:anim/accelerate_decelerate_interpolator"
android:propertyName="yFraction"
android:valueType="floatType"
android:valueFrom="0.58"
android:valueTo="1.0"
android:duration="#android:integer/config_mediumAnimTime"/>
<objectAnimator
android:interpolator="#android:anim/accelerate_decelerate_interpolator"
android:propertyName="alpha"
android:valueType="floatType"
android:valueFrom="1"
android:valueTo="0"
android:duration="#android:integer/config_mediumAnimTime"/>
</set>
The code in Activity which calling this Animation on Marker click :
public void toggleList() {
Fragment f = getFragmentManager().findFragmentByTag(LIST_FRAGMENT_TAG);
if (f != null) {
getFragmentManager().popBackStack();
} else {
getFragmentManager()
.beginTransaction()
.setCustomAnimations(R.anim.slide_up,
R.anim.slide_down,
R.anim.slide_up,
R.anim.slide_down)
.add(R.id.list_fragment_container, BaseMapSlidingFragment
.instantiate(this, BaseMapSlidingFragment.class.getName()),
LIST_FRAGMENT_TAG
)
.addToBackStack(null).commit();
googleMap.getUiSettings().setAllGesturesEnabled(false);
if(animCheck == false){
animCheck = true;
}else
{
animCheck= false;
}
}}
menu_Sliding.up.xml
<?xml version="1.0" encoding="utf-8"?>
<com.trickyandroid.fragmenttranslate.app.view.SlidingRelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="#7c7c7c">
<ListView
android:id="#android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</com.trickyandroid.fragmenttranslate.app.view.SlidingRelativeLayout>
Custom_View :
**package com.trickyandroid.fragmenttranslate.app.view;
import android.content.Context;
import android.util.AttributeSet;
import android.view.ViewTreeObserver;
import android.widget.RelativeLayout;
/**
* Created by paveld on 4/13/14.
*/
public class SlidingRelativeLayout extends RelativeLayout {
private float yFraction = 0;
public SlidingRelativeLayout(Context context) {
super(context);
}
public SlidingRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SlidingRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
private ViewTreeObserver.OnPreDrawListener preDrawListener = null;
public void setYFraction(float fraction) {
this.yFraction = fraction;
if (getHeight() == 0) {
if (preDrawListener == null) {
preDrawListener = new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
getViewTreeObserver().removeOnPreDrawListener(preDrawListener);
setYFraction(yFraction);
return true;
}
};
getViewTreeObserver().addOnPreDrawListener(preDrawListener);
}
return;
}
float translationY = getHeight() * fraction;
setTranslationY(translationY);
}
public float getYFraction() {
return this.yFraction;
}
}**
Now how to get this menu to top of screen on clicking slide up menu
which is on the half of screen ?

I suggest you to use AndroidSlidingUpPanel library, that can be found here. There is no point for you to write the same thing again.
It has what you need and much more. It is easy to use and modify (I am using it in my project).
How to use
Include com.sothree.slidinguppanel.SlidingUpPanelLayout as the root element in your activity layout.
The layout must have gravity set to either top or bottom.
Make sure that it has two children. The first child is your main layout. The second child is your layout for the sliding up panel.
The main layout should have the width and the height set to match_parent.
The sliding layout should have the width set to match_parent and the height set to either match_parent, wrap_content or the max desireable height.
By default, the whole panel will act as a drag region and will intercept clicks and drag events. You can restrict the drag area to a specific view by using the setDragView method or umanoDragView attribute.

Related

Fragment transition having a blinking effect

I'm trying to put slide animation on a FragmentContainerView. However, the fragment that should appear flickers before starting the animation.
To summarize, when the transaction begins, the current fragment slides out, the next fragment flickers and then slides in.
I'm not asking for direct solutions, but if somebody knows what can cause this kind of bug, because I don't even know what to look at to solve this problem.
Edit: Code samples
FragmentContainerView (XML)
<androidx.fragment.app.FragmentContainerView
android:id="#+id/frame_cigarette"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="#id/toolbar_activity_filter"
app:layout_constraintBottom_toBottomOf="parent"
/>
Code for fragment transition
supportFragmentManager.beginTransaction()
.setCustomAnimations(
R.anim.slide_in_right,
R.anim.slide_out_left,
R.anim.slide_in_left,
R.anim.slide_out_right
)
.replace(
R.id.frame_cigarette,
CigaretteCravingFragment.newInstance(),
CigaretteCravingFragment::class.java.simpleName
)
.addToBackStack(CigaretteCravingFragment::class.java.simpleName)
.commit()
Code for an exit slide animation (slide out right)
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="#android:integer/config_mediumAnimTime"
android:fromXDelta="0"
android:toXDelta="100%p" />
</set>
Code for an enter slide animation (slide in left)
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="#android:integer/config_mediumAnimTime"
android:fromXDelta="-100%p"
android:toXDelta="0" />
</set>
I had the same problem and I solved this few moments ago. In my case Im using navigation graph to define all animation between fragments but in your case should work the same because setCustomAnimations also can take #AnimatorRes. You have to use ObjectAnimator and custom layout. Inspiration from: Animate the transition between fragments
CustomConstraintLayout.java
public class CustomConstraintLayout extends ConstraintLayout {
public float getXFraction() {
return getX() / getWidth();
}
public float getYFraction() {
return getY() / getHeight();
}
public void setXFraction(float xFraction) {
final int width = getWidth();
setX((width > 0) ? (xFraction * width) : -9999);
}
public void setYFraction(float yFraction) {
final int height = getHeight();
setY((height > 0) ? (yFraction * height) : -9999);
}
}
slide_in_from_bottom_animator.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:valueType="floatType"
android:propertyName="yFraction"
android:valueFrom="1.0"
android:valueTo="0.0"
android:interpolator="#android:anim/accelerate_decelerate_interpolator"
android:duration="#integer/change_view_anim_time"/>
</set>
YourFragment.xml
<?xml version="1.0" encoding="utf-8"?>
<com.yourapp.ui.CustomConstraintLayout 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">
<!-- some stuff -->
</com.yourapp.ui.CustomConstraintLayout>

R.animator with objectAnimators aren't working with Fragment add/remove animations

I don't know why this is always so hard to get working. I'm using the AppCompat Library and android.app.Fragment. I try to add animations to slide new fragments in left/right (like iOS does), but when the fragments are added they are adding / removing instantly, without any animation.
What am I doing wrong?
getFragmentManager()
.beginTransaction()
.setCustomAnimations(R.animator.slide_in_from_right, R.animator.slide_out_to_the_left)
.add(R.id.navrootlayout, fragment)
.addToBackStack(null)
.commit();
res/animator/slide_in_from_right.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:interpolator="#interpolator/decelerate_cubic"
android:valueFrom="1"
android:valueTo="0"
android:valueType="floatType"
android:propertyName="xFraction"
android:duration="3000"/>
</set>
res/animator/slide_out_to_the_left.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:interpolator="#interpolator/decelerate_cubic"
android:valueFrom="0"
android:valueTo="-1"
android:valueType="floatType"
android:propertyName="xFraction"
android:duration="3000"/>
</set>
I even set the duration of the animation to 3000 (i.e. 3 seconds) so that I could DEFINITELY see if it was being used at all, but it's not. The fragment is added without any animation at all. I captured a screen video of it happening, and the new fragment appears (and eventually disappears) instantly.
I figured out what I was doing wrong. I had grabbed the animation xml files from an example somewhere, and that example didn't happen to mention that I needed to implement the xFraction property myself. I wrongly assumed that this was a built-in behavior that would understand xFraction and x were related, similar to the way that the old style res/anim style animations would allow you to use percentage values as the animation start/end values.
But, nope. In order to do this, you have to create a subclass of your layout and add the xFraction property yourself. This is how I did it.
package com.mydomain.myapp;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.RelativeLayout;
public class SlideableLayout extends RelativeLayout {
public SlideableLayout(Context context) {
super(context);
}
public SlideableLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SlideableLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public float getXFraction() {
final int width = getWidth();
if (width != 0) {
return getX() / getWidth();
} else {
return getX();
}
}
public void setXFraction(float xFraction) {
final int width = getWidth();
if (width > 0) {
setX(xFraction * width);
} else {
setX(-10000);
}
}
}
Then, for each of my Fragments that I want to animate on and off the screen, I use my SlideableLayout as the root layout.
<?xml version="1.0" encoding="utf-8"?>
<com.mydomain.myapp.SlideableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/holo_blue_bright"
>
</com.mydomain.myapp.SlideableLayout>

Animation of a Custom Dialog Fragment in Android

To achieve a animated pop up, I have used a custom DialogFragment extending the DialogFragment class. Then, I designed the overlay layout in xml and added that during loading of the pop up. As, I needed to do the animation, I designed 2 animation xml files and added that in styles.xml and added the animation during the loading of the overlay.
I am showing the overlay animation in 3 places. It shows perfectly i.e. grows from the Bee icon and disappears in the bee icon when I use it as a component in a list item and when I use it along with a image view. But when I use it along with a VideoView or Exoplayer View the starting point of the animation shifts to the left for the x axis and goes up for the Y axis.
Also, the the pop up along with the animation works perfectly using the below code for Samsung Note Edge but causes the above problem for Samsung Galaxy S5 and Nexus 4.
It also works fine i.e. the overlay is at the exact position where the bee is for all 3 screens when I do not use the animation. So, I guess the error is in the animation.
I am giving sample code, I wrote.
Custom Dialog Fragment:
public class ConfirmBox extends DialogFragment {
private View source;
private RelativeLayout relLayout;
public ConfirmBox() {
}
public ConfirmBox(View source) {
this.source = source;
}
public ConfirmBox(View source,RelativeLayout relLayout) {
this.source = source;
this.relLayout =relLayout;
}
public static ConfirmBox newInstance(View source) {
return new ConfirmBox(source);
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setStyle(STYLE_NO_TITLE, 0);
}
#Override
public void onStart() {
super.onStart();
// Less dimmed background; see http://stackoverflow.com/q/13822842/56285
Window window = getDialog().getWindow();
WindowManager.LayoutParams params = window.getAttributes();
params.dimAmount = 0.2f; // dim only a little bit
window.setAttributes(params);
// Transparent background; see http://stackoverflow.com/q/15007272/56285
// (Needed to make dialog's alpha shadow look good)
window.setBackgroundDrawableResource(android.R.color.transparent);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Put your dialog layout in R.layout.view_confirm_box
final View view = inflater.inflate(R.layout.overlay_view, container, false);
RelativeLayout layout = (RelativeLayout) view.findViewById(R.id.overlayLayout);
layout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
getDialog().dismiss();
}
});
setDialogPosition();
return view;
}
/**
* Try to position this dialog next to "source" view
*/
private void setDialogPosition() {
if (source == null) {
return; // Leave the dialog in default position
}
// Find out location of source component on screen
// see http://stackoverflow.com/a/6798093/56285
int[] location = new int[2];
source.getLocationOnScreen(location);
int sourceX = location[0];
int sourceY = location[1];
Log.d("CONFIRM BOX:", "X: " + sourceX + "Y: " + sourceY);
Window window = getDialog().getWindow();
window.getAttributes().windowAnimations = R.style.PauseDialogAnimation;
// set "origin" to top left corner
window.setGravity(Gravity.TOP| Gravity.LEFT);
WindowManager.LayoutParams params = window.getAttributes();
params.x = sourceX;
params.y = sourceY - dpToPx(25);
Log.d("CONFIRM BOX:","X: "+ sourceX + "Y: "+ sourceY);
Log.d("CONFIRM BOX:","-----------------------------------------------------------");
window.setAttributes(params);
// Show the animation of the DialogueFragment
window.getAttributes().windowAnimations = R.style.PauseDialogAnimation;
}
public int dpToPx(float valueInDp) {
DisplayMetrics metrics = getActivity().getResources().getDisplayMetrics();
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, valueInDp, metrics);
}
}
anim_in.xml:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<scale
android:interpolator="#android:anim/linear_interpolator"
android:fromXScale="0.0"
android:toXScale="1.0"
android:fromYScale="0.0"
android:toYScale="1.0"
android:fillAfter="false"
android:startOffset="100"
android:duration="200"
android:pivotX = "5%"
android:pivotY = "-40%"
/>
<translate
android:fromYDelta="50%"
android:toYDelta="0"
android:startOffset="100"
android:duration="200"
/>
</set>
anim_out.xml:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<scale
android:interpolator="#android:anim/linear_interpolator"
android:fromXScale="1.0"
android:toXScale="0.0"
android:fromYScale="1.0"
android:toYScale="0.0"
android:fillAfter="false"
android:duration="200"
android:pivotX = "5%"
android:pivotY = "-40%"
/>
<translate
android:fromYDelta="0"
android:toYDelta="50%"
android:duration="200"
/>
</set>
styles.xml:
<style name="PauseDialogAnimation">
<item name="android:windowEnterAnimation">#anim/anim_in</item>
<item name="android:windowExitAnimation">#anim/anim_out</item>
</style>
Can someone kindly help me on zeroing in on the problem here?
Thanks.

Android - Making translations and objectAnimator on the same XML file

I've been trying to make a 3D Cube rotation effect while sliding from one fragment to another.
First i was using a translate effect (on XML) calling with FragmentTransaction.setCustomAnimations(...) and then, when opening/closing the fragment, i was playing with the Camera classe to make the rotation.
This was working FINE, but seems that I HAVE TOO (don't ask me why) use all of this animation using only XML file. After a long search i found out that i should use objectAnimator to make the rotation.
Followed the Google sample and i manage to make the flip animation. Now i need to translate the fragments making them sliding in and sliding out. Seems that i can't use objectAnimator and translate effect on the same XML file. Since this error appears:
java.lang.RuntimeException: Unknown animator name: translate at (...)
Any ideas on how i can make the sliding effect and use the objectAnimator on the same time?
Thank you for your time!
Code i've been using:
card_flip_right_in.xml
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<!-- Before rotating, immediately set the alpha to 0. -->
<objectAnimator
android:duration="0"
android:propertyName="alpha"
android:valueFrom="1.0"
android:valueTo="0.0" />
<!-- Rotate. -->
<objectAnimator
android:duration="#integer/card_flip_time_full"
android:interpolator="#android:interpolator/accelerate_decelerate"
android:propertyName="rotationY"
android:valueFrom="180"
android:valueTo="0" />
<!-- Half-way through the rotation (see startOffset), set the alpha to 1. -->
<objectAnimator
android:duration="1"
android:propertyName="alpha"
android:startOffset="#integer/card_flip_time_half"
android:valueFrom="0.0"
android:valueTo="1.0" />
</set>
Fragment calling another fragment: (cube rotation should be visible between this 2)
private void launchArticle(int prev, int pos){
ArticleFragment newFragment = new ArticleFragment();
Bundle args = new Bundle();
args.putString("pos", pos);
args.putInt("prev", prev);
newFragment.setArguments(args);
android.app.FragmentTransaction transaction = getFragmentManager().beginTransaction();
Fragment currFrag = (Fragment)getFragmentManager().findFragmentById(R.id.headlines_fragment);
if (currFrag != null) {
transaction.hide(currFrag);
}
transaction.setCustomAnimations(
R.animator.card_flip_right_in,
R.animator.card_flip_right_out,
R.animator.card_flip_left_in,
R.animator.card_flip_left_out
);
transaction.replace(R.id.fragment_container, newFragment, pos);
transaction.addToBackStack(null);
transaction.commit();
}
UPDATE:
I've manage to solve the previous problem using a class that extends my framelayout of the fragments i'm using
SlidingFrameLayout.java
public class SlidingFrameLayout extends FrameLayout
{
private static final String TAG = SlidingFrameLayout.class.getName();
public SlidingFrameLayout(Context context) {
super(context);
}
public SlidingFrameLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public float getXFraction()
{
final int width = getWidth();
if(width != 0) return getX() / getWidth();
else return getX();
}
public void setXFraction(float xFraction) {
final int width = getWidth();
setX((width > 0) ? (xFraction * width) : -9999);
}
public float getYFraction()
{
final int height = getHeight();
if(height != 0) return getY() / getHeight(); else return getY();
}
public void setYFraction(float yFraction) {
final int height = getHeight();
setY((height > 0) ? (yFraction * height) : -9999);
}
}
and by adding this to the objectAnimator:
<!-- Move -->
<objectAnimator
xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="#integer/card_flip_time_full"
android:interpolator="#android:anim/linear_interpolator"
android:propertyName="xFraction"
android:valueFrom="-1"
android:valueTo="0" />
This is working better, but the rottation axes are in the middle of the FrameLayout and it's not making the illusion of a cube... Is it possible to set the rotation axes on a certain point?
Solved the problem by creating my own methods on the extended FrameLayout. Here's the code from the extended FrameLayout:
//Rotate from Left to Right turning visible
public float getRotateLeftRightIn(){
return getRotationY();
}
public void setRotateLeftRightIn(int rotateLeftRightIn){
setPivotX(getWidth());
setPivotY(getHeight()/2);
setRotationY(rotateLeftRightIn);
}
And on the XML:
<!-- Rotate. -->
<objectAnimator
android:duration="#integer/card_flip_time_full"
android:interpolator="#android:interpolator/accelerate_decelerate"
android:propertyName="rotateLeftRightIn"
android:valueFrom="#integer/card_flip_rotation_off"
android:valueTo="0"
android:valueType="intType"/>
In this case, #integer/card_flip_time_full stands for the duration of the entire animation and #integer/card_flip_rotation_off stands for the degrees (in this case -90%).
After this, all I need to do to make this animation to work is, when starting the fragment, set the xml files in the custom animation
transaction.setCustomAnimations(enter,exit,popEnter,popExit);
Hope this can be useful to some one ^^
An alternative to the answer accepted, you could define an animator set, as seen here:
<item android:state_pressed="true">
<set>
<objectAnimator android:propertyName="translationZ"
android:duration="100"
android:valueTo="2"
android:valueType="floatType"/>
<!-- you could have other objectAnimator elements
here for "x" and "y", or other properties -->
</set>
</item>
<item android:state_enabled="true"
android:state_pressed="false"
android:state_focused="true">
<set>
<objectAnimator android:propertyName="translationZ"
android:duration="100"
android:valueTo="2"
android:valueType="floatType"/>
</set>
</item>
How to use StateListAnimator?

shaking / wobble view animation in android

I created an anim.xml file such as below to shake imageview like IOS icon shaking in android.
However it does not provide me same result.
Is there any better idea?
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="300"
android:fromDegrees="-2"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="infinite"
android:toDegrees="2" />
Try setting android:repeatMode="reverse". Below animation gives a very reasonable immitation on my Galaxy Nexus. Obviously you can fine tune the parameters to your own liking.
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="100"
android:fromDegrees="-5"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="infinite"
android:repeatMode="reverse"
android:toDegrees="5" />
Nice shake animation;
res/anim/shake.xml
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:duration="150"
android:fromXDelta="-10%"
android:repeatCount="5"
android:repeatMode="reverse"
android:toXDelta="10%"/>
</set>
How to use it
final Animation animShake = AnimationUtils.loadAnimation(this, R.anim.shake);
btn_done = (Button) findViewById(R.id.btn_act_confirm_done);
btn_done.startAnimation(animShake);
How to use it (Simpler version):
btn_done.startAnimation(AnimationUtils.loadAnimation(this,R.anim.shake));
You could try this:
shake.xml
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromXDelta="0"
android:toXDelta="10"
android:duration="1000"
android:interpolator="#anim/cycle_7" />
cycle_7.xml
<cycleInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
android:cycles="7" />
try to use this one:
<set xmlns:android="http://schemas.android.com/apk/res/android">
<rotate
android:duration="70"
android:fromDegrees="-5"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="5"
android:repeatMode="reverse"
android:interpolator="#android:anim/linear_interpolator"
android:toDegrees="5" />
<translate
android:fromXDelta="-10"
android:toXDelta="10"
android:repeatCount="5"
android:repeatMode="reverse"
android:interpolator="#android:anim/linear_interpolator"
android:duration="70" />
</set>
To make shake effect like this
First define shake animation inside anim folder as shake.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<rotate
android:duration="70"
android:fromDegrees="-5"
android:interpolator="#android:anim/linear_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="5"
android:repeatMode="reverse"
android:toDegrees="5" />
<translate
android:duration="70"
android:fromXDelta="-10"
android:interpolator="#android:anim/linear_interpolator"
android:repeatCount="5"
android:repeatMode="reverse"
android:toXDelta="10" />
</set>
Then in code
if (TextUtils.isEmpty(phone.getText())
|| phone.getText().length() < 10)
{
//shake animation
phone.startAnimation(AnimationUtils.loadAnimation(getActivity(), R.anim.shake));
}
I created a shake effect on Android and posted in GitHub. See if it works better.
https://github.com/teoinke/ShakeAnimation
Relevant code:
<?xml version="1.0" encoding="utf-8"?>
<set
xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="#android:anim/overshoot_interpolator"
android:fillAfter="true">
<translate
android:startOffset="100"
android:fromXDelta="0%p"
android:toXDelta="10%p"
android:duration="50" />
<translate
android:startOffset="150"
android:fromXDelta="0%p"
android:toXDelta="-25%p"
android:duration="110" />
<translate
android:startOffset="260"
android:fromXDelta="0%p"
android:toXDelta="25%p"
android:duration="120" />
<translate
android:startOffset="380"
android:fromXDelta="0%p"
android:toXDelta="-20%p"
android:duration="130" />
<translate
android:startOffset="510"
android:fromXDelta="0%p"
android:toXDelta="10%p"
android:duration="140" />
</set>
This one works pretty well (though not perfectly) as an iOS "incorrect PIN" shaking clone:
final float FREQ = 3f;
final float DECAY = 2f;
// interpolator that goes 1 -> -1 -> 1 -> -1 in a sine wave pattern.
TimeInterpolator decayingSineWave = new TimeInterpolator() {
#Override
public float getInterpolation(float input) {
double raw = Math.sin(FREQ * input * 2 * Math.PI);
return (float)(raw * Math.exp(-input * DECAY));
}
};
shakeField.animate()
.xBy(-100)
.setInterpolator(decayingSineWave)
.setDuration(500)
.start();
/**
*
* #param view view that will be animated
* #param duration for how long in ms will it shake
* #param offset start offset of the animation
* #return returns the same view with animation properties
*/
public static View makeMeShake(View view, int duration, int offset) {
Animation anim = new TranslateAnimation(-offset,offset,0,0);
anim.setDuration(duration);
anim.setRepeatMode(Animation.REVERSE);
anim.setRepeatCount(5);
view.startAnimation(anim);
return view;
}
use:
TextView tv;
makeMeShake(tv,20,5); // it will shake quite fast
For Kotlin users:
First create an Animation resource file called shake.xml. Right click on the res folder in Android Studio, then click New > Android Resource File > enter shake for the file name and select Animation for Resource type dropdown. Click OK.
Inside shake.xml paste the following:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:duration="200"
android:fromXDelta="-5%"
android:repeatCount="3"
android:repeatMode="reverse"
android:toXDelta="5%"/>
</set>
Now just call it on a view!
From within a fragment:
myView.startAnimation(AnimationUtils.loadAnimation(requireContext(), R.anim.shake))
From within an activity:
myView.startAnimation(AnimationUtils.loadAnimation(this, R.anim.shake))
(note - myView is the ID given to the view that you want to animate)
If you would like to fine-tune the animation, simply modify the values in shake.xml.
I created a very good approximation of iOS shaking (when you long press a icon to remove app from homescreen). You have to apply inside your code, programmatically, as it requires random number generation:
int dur1 = 70 + (int)(Math.random() * 30);
int dur2 = 70 + (int)(Math.random() * 30);
// Create an animation instance
Animation an = new RotateAnimation(-3, 3, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
// Set the animation's parameters
an.setDuration(dur1); // duration in ms
an.setRepeatCount(-1); // -1 = infinite repeated
an.setRepeatMode(Animation.REVERSE);
an.setFillAfter(true); // keep rotation after animation
// Create an animation instance
Animation an2 = new TranslateAnimation(-TranslateAnimation.RELATIVE_TO_SELF,0.02f,
TranslateAnimation.RELATIVE_TO_SELF,0.02f,
-TranslateAnimation.RELATIVE_TO_SELF,0.02f,
TranslateAnimation.RELATIVE_TO_SELF,0.02f);
// Set the animation's parameters
an2.setDuration(dur2); // duration in ms
an2.setRepeatCount(-1); // -1 = infinite repeated
an2.setRepeatMode(Animation.REVERSE);
an2.setFillAfter(true); // keep rotation after animation
AnimationSet s = new AnimationSet(false);//false means don't share interpolators
s.addAnimation(an);
s.addAnimation(an2);
// Apply animation to image view
itemView.setAnimation(s);
This code was design to be applied inside an adapter's gridview (getView), but you can apply to any view by changing the last line to:
yourViewName.setAnimations(s);
Kotlin version of lincolnq's answer
val FREQ = 3f
val DECAY = 2f
val decayingSineWave = TimeInterpolator { input ->
val raw = sin(FREQ * input * 2 * Math.PI)
(raw * exp((-input * DECAY).toDouble())).toFloat()
}
// where binding.loginFrame is the view you wanna shake
binding.loguinFrame.animate()
.withEndAction{
// here you can clear the fields after the shake
}
.xBy(-100f)
.setInterpolator(decayingSineWave)
.setDuration(500)
.start()
IOS wobble animation is not that simple try to change pivot x and y randomly when rotate. You should change the value programatically though. May be you also can use translate animation simultaneously
Banging my head for more than two hours, I knew how to shake and wobble an view.
Unfortunately the accepted answer won't work apart from onCreateView of fragment.
Example if you have onClick method and inside in it. You have animation like below it won't work.
Please go through the code.
DoneStart.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
register(view);
}
});
The register method has some checks like below code
private void register(View view) {
String type = typedThings.getText.toString();
String km = Km_Now.getText().toString();
if (serviceType == null) {
animationServiceList = AnimationUtils.loadAnimation(getActivity(), R.anim.shake_wobble);
silverServiceButton.setAnimation(animationServiceList);
generalServiceButton.setAnimation(animationServiceList);
platinumServiceButton.setAnimation(animationServiceList);
animationServiceList.start();
} else if (km == null) {
animationEditText = AnimationUtils.loadAnimation(getActivity(), R.anim.shake_wobble);
Km_Now.setAnimation(animationEditText);
animationEditText.start();
}
The Call animationServiceList.start(); will never be called,
SOLUTION: Use PropertyAnimator like ObjectAnimator.
Other answers are correct as well but this is a bit smoother than them since it uses an interpolator produces smooth numbers for back an forth movement
public class WobblyView extends ImageView implements ValueAnimator.AnimatorUpdateListener {
private final ValueAnimator va = ValueAnimator.ofInt(-10, 10);
public WobblyView(Context context) {
this(context, null);
}
public WobblyView(Context context, #Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public WobblyView(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setAdjustViewBounds(true);
setImageResource(R.drawable.ic_logo);
va.setInterpolator(new AccelerateDecelerateInterpolator());
va.setRepeatMode(ValueAnimator.REVERSE);
va.setRepeatCount(ValueAnimator.INFINITE);
va.setDuration(1000);
}
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
va.addUpdateListener(this);
va.start();
}
#Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
va.removeUpdateListener(this);
}
#Override
public void onAnimationUpdate(ValueAnimator animation) {
int heading = (int) animation.getAnimatedValue();
setRotation(heading);
}
}

Categories

Resources