Android 模拟消息悬浮通知(只适用于应用内--ViewDragHelper,不需要权限的悬浮Notification)

package com.zzm.ndkandmaterialdesigndemo;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.os.CountDownTimer;
import android.util.Log;
import android.view.View;
import android.view.animation.LinearInterpolator;

public class MessageRemindUtil {
    public static float moveToPosition = -200f;
    public static int moveDownDuration = 200;
    public static int moveUpDuration = 200;
    public static int disappearTime = 4000;
    public static ObjectAnimator animator;
    public static CountDownTimer countDownTimer;
    private static View target;
    private static MoveUpEndListener moveUpEndListener;

    private static void moveUp(int moveUpDuration, AnimatorListenerAdapter animatorListenerAdapter) {
        float start = target.getY();
        animator = ObjectAnimator.ofFloat(target, "TranslationY", start, moveToPosition);
        animator.setDuration(moveUpDuration);
        animator.setInterpolator(new LinearInterpolator());
        if (animatorListenerAdapter != null) {
            animator.addListener(animatorListenerAdapter);
        } else {
            animator.addListener(new NormalMoveUpListener());
        }
        animator.start();
    }

    private static void moveDown(int moveDownDuration) {
        float start = target.getY();
        target.setVisibility(View.VISIBLE);
        animator = ObjectAnimator.ofFloat(target, "TranslationY", start, 0f);
        animator.setDuration(moveDownDuration);
        animator.setInterpolator(new LinearInterpolator());
        animator.start();
    }

    public static void initial(View view, float moveToPosition1, int moveDownDuration, int moveUpDuration, int disappearTime1) {
        target = view;
        moveToPosition = moveToPosition1;
        disappearTime = disappearTime1;
        float start = view.getY();
        animator = ObjectAnimator.ofFloat(view, "TranslationY", start, moveToPosition);
        animator.start();
    }


    public static void messageRemind() {
        //重置计时器
        if (countDownTimer != null)
            countDownTimer.cancel();
        countDownTimer = new MyCountDownTimer(disappearTime, 1000);
        countDownTimer.start();
        //3种情况 1.正在移动 2.已经隐藏了 3已经显示
        if (animator.isRunning()) {
            // 1  停止运行 隐藏控件 移动到 原始位置 然后 在 move  down
            animator.cancel();
            target.setVisibility(View.GONE);
            moveUp(0, new UnNormalMoveUpListener());
        } else if (target.getVisibility() != View.VISIBLE) {
            // 2 正常 move down
            moveDown(moveDownDuration);
        } else if (target.getVisibility() == View.VISIBLE) {
            // 3 隐藏 控件 向上移动,然后正常向移动
            target.setVisibility(View.GONE);
            moveUp(0, new UnNormalMoveUpListener());
        }
    }

    //跳转到其他界面时不要显示消息提醒
    public static void cancelMessageRemind() {
        //取消计时
        countDownTimer.cancel();
        //2种情况 1.正在移动  3已经显示
        if (animator.isRunning()) {
            // 1   停止运行 隐藏控件 移动到 原始位置
            animator.cancel();
            target.setVisibility(View.GONE);
            moveUp(0, null);
        } else if (target.getVisibility() == View.VISIBLE) {
            // 2   隐藏 控件 向上移动
            target.setVisibility(View.GONE);
            moveUp(0, null);
        }
    }

    //滑动floatView来提前让他moveup
    public static void moveOrClickFloatMessageViewToMoveUp() {
        countDownTimer.cancel();
        moveUp(200, new MoveFloatMessageVeiwMoveUpListener());
    }

    public static void setMoveUpEndListener(MoveUpEndListener moveUpEndListener1) {
        moveUpEndListener = moveUpEndListener1;
    }

    //销毁计时器,和一些对象,避免内存泄露
    public static void destroyAnything() {
        if (countDownTimer != null) {
            countDownTimer.cancel();
            countDownTimer = null;
        }
        target = null;
        animator = null;
    }

