I'm currently working in a messaging application, where I want to show new user as animated flying icon like facebook live floating reactions. Can anybody enlighten which process I should follow or any helpful library link please?
I've already tried browsing and checked few library links like this1 and this2 but these are not something similar to facebook live reaction packs.
I also asked your same question, and I did not find something similar for Android, so I decided to do it myself.
I created this project in Android Studio, I based on a tutorial of Swift that I found on YouTube, you can see it by clicking here.
I used the Path
class to draw a path to be followed by the bubble or image (ImageView
), and ObjectAnimator
to initialize the animation by means of that path.
In the end I used Rand
to randomize the values of the path to give it a more similar appearance to Facebook Live and not only to follow the same path all bubbles or images.
Here is the video of the demonstration of my project in Android Studio: Here!
I have achieved this by modifying an old code So here is my solution. You can change the direction and number of emojis as well you can make it fly over or under any views.
Main 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:layout_gravity="top|left"
android:orientation="vertical">
<FrameLayout
android:id="@+id/animation_holder"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
Main Activity
package com.example.hamidraza.flyinemojis;
import android.app.Activity;
import android.os.Bundle;
import android.view.ViewGroup;
import android.view.animation.Animation;
public class MainActivity extends Activity {
public void onCreate(Bundle savedState) {
super.onCreate(savedState);
setContentView(R.layout.activity_main);
emoji_one();
emoji_two();
emoji_three();
}
public void flyEmoji(final int resId) {
ZeroGravityAnimation animation = new ZeroGravityAnimation();
animation.setCount(1);
animation.setScalingFactor(0.2f);
animation.setOriginationDirection(Direction.BOTTOM);
animation.setDestinationDirection(Direction.TOP);
animation.setImage(resId);
animation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
}
);
ViewGroup container = findViewById(R.id.animation_holder);
animation.play(this,container);
}
public void emoji_one() {
// You can change the number of emojis that will be flying on screen
for (int i = 0; i < 5; i++) {
flyEmoji(R.drawable.emojis1);
}
}
// You can change the number of emojis that will be flying on screen
public void emoji_two(){
for(int i=0;i<5;i++) {
flyEmoji(R.drawable.dabemoji);
}
}
// You can change the number of emojis that will be flying on screen
public void emoji_three(){
for(int i=0;i<5;i++) {
flyEmoji(R.drawable.heart);
}
}
// This method will be used if You want to fly your Emois Over any view
// public void flyObject(final int resId, final int duration, final Direction from, final Direction to, final float scale) {
//
// ZeroGravityAnimation animation = new ZeroGravityAnimation();
// animation.setCount(1);
// animation.setScalingFactor(scale);
// animation.setOriginationDirection(from);
// animation.setDestinationDirection(to);
// animation.setImage(resId);
// animation.setDuration(duration);
// animation.setAnimationListener(new Animation.AnimationListener() {
// @Override
// public void onAnimationStart(Animation animation) {
//
// }
//
// @Override
// public void onAnimationEnd(Animation animation) {
//
// flyObject(resId, duration, from, to, scale);
// }
//
// @Override
// public void onAnimationRepeat(Animation animation) {
//
// }
// });
//
// ViewGroup container = (ViewGroup) findViewById(R.id.animation_bigger_objects_holder);
// animation.play(this,container);
//
// }
//
}
ZeroGravityAnimation
package com.example.hamidraza.flyinemojis;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.widget.FrameLayout;
public class ZeroGravityAnimation {
private static final int RANDOM_DURATION = -1;
private Direction mOriginationDirection = Direction.RANDOM;
private Direction mDestinationDirection = Direction.RANDOM;
private int mDuration = RANDOM_DURATION;
private int mCount = 1;
private int mImageResId;
private float mScalingFactor = 1f;
private Animation.AnimationListener mAnimationListener;
/**
* Sets the orignal direction. The animation will originate from the given direction.
*/
public ZeroGravityAnimation setOriginationDirection(Direction direction) {
this.mOriginationDirection = direction;
return this;
}
/**
* Sets the animation destination direction. The translate animation will proceed towards the given direction.
* @param direction
* @return
*/
public ZeroGravityAnimation setDestinationDirection(Direction direction) {
this.mDestinationDirection = direction;
return this;
}
/**
* Will take a random time duriation for the animation
* @return
*/
public ZeroGravityAnimation setRandomDuration() {
return setDuration(RANDOM_DURATION);
}
/**
* Sets the time duration in millseconds for animation to proceed.
* @param duration
* @return
*/
public ZeroGravityAnimation setDuration(int duration) {
this.mDuration = duration;
return this;
}
/**
* Sets the image reference id for drawing the image
* @param resId
* @return
*/
public ZeroGravityAnimation setImage(int resId) {
this.mImageResId = resId;
return this;
}
/**
* Sets the image scaling value.
* @param scale
* @return
*/
public ZeroGravityAnimation setScalingFactor(float scale) {
this.mScalingFactor = scale;
return this;
}
public ZeroGravityAnimation setAnimationListener(Animation.AnimationListener listener) {
this.mAnimationListener = listener;
return this;
}
public ZeroGravityAnimation setCount(int count) {
this.mCount = count;
return this;
}
/**
* Starts the Zero gravity animation by creating an OTT and attach it to th given ViewGroup
* @param activity
* @param ottParent
*/
public void play(Activity activity, ViewGroup ottParent) {
DirectionGenerator generator = new DirectionGenerator();
if(mCount > 0) {
for (int i = 0; i < mCount; i++) {
final int iDupe = i;
Direction origin = mOriginationDirection == Direction.RANDOM ? generator.getRandomDirection() : mOriginationDirection;
Direction destination = mDestinationDirection == Direction.RANDOM ? generator.getRandomDirection(origin) : mDestinationDirection;
int startingPoints[] = generator.getPointsInDirection(activity, origin);
int endPoints[] = generator.getPointsInDirection(activity,destination);
Bitmap bitmap = BitmapFactory.decodeResource(activity.getResources(), mImageResId);
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, (int) (bitmap.getWidth() * mScalingFactor), (int) (bitmap.getHeight() * mScalingFactor), false);
switch (origin) {
case LEFT:
startingPoints[0] -= scaledBitmap.getWidth();
break;
case RIGHT:
startingPoints[0] += scaledBitmap.getWidth();
break;
case TOP:
startingPoints[1] -= scaledBitmap.getHeight();
break;
case BOTTOM:
startingPoints[1] += scaledBitmap.getHeight();
break;
}
switch (destination) {
case LEFT:
endPoints[0] -= scaledBitmap.getWidth();
break;
case RIGHT:
endPoints[0] += scaledBitmap.getWidth();
break;
case TOP:
endPoints[1] -= scaledBitmap.getHeight();
break;
case BOTTOM:
endPoints[1] += scaledBitmap.getHeight();
break;
}
final OverTheTopLayer layer = new OverTheTopLayer();
FrameLayout ottLayout = layer.with(activity)
.scale(mScalingFactor)
.attachTo(ottParent)
.setBitmap(scaledBitmap, startingPoints)
.create();
switch (origin) {
case LEFT:
}
int deltaX = endPoints[0] - startingPoints[0];
int deltaY = endPoints[1] - startingPoints[1];
int duration = mDuration;
if (duration == RANDOM_DURATION) {
duration = RandomUtil.generateRandomBetween(3500, 12500);
}
TranslateAnimation animation = new TranslateAnimation(0, deltaX, 0, deltaY);
animation.setDuration(duration);
animation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
if (iDupe == 0) {
if (mAnimationListener != null) {
mAnimationListener.onAnimationStart(animation);
}
}
}
@Override
public void onAnimationEnd(Animation animation) {
layer.destroy();
if (iDupe == (mCount - 1)) {
if (mAnimationListener != null) {
mAnimationListener.onAnimationEnd(animation);
}
}
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
layer.applyAnimation(animation);
}
}
else {
Log.e(ZeroGravityAnimation.class.getSimpleName(),"Count was not provided, animation was not started");
}
}
/**
* Takes the content view as view parent for laying the animation objects and starts the animation.
* @param activity - activity on which the zero gravity animation should take place.
*/
public void play(Activity activity) {
play(activity,null);
}
}
RandomUtils java class
package com.example.hamidraza.flyinemojis;
import java.util.Random;
public class RandomUtil {
/**
* Generates the random between two given integers.
*/
public static int generateRandomBetween(int start, int end) {
Random random = new Random();
int rand = random.nextInt(Integer.MAX_VALUE - 1) % end;
if (rand < start) {
rand = start;
}
return rand;
}
}
Direction
package com.example.hamidraza.flyinemojis;
public enum Direction {
TOP, LEFT, RIGHT, BOTTOM,RANDOM
}
DirectionGeneretor
package com.example.hamidraza.flyinemojis;
import android.app.Activity;
import java.util.Random;
public class DirectionGenerator {
/**
* Gets the random pixel points in the given direction of the screen
* @param activity - activity from where you are referring the random value.
* @param direction - on among LEFT,RIGHT,TOP,BOTTOM,RANDOM
* @return a pixel point {x,y} in the given direction.
*/
public int[] getPointsInDirection(Activity activity, Direction direction) {
switch (direction) {
case LEFT:
return getRandomLeft(activity);
case RIGHT:
return getRandomRight(activity);
case BOTTOM:
return getRandomBottom(activity);
case TOP:
return getRandomTop(activity);
default:
Direction[] allDirections = new Direction[]{Direction.LEFT,Direction.TOP,Direction.BOTTOM,Direction.RIGHT};
int index = new Random().nextInt(allDirections.length);
return getPointsInDirection(activity, allDirections[index]);
}
}
/**
* Gets the random pixel points in the left direction of the screen. The value will be of {0,y} where y will be a random value.
* @param activity - activity from where you are referring the random value.
* @return a pixel point {x,y}.
*/
public int[] getRandomLeft(Activity activity) {
int x = 0;
int height = activity.getResources().getDisplayMetrics().heightPixels;
Random random = new Random();
int y = random.nextInt(height);
return new int[]{x, y};
}
/**
* Gets the random pixel points in the top direction of the screen. The value will be of {x,0} where x will be a random value.
* @param activity - activity from where you are referring the random value.
* @return a pixel point {x,y}.
*/
public int[] getRandomTop(Activity activity) {
int y = 0;
int width = activity.getResources().getDisplayMetrics().widthPixels;
Random random = new Random();
int x = random.nextInt(width);
return new int[]{x, y};
}
/**
* Gets the random pixel points in the right direction of the screen. The value will be of {screen_width,y} where y will be a random value.
* @param activity - activity from where you are referring the random value.
* @return a pixel point {x,y}.
*/
public int[] getRandomRight(Activity activity) {
int width = activity.getResources().getDisplayMetrics().widthPixels;
int height = activity.getResources().getDisplayMetrics().heightPixels;
int x = width ;
Random random = new Random();
int y = random.nextInt(height);
return new int[]{x, y};
}
/**
* Gets the random pixel points in the bottom direction of the screen. The value will be of {x,screen_height} where x will be a random value.
* @param activity - activity from where you are referring the random value.
* @return a pixel point {x,y}.
*/
public int[] getRandomBottom(Activity activity) {
int width = activity.getResources().getDisplayMetrics().widthPixels;
int height = activity.getResources().getDisplayMetrics().heightPixels;
int y = height ;
Random random = new Random();
int x = random.nextInt(width);
return new int[]{x, y};
}
/**
* Gets a random direction.
* @return one among LEFT,RIGHT,BOTTOM,TOP
*/
public Direction getRandomDirection() {
Direction[] allDirections = new Direction[]{Direction.LEFT,Direction.TOP,Direction.BOTTOM,Direction.RIGHT};
int index = new Random().nextInt(allDirections.length);
return (allDirections[index]);
}
/**
* Gets a random direction skipping the given direction.
* @param toSkip a direction which should not be returned by this method.
* @return one among LEFT,RIGHT,BOTTOM if TOP is provided as direction to skip,
* one among TOP,RIGHT,BOTTOM if LEFT is provided as direction to skip
* and so on.
*/
public Direction getRandomDirection(Direction toSkip) {
Direction[] allExceptionalDirections;
switch (toSkip) {
case LEFT:
allExceptionalDirections = new Direction[]{Direction.TOP,Direction.BOTTOM,Direction.RIGHT};
break;
case RIGHT:
allExceptionalDirections = new Direction[]{Direction.TOP,Direction.BOTTOM,Direction.LEFT};
break;
case BOTTOM:
allExceptionalDirections = new Direction[]{Direction.TOP,Direction.LEFT,Direction.RIGHT};
break;
case TOP:
allExceptionalDirections = new Direction[]{Direction.LEFT,Direction.BOTTOM,Direction.RIGHT};
break;
default:
allExceptionalDirections = new Direction[]{Direction.LEFT,Direction.TOP,Direction.BOTTOM,Direction.RIGHT};
}
int index = new Random().nextInt(allExceptionalDirections.length);
return (allExceptionalDirections[index]);
}
}
OverTheTopLayer This will help to fly emojis over some views
package com.example.hamidraza.flyinemojis;
import android.app.Activity;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.widget.FrameLayout;
import android.widget.ImageView;
import java.lang.ref.WeakReference;
public class OverTheTopLayer {
public static class OverTheTopLayerException extends RuntimeException {
public OverTheTopLayerException(String msg) {
super(msg);
}
}
private WeakReference<Activity> mWeakActivity;
private WeakReference<ViewGroup> mWeakRootView;
private FrameLayout mCreatedOttLayer;
private float mScalingFactor = 1.0f;
private int[] mDrawLocation = {0, 0};
private Bitmap mBitmap;
public OverTheTopLayer() {
}
/**
* To create a layer on the top of activity
*/
public OverTheTopLayer with(Activity weakReferenceActivity) {
mWeakActivity = new WeakReference<Activity>(weakReferenceActivity);
return this;
}
/**
* Draws the image as per the drawable resource id on the given location pixels.
*/
public OverTheTopLayer generateBitmap(Resources resources, int drawableResId, float mScalingFactor, int[] location) {
if (location == null) {
location = new int[]{0, 0};
} else if (location.length != 2) {
throw new OverTheTopLayerException("Requires location as an array of length 2 - [x,y]");
}
Bitmap bitmap = BitmapFactory.decodeResource(resources, drawableResId);
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, (int) (bitmap.getWidth() * mScalingFactor), (int) (bitmap.getHeight() * mScalingFactor), false);
this.mBitmap = scaledBitmap;
this.mDrawLocation = location;
return this;
}
public OverTheTopLayer setBitmap(Bitmap bitmap, int[] location) {
if (location == null) {
location = new int[]{0, 0};
} else if (location.length != 2) {
throw new OverTheTopLayerException("Requires location as an array of length 2 - [x,y]");
}
this.mBitmap = bitmap;
this.mDrawLocation = location;
return this;
}
/**
* Holds the scaling factor for the image.
*
* @param scale
* @return
*/
public OverTheTopLayer scale(float scale) {
if (scale <= 0) {
throw new OverTheTopLayerException("Scaling should be > 0");
}
this.mScalingFactor = scale;
return this;
}
/**
* Attach the OTT layer as the child of the given root view.
* @return
*/
public OverTheTopLayer attachTo(ViewGroup rootView) {
this.mWeakRootView = new WeakReference<ViewGroup>(rootView);
return this;
}
/**
* Creates an OTT.
* @return
*/
public FrameLayout create() {
if(mCreatedOttLayer != null) {
destroy();
}
if (mWeakActivity == null) {
throw new OverTheTopLayerException("Could not create the layer as not activity reference was provided.");
}
Activity activity = mWeakActivity.get();
if (activity != null) {
ViewGroup attachingView = null;
if (mWeakRootView != null && mWeakRootView.get() != null) {
attachingView = mWeakRootView.get();
} else {
attachingView = (ViewGroup) activity.findViewById(android.R.id.content);
}
ImageView imageView = new ImageView(activity);
imageView.setImageBitmap(mBitmap);
int minWidth = mBitmap.getWidth();
int minHeight = mBitmap.getHeight();
imageView.measure(View.MeasureSpec.makeMeasureSpec(minWidth, View.MeasureSpec.AT_MOST), View.MeasureSpec.makeMeasureSpec(minHeight, View.MeasureSpec.AT_MOST));
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) imageView.getLayoutParams();
if (params == null) {
params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT, Gravity.TOP);
imageView.setLayoutParams(params);
}
int xPosition = mDrawLocation[0];
int yPosition = mDrawLocation[1];
params.width = minWidth;
params.height = minHeight;
params.leftMargin = xPosition;
params.topMargin = yPosition;
imageView.setLayoutParams(params);
FrameLayout ottLayer = new FrameLayout(activity);
FrameLayout.LayoutParams topLayerParam = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT, Gravity.TOP);
ottLayer.setLayoutParams(topLayerParam);
ottLayer.addView(imageView);
attachingView.addView(ottLayer);
mCreatedOttLayer = ottLayer;
} else {
Log.e(OverTheTopLayer.class.getSimpleName(), "Could not create the layer. Reference to the activity was lost");
}
return mCreatedOttLayer;
}
/**
* Kills the OTT
*/
public void destroy() {
if (mWeakActivity == null) {
throw new OverTheTopLayerException("Could not create the layer as not activity reference was provided.");
}
Activity activity = mWeakActivity.get();
if (activity != null) {
ViewGroup attachingView = null;
if (mWeakRootView != null && mWeakRootView.get() != null) {
attachingView = mWeakRootView.get();
} else {
attachingView = (ViewGroup) activity.findViewById(android.R.id.content);
}
if (mCreatedOttLayer != null) {
attachingView.removeView(mCreatedOttLayer);
mCreatedOttLayer = null;
}
} else {
Log.e(OverTheTopLayer.class.getSimpleName(), "Could not destroy the layer as the layer was never created.");
}
}
/**
* Applies the animation to the image view present in OTT.
* @param animation
*/
public void applyAnimation(Animation animation) {
if(mCreatedOttLayer != null) {
ImageView drawnImageView = (ImageView) mCreatedOttLayer.getChildAt(0);
drawnImageView.startAnimation(animation);
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With