应用内悬浮按钮 可吸附 展开有动画 mini播放器

SystemUtils.java:

public class SystemUtils {

    public static int getStatusBarHeight(Context context) {
        int result = 0;
        int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
        if (resourceId > 0) {
            result = context.getResources().getDimensionPixelSize(resourceId);
        }
        return result;
    }

    public static int getScreenWidth(Context context) {
        int screenWith = -1;
        try {
            screenWith = context.getResources().getDisplayMetrics().widthPixels;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return screenWith;
    }

    public static int getScreenHeight(Context context) {
        int screenHeight = -1;
        try {
            screenHeight = context.getResources().getDisplayMetrics().heightPixels;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return screenHeight;
    }

}

布局文件:layout_music_floating_view.xml

是一个头像,加三个按钮:

效果如下:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/shape_music_floating_view"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:layout_gravity="center"
            android:orientation="horizontal">
            <com.example.m_evolution.View.CircleImageView
                android:id="@+id/iv_user_avatar"
                android:layout_width="50dp"
                android:layout_height="50dp"
                android:src="@drawable/user_demo"
                android:layout_gravity="center"/>
            <LinearLayout
                android:id="@+id/layout_more"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:orientation="horizontal">
                <RelativeLayout
                    android:id="@+id/button_play"
                    android:layout_width="40dp"
                    android:layout_height="40dp"
                    android:layout_gravity="center">
                    <ImageView
                        android:layout_width="20dp"
                        android:layout_height="20dp"
                        android:layout_centerInParent="true"
                        android:src="@drawable/icon_floating_view_pause"/>
                </RelativeLayout>
                <RelativeLayout
                    android:id="@+id/button_cut"
                    android:layout_width="40dp"
                    android:layout_height="40dp"
                    android:layout_gravity="center">
                    <ImageView
                        android:layout_width="20dp"
                        android:layout_height="20dp"
                        android:layout_centerInParent="true"
                        android:src="@drawable/icon_floating_view_cut"/>
                </RelativeLayout>
                <RelativeLayout
                    android:id="@+id/button_like"
                    android:layout_width="40dp"
                    android:layout_height="40dp"
                    android:layout_gravity="center">
                    <ImageView
                        android:layout_width="20dp"
                        android:layout_height="20dp"
                        android:layout_centerInParent="true"
                        android:src="@drawable/icon_floating_view_like"/>
                </RelativeLayout>
            </LinearLayout>
        </LinearLayout>
    </LinearLayout>

</android.support.constraint.ConstraintLayout>

 接着写一个FrameLayout来实现这个按钮,包括动画、吸附等:

import android.animation.ValueAnimator;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;

import com.example.m_evolution.R;
import com.example.m_evolution.Utils.SystemUtils;

public class FloatingMagnetView extends FrameLayout {

    public static final int MARGIN_EDGE = 13;
    private float mOriginalRawX;
    private float mOriginalRawY;
    private float mOriginalX;
    private float mOriginalY;
    private OnListener onListener;
    private static final int TOUCH_TIME_THRESHOLD = 150;
    private long mLastTouchDownTime;
    protected MoveAnimator mMoveAnimator;
    protected int mScreenWidth;
    private int mScreenHeight;
    private int mStatusBarHeight;

    //View
    private CircleImageView iv_user_avatar;
    private RelativeLayout button_play;
    private RelativeLayout button_cut;
    private RelativeLayout button_like;
    private LinearLayout layout_more;
    private int width_layout_more;
    private boolean is_init_layout_more = false;

    private String TAG = "FloatingMagnetView";

    public void setOnListener(OnListener onListener) {
        this.onListener = onListener;
    }

    public FloatingMagnetView(Context context) {
        this(context, null);
    }

    public FloatingMagnetView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public FloatingMagnetView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        inflate(context, R.layout.layout_music_floating_view, this);
        iv_user_avatar = findViewById(R.id.iv_user_avatar);
        button_play = findViewById(R.id.button_play);
        button_cut = findViewById(R.id.button_cut);
        button_like = findViewById(R.id.button_like);
        layout_more = findViewById(R.id.layout_more);
        init();
    }

    private void init() {
        layout_more.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                // TODO Auto-generated method stub
                if(!is_init_layout_more){
                    is_init_layout_more = true;
                    width_layout_more = layout_more.getWidth();
                    ViewGroup.LayoutParams params = layout_more.getLayoutParams();
                    params.width = 0;
                    layout_more.setLayoutParams(params);
                }
            }
        });
        mMoveAnimator = new MoveAnimator();
        mStatusBarHeight = SystemUtils.getStatusBarHeight(getContext());
        setClickable(true);
        updateSize();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event == null) {
            return false;
        }
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                changeOriginalTouchParams(event);
                updateSize();
                mMoveAnimator.stop();
                break;
            case MotionEvent.ACTION_MOVE:
                updateViewPosition(event);
                break;
            case MotionEvent.ACTION_UP:
                if (isOnClickEvent(event)) {
                    int[] position = new int[2];
                    iv_user_avatar.getLocationOnScreen(position);
                    if(mOriginalRawX>=position[0] && mOriginalRawX<=position[0]+iv_user_avatar.getWidth() && mOriginalRawY>=position[1] && mOriginalRawY<=position[1]+iv_user_avatar.getHeight()){
                        final ViewGroup.LayoutParams params = layout_more.getLayoutParams();
                        if(params.width == 0){

                            ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
                            valueAnimator.setDuration(500);
                            valueAnimator.start();//开始动画
                            //注册监听,监听属性值的变化,并设置给目标对象
                            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
                            {
                                @Override
                                public void onAnimationUpdate(ValueAnimator animation)
                                {
                                    float value = (float) animation.getAnimatedValue();
                                    params.width = (int)(value*width_layout_more);
                                    layout_more.setLayoutParams(params);
//                                    if(value == 1.0f){
                                        moveToEdge();
//                                    }
                                }
                            });
                        }
                        else{
                            ValueAnimator valueAnimator = ValueAnimator.ofFloat(1, 0);
                            valueAnimator.setDuration(500);
                            valueAnimator.start();//开始动画
                            //注册监听,监听属性值的变化,并设置给目标对象
                            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
                            {
                                @Override
                                public void onAnimationUpdate(ValueAnimator animation)
                                {
                                    float value = (float) animation.getAnimatedValue();
                                    params.width = (int)(value*width_layout_more);
                                    layout_more.setLayoutParams(params);
//                                    if(value == 0.0f){
                                        moveToEdge();
//                                    }
                                }
                            });
                        }
                        return true;
                    }
                    button_play.getLocationOnScreen(position);
                    if(mOriginalRawX>=position[0] && mOriginalRawX<=position[0]+button_play.getWidth() && mOriginalRawY>=position[1] && mOriginalRawY<=position[1]+button_play.getHeight()){
                        onListener.ClickPlay();
                        return true;
                    }
                    button_cut.getLocationOnScreen(position);
                    if(mOriginalRawX>=position[0] && mOriginalRawX<=position[0]+button_cut.getWidth() && mOriginalRawY>=position[1] && mOriginalRawY<=position[1]+button_cut.getHeight()){
                        onListener.ClickCut();
                        return true;
                    }
                    button_like.getLocationOnScreen(position);
                    if(mOriginalRawX>=position[0] && mOriginalRawX<=position[0]+button_like.getWidth() && mOriginalRawY>=position[1] && mOriginalRawY<=position[1]+button_like.getHeight()){
                        onListener.ClickLike();
                        return true;
                    }
                }
                else{
                    moveToEdge();
                }
                break;
        }
        return true;
    }

    protected boolean isOnClickEvent(MotionEvent event) {
        if(event.getRawX() == mOriginalRawX &&event.getRawY() == mOriginalRawY){
            return (System.currentTimeMillis() - mLastTouchDownTime < TOUCH_TIME_THRESHOLD);
        }
        return false;
    }

    private void updateViewPosition(MotionEvent event) {
        setX(mOriginalX + event.getRawX() - mOriginalRawX);
        // 限制不可超出屏幕高度
        float desY = mOriginalY + event.getRawY() - mOriginalRawY;
        if (desY < mStatusBarHeight) {
            desY = mStatusBarHeight;
        }
        if (desY > mScreenHeight - getHeight()) {
            desY = mScreenHeight - getHeight();
        }
        setY(desY);
    }

    private void changeOriginalTouchParams(MotionEvent event) {
        mOriginalX = getX();
        mOriginalY = getY();
        mOriginalRawX = event.getRawX();
        mOriginalRawY = event.getRawY();
        mLastTouchDownTime = System.currentTimeMillis();
    }

    protected void updateSize() {
        mScreenWidth = (SystemUtils.getScreenWidth(getContext()) - this.getWidth());
        mScreenHeight = SystemUtils.getScreenHeight(getContext());
    }

    public void moveToEdge() {
        updateSize();
        float moveDistance = isNearestLeft() ? MARGIN_EDGE : mScreenWidth - MARGIN_EDGE;
        mMoveAnimator.start(moveDistance, getY());
    }

    protected boolean isNearestLeft() {
        int middle = mScreenWidth / 2;
        return getX() < middle;
    }

    protected class MoveAnimator implements Runnable {

        private Handler handler = new Handler(Looper.getMainLooper());
        private float destinationX;
        private float destinationY;
        private long startingTime;

        void start(float x, float y) {
            this.destinationX = x;
            this.destinationY = y;
            startingTime = System.currentTimeMillis();
            handler.post(this);
        }

        @Override
        public void run() {
            if (getRootView() == null || getRootView().getParent() == null) {
                return;
            }
            float progress = Math.min(1, (System.currentTimeMillis() - startingTime) / 400f);
            float deltaX = (destinationX - getX()) * progress;
            float deltaY = (destinationY - getY()) * progress;
            move(deltaX, deltaY);
            if (progress < 1) {
                handler.post(this);
            }
        }

        private void stop() {
            handler.removeCallbacks(this);
        }
    }

    private void move(float deltaX, float deltaY) {
        setX(getX() + deltaX);
        setY(getY() + deltaY);
    }

    public interface OnListener {
        void ClickPlay();
        void ClickPause();
        void ClickCut();
        void ClickLike();
    }

}