    public interface MoveUpEndListener {
        void floatMessageViewContentViewResume();
    }


    private static class UnNormalMoveUpListener extends AnimatorListenerAdapter {
        @Override
        public void onAnimationEnd(Animator animation) {
            super.onAnimationEnd(animation);
            if (target.getVisibility() == View.VISIBLE)
                target.setVisibility(View.GONE);
            moveDown(moveDownDuration);
        }
    }

    private static class NormalMoveUpListener extends AnimatorListenerAdapter {
        @Override
        public void onAnimationEnd(Animator animation) {
            super.onAnimationEnd(animation);
            if (target.getVisibility() == View.VISIBLE)
                target.setVisibility(View.GONE);
        }
    }

    private static class MoveFloatMessageVeiwMoveUpListener extends AnimatorListenerAdapter {
        @Override
        public void onAnimationEnd(Animator animation) {
            super.onAnimationEnd(animation);
            if (target.getVisibility() == View.VISIBLE)
                target.setVisibility(View.GONE);
            if (moveUpEndListener != null)
                moveUpEndListener.floatMessageViewContentViewResume();
        }
    }

    private static class MyCountDownTimer extends CountDownTimer {

        /**
         * @param millisInFuture    The number of millis in the future from the call
         *                          to {@link #start()} until the countdown is done and {@link #onFinish()}
         *                          is called.
         * @param countDownInterval The interval along the way to receive
         *                          {@link #onTick(long)} callbacks.
         */
        public MyCountDownTimer(long millisInFuture, long countDownInterval) {
            super(millisInFuture, countDownInterval);
        }

        @Override
        public void onTick(long millisUntilFinished) {
            Log.i("zengzeming", "onTick : " + millisUntilFinished / 1000);
        }

        @Override
        public void onFinish() {
            moveUp(moveUpDuration, null);
        }
    }

}
package com.zzm.ndkandmaterialdesigndemo;

import android.animation.FloatEvaluator;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Point;
import android.support.v4.widget.ViewDragHelper;
import android.support.v7.widget.CardView;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

/**
 * Date : 2017/12/21  9:28
 * Author : 22m
 * Description :
 * onFinishInflate
 * onMeasure
 * onSizeChanged
 * onLayout
 */

public class FloatMessageView extends CardView implements MessageRemindUtil.MoveUpEndListener {
    private ViewDragHelper viewDragHelper;
    private View dragedView;
    private Point dragedViewPositionPoint;
    private boolean manualMoved;
    private int dragedViewWidth;
    private int floatMessageViewWidth;
    private int paddingLeft;
    private int paddingRight;

    public FloatMessageView(Context context) {
        super(context);
        initial();
    }

