创建Facebook Live Reactions动画叠加

时间:2017-08-16 11:38:25

标签: android facebook android-layout android-animation android-design-library

我目前正在使用消息传递应用程序,我希望将新用户显示为动态飞行图标,例如facebook实时浮动反应。任何人都可以启发我应该遵循的流程或任何有用的图书馆链接吗?

我已经尝试浏览并检查了一些图书馆链接,例如this1this2,但这些与facebook live reaction pack不相似。sample image

2 个答案:

答案 0 :(得分:1)

我也问过您同样的问题,但是我没有找到与Android类似的东西,所以我决定自己做。

我在Android Studio中创建了这个项目,该项目基于我在YouTube上找到的Swift教程,您可以通过点击here来看到它。

我使用Path类绘制气泡或图像(ImageView)所遵循的路径,并使用ObjectAnimator通过该路径初始化动画。

最后,我使用Rand来随机化路径的值,以使其外观与Facebook Live更相似,而不仅是沿着所有气泡或图像遵循相同的路径。

以下是我在Android Studio中的项目演示的视频:Here!

答案 1 :(得分:1)

我已经通过修改旧代码实现了这一点,这是我的解决方案。您可以更改表情符号的方向和数量,也可以使其在任何视图上方或下方飞行。

主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>       

主要活动

    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类

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;
    }
}

方向

     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 这将有助于使表情符号飞过某些视图

    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);[enter image description here][1]
            drawnImageView.startAnimation(animation);
        }
    }
}