接着编写一个类来管理这个按钮:

import android.app.Activity;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.support.v4.view.ViewCompat;
import android.view.Gravity;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;

public class MusicFloatingView{

    private FloatingMagnetView mEnFloatingView;
//    private static volatile MusicFloatingView mInstance;
    private FrameLayout mContainer;
    private Context context;

    public MusicFloatingView(Context context) {
        this.context = context;
    }

//    public static MusicFloatingView get() {
//        return mInstance;
//    }

    public MusicFloatingView remove() {
        new Handler(Looper.getMainLooper()).post(new Runnable() {
            @Override
            public void run() {
                if (mEnFloatingView == null) {
                    return;
                }
                if (ViewCompat.isAttachedToWindow(mEnFloatingView) && mContainer != null) {
                    mContainer.removeView(mEnFloatingView);
                }
                mEnFloatingView = null;
            }
        });
        return this;
    }

    private void ensureMiniPlayer(Context context) {
        synchronized (this) {
            if (mEnFloatingView != null) {
                return;
            }
            mEnFloatingView = new FloatingMagnetView(context.getApplicationContext());
            mEnFloatingView.setLayoutParams(getParams());
            addViewToWindow(mEnFloatingView);
        }
    }

    public MusicFloatingView add() {
        ensureMiniPlayer(context);
        return this;
    }