    public FloatMessageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initial();
    }

    public FloatMessageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initial();
    }

    private void initial() {
        //初始化viewdraghelper(绑定要反馈的布局)等
        viewDragHelper = ViewDragHelper.create(this, 1.0f, new ViewDragHelperCallback());
        MessageRemindUtil.setMoveUpEndListener(this);
        dragedViewPositionPoint = new Point();
        manualMoved = false;
        dragedViewWidth = 0;
        floatMessageViewWidth = 0;
        paddingLeft = 0;
        paddingRight = 0;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //触摸事件传递给viewdraghelper来处理
        viewDragHelper.processTouchEvent(event);
        return true;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        //拦截事件交给viewdraghelper来处理
        return viewDragHelper.shouldInterceptTouchEvent(ev);
    }

    @Override
    protected void onFinishInflate() {
        //布局加载完调用,可以得到布局里面的控件
        super.onFinishInflate();
        dragedView = getChildAt(0);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        //第一次布局分配尺寸的时候调用,以及布局变化的时候调用
        super.onSizeChanged(w, h, oldw, oldh);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        //确定了布局位置的时候调用
        super.onLayout(changed, left, top, right, bottom);
        // getTop(getLeft)永远都是第一次加载布局控件的原始y值
        dragedViewPositionPoint.x = dragedView.getLeft();
        dragedViewPositionPoint.y = dragedView.getTop();
        dragedViewWidth = dragedView.getMeasuredWidth();
        floatMessageViewWidth = getMeasuredWidth();
        paddingLeft = getPaddingLeft();
        paddingRight = getPaddingRight();
    }

    @Override
    public void floatMessageViewContentViewResume() {
        //移动到原位透明度也设置为原始值
        ObjectAnimator animator = ObjectAnimator.ofFloat(dragedView, "TranslationX", 0f, 0f);
        animator.setDuration(0);
        animator.start();
        dragedView.setAlpha(1f);
    }


    public void setDragedViewClickInformation(Object object) {
        dragedView.setTag(object);
        ((TextView) dragedView).setText((CharSequence) object);
    }

    public void setDragedViewClickListener(OnClickListener onClickListener) {
        dragedView.setOnClickListener(onClickListener);
    }

    @Override
    public void computeScroll() {
        //更新滑动的一些相关数据
        super.computeScroll();
        //判断是否正在移动
        if (viewDragHelper.continueSettling(true)) {
            invalidate();
        } else {
            if (manualMoved && dragedView.getX() != dragedViewPositionPoint.x) {
                MessageRemindUtil.moveOrClickFloatMessageViewToMoveUp();
            }
            manualMoved = false;
        }
    }

    private class ViewDragHelperCallback extends ViewDragHelper.Callback {

        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            //可以确定哪个view能够响应手指的触摸事件
            return true;
        }

        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            //控制水平移动的响应
            return left;
        }

        @Override
        public int clampViewPositionVertical(View child, int top, int dy) {
            //控制垂直移动的响应
            return dragedViewPositionPoint.y;
        }

        @Override
        public int getViewHorizontalDragRange(View child) {
            return floatMessageViewWidth - paddingLeft - paddingRight;
        }

        @Override
        public int getViewVerticalDragRange(View child) {
            return super.getViewVerticalDragRange(child);
        }

        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            //当手指触摸控件后离开控件的时候方法的调用
            super.onViewReleased(releasedChild, xvel, yvel);
            int x = dragedViewPositionPoint.x;
            int y = dragedViewPositionPoint.y;
            float dragViewX = releasedChild.getX();
            if (releasedChild == dragedView) {
                if (dragViewX > dragedViewWidth / 3 || (xvel > 500 && dragViewX > 0)) {
                    x = floatMessageViewWidth - paddingLeft - paddingRight;
                } else if (dragViewX < -dragedViewWidth / 3 || (xvel < -500 && dragViewX < 0)) {
                    x = -dragedViewWidth;
                }
                //此方法针对释放的view,模拟手指拖动的view 的动作(只能在onViewReleased方法中调用)
                viewDragHelper.settleCapturedViewAt(x, y);
                //更新绘制的一些数据
                invalidate();
            }
        }

        @Override
        public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
            // drag view  位置改变的时候 可以设置透明的操作
            super.onViewPositionChanged(changedView, left, top, dx, dy);
            manualMoved = true;
            FloatEvaluator floatEvaluator = new FloatEvaluator();
            float total = floatMessageViewWidth / 2;
            float current = Math.abs(left);
            current = current > total ? total : current;
            float ratio = current / total;
            float alpha = floatEvaluator.evaluate(ratio, 1, 0);
            dragedView.setAlpha(alpha);
        }
    }

    /**
     * 使用步骤:
     * 1.找到控件
     * 2.MessageRemindUtil初始化
     * 3.设置DragedView 的点击监听器
     * 4.设置DragedView的点击可以使用的信息
     * 5.弹出悬浮view
     * 6.  4-->5-->4-->5-->4-->5
     */

}

猜你喜欢

转载自my.oschina.net/u/2987490/blog/1592719