    public MusicFloatingView attach(Activity activity) {
        attach(getActivityRoot(activity));
        return this;
    }

    public MusicFloatingView attach(FrameLayout container) {
        if (container == null || mEnFloatingView == null) {
            mContainer = container;
            return this;
        }
        if (mEnFloatingView.getParent() == container) {
            return this;
        }
        if (mContainer != null && mEnFloatingView.getParent() == mContainer) {
            mContainer.removeView(mEnFloatingView);
        }
        mContainer = container;
        、、container.addView(mEnFloatingView);
        return this;
    }

    public MusicFloatingView detach(Activity activity) {
        detach(getActivityRoot(activity));
        return this;
    }

    public MusicFloatingView detach(FrameLayout container) {
        if (mEnFloatingView != null && container != null && ViewCompat.isAttachedToWindow(mEnFloatingView)) {
            container.removeView(mEnFloatingView);
        }
        if (mContainer == container) {
            mContainer = null;
        }
        return this;
    }

    public FloatingMagnetView getView() {
        return mEnFloatingView;
    }

    public MusicFloatingView layoutParams(ViewGroup.LayoutParams params) {
        if (mEnFloatingView != null) {
            mEnFloatingView.setLayoutParams(params);
        }
        return this;
    }

    public MusicFloatingView setOnListener(FloatingMagnetView.OnListener onListener) {
        if (mEnFloatingView != null) {
            mEnFloatingView.setOnListener(onListener);
        }
        return this;
    }

    private void addViewToWindow(final FloatingMagnetView view) {
        if (mContainer == null) {
            return;
        }
        mContainer.addView(view);
    }

    private FrameLayout.LayoutParams getParams() {
        FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
                RelativeLayout.LayoutParams.WRAP_CONTENT,
                RelativeLayout.LayoutParams.WRAP_CONTENT);
        params.gravity = Gravity.BOTTOM | Gravity.START;
        params.setMargins(13, params.topMargin, params.rightMargin, 56);
        return params;
    }

    private FrameLayout getActivityRoot(Activity activity) {
        if (activity == null) {
            return null;
        }
        try {
            return (FrameLayout) activity.getWindow().getDecorView().findViewById(android.R.id.content);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
原文地址:https://www.cnblogs.com/zhaozilongcjiajia/p/11227156.html