婚恋系统源码如何实现一个自定义悬浮窗

前言

我们婚恋系统源码的项目中一直有一个 提现功能,在实际的效果中就是一个可以拖动的红包View,点击后就是响应的逻辑

最近一个婚恋系统源码国际版客户就提出来想要使用,我当时还纳闷了,谷歌商店和外国客户吃这一套吗?不管了客户就是爷,开整.

这个红色的就是我们项目中的一个效果,好像是我们目前的Android老大哥封装的,代码有点乱,我没咋看明白.大家可以看到最后上划时卡住了一下,这个不用在意,这是投影软件的问题.

这个View在使用中有点问题,我们婚恋系统源码主页上面有一个banner,每当你手指拖动时只要上方banner切换,就会立马卡到最左或者最右.十分诡异.我们程序员是不可能容忍这种事情发生的,立马开始改造!!!

首先贴出来老代码,大家来琢磨琢磨

/**
 * 红包悬浮窗
 */
public class FloatDragView {

    // 屏幕的宽度 屏幕的高度
    private static int mScreenWidth = -1, mScreenHeight = -1;
    // 用于记录上一次的位置(坐标0对应x,坐标1对应y)
    private static int[] lastPosition;
    // 上下文
    private final Activity context;
    // 可拖动按钮(外层布局)
    private RelativeLayout mImageView;
    // 是否截断touch事件
    private boolean isIntercept = false;
    // 控件宽高
    private int mImageViewWidth, mImageViewHeight;
    // 控件相对屏幕左上角移动的位置
    private int relativeMoveX, relativeMoveY;

    /**
     * 初始化实例
     *
     * @param context
     */
    public FloatDragView(Activity context) {
        this.context = context;
        mScreenWidth = ScreenSizeUtils.getInstance(context).getScreenWidth();
        mScreenHeight = ScreenSizeUtils.getScreenHeight(context);
        lastPosition = new int[]{0, 0};
    }

    /**
     * @param context        上下文
     * @param mViewContainer 可拖动按钮要存放的对应的Layout
     * @param clickListener  可拖动按钮的点击事件
     */
    public void addFloatDragView(Activity context, RelativeLayout mViewContainer, View.OnClickListener clickListener) {
        // 设置宽高
        mImageViewWidth = ImageUtil.dp2px(context, 55);
        mImageViewHeight = mImageViewWidth * 136 / 107;
        // 获取拖动按钮
        mImageView = getFloatDragView(clickListener);
        if (!getChildA(mViewContainer)) {
            mViewContainer.addView(mImageView);
            // 设置拖动按钮,并添加在父布局里
            RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) mImageView.getLayoutParams();
            layoutParams.width = mImageViewWidth;
            layoutParams.height = mImageViewHeight;
            mImageView.setLayoutParams(layoutParams);
        }
    }

    /**
     * @param clickListener
     * @return 获取可拖动按钮的实例
     */
    private RelativeLayout getFloatDragView(View.OnClickListener clickListener) {
        if (mImageView != null && mImageView.getChildCount() != 0) {
            if (mImageView.getVisibility() != View.VISIBLE) {
                mImageView.setVisibility(View.VISIBLE);
            }
            return mImageView;
        } else {
            if (mImageView == null) {
                mImageView = new RelativeLayout(context);
                mImageView.setTag("123456789");
            }
            mImageView.setVisibility(View.VISIBLE);
            mImageView.setClickable(true);
            mImageView.setFocusable(true);
            if (clickListener != null) {
                mImageView.setOnClickListener(clickListener);
            }
            // 添加图片控件
            ImageView imageView = new ImageView(context);
            imageView.setClickable(false);
            imageView.setFocusable(false);
            imageView.setEnabled(false);
            imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
            imageView.setImageResource(R.mipmap.float_red_package);
            RelativeLayout.LayoutParams imageParams = new RelativeLayout.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
            mImageView.addView(imageView, imageParams);
            // 初始位置
            RelativeLayout.LayoutParams lpFeedback = new RelativeLayout.LayoutParams(
                    RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
            lpFeedback.setMargins(0, 0, 15, 250);
            lpFeedback.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
            lpFeedback.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
            lpFeedback.width = mImageViewWidth;
            lpFeedback.height = mImageViewHeight;
            mImageView.setLayoutParams(lpFeedback);
            // 设置拖动
            setFloatDragViewTouch(mImageView);
            return mImageView;
        }
    }

    /**
     * 可拖动按钮的touch事件
     *
     * @param floatDragView
     */
    @SuppressLint("ClickableViewAccessibility")
    private void setFloatDragViewTouch(final RelativeLayout floatDragView) {
        floatDragView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(final View v, MotionEvent event) {
                int action = event.getAction();
                switch (action) {
                    case MotionEvent.ACTION_DOWN:
                        isIntercept = false;
                        relativeMoveX = (int) event.getRawX();
                        relativeMoveY = (int) event.getRawY();
                        break;
                    case MotionEvent.ACTION_MOVE:
                        int dx = (int) event.getRawX() - relativeMoveX;
                        int dy = (int) event.getRawY() - relativeMoveY;
                        // 这里修复一些华为手机无法触发点击事件
                        int distance = (int) Math.sqrt(dx * dx + dy * dy);
                        // 此处稍微增加一些移动的偏移量,防止手指抖动,误判为移动无法触发点击时间
                        if (distance == 0) {
                            isIntercept = false;
                            Log.e("TAG", "isIntercept: ");
                            break;
                        }
                        isIntercept = true;
                        int left = v.getLeft() + dx;
                        int top = v.getTop() + dy;
                        int right = v.getRight() + dx;
                        int bottom = v.getBottom() + dy;
                        // 范围判断
                        if (left < 15) {
                            left = 15;
                            right = left + v.getWidth();
                        }
                        if (right > mScreenWidth - 15) {
                            right = mScreenWidth - 15;
                            left = right - v.getWidth();
                        }
                        if (top < 250) {
                            top = 250;
                            bottom = top + v.getHeight();
                        }
                        if (bottom > mScreenHeight - 250) {
                            bottom = mScreenHeight - 250;
                            top = bottom - v.getHeight();
                        }
                        Log.e("TAG", "onTouch: "+left+"   "+top+"   "+right+"   "+bottom);
                        v.layout(left, top, right, bottom);
                        relativeMoveX = (int) event.getRawX();
                        relativeMoveY = (int) event.getRawY();
                        break;
                    case MotionEvent.ACTION_UP:
                        if (isIntercept) {
                            // 每次移动都要设置其layout,不然由于父布局可能嵌套listview,
                            // 当父布局发生改变冲毁(如下拉刷新时)则移动的view会回到原来的位置
                            RelativeLayout.LayoutParams lpFeedback = new RelativeLayout.LayoutParams(
                                    RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
                            lpFeedback.setMargins(v.getLeft(), v.getTop(), 0, 0);
                            lpFeedback.width = mImageViewWidth;
                            lpFeedback.height = mImageViewHeight;
                            v.setLayoutParams(lpFeedback);
                            // 设置靠近边沿的
                            setImageViewNearEdge(v);
                        }
                        break;
                }
                return isIntercept;
            }
        });
    }

    /**
     * 将拖动按钮移动到边沿
     *
     * @param v
     */
    private void setImageViewNearEdge(final View v) {
        Log.e("TAG", "setImageViewNearEdge: "+v.getLeft());
        if (v.getLeft() < (mScreenWidth / 2)) {
            // 设置位移动画 向左移动控件位置
            final TranslateAnimation animation = new TranslateAnimation(0, -v.getLeft() + 15, 0, 0);
            animation.setDuration(400);// 设置动画持续时间
            animation.setRepeatCount(0);// 设置重复次数
            animation.setFillAfter(true);
            animation.setRepeatMode(Animation.ABSOLUTE);
            animation.setAnimationListener(new Animation.AnimationListener() {
                @Override
                public void onAnimationStart(Animation arg0) {
                    // TODO: 2017/3/1
                }

                @Override
                public void onAnimationRepeat(Animation arg0) {
                    // TODO: 2017/3/1
                }

                @Override
                public void onAnimationEnd(Animation arg0) {
                    v.clearAnimation();
                    RelativeLayout.LayoutParams lpFeedback = new RelativeLayout.LayoutParams(
                            RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
                    lpFeedback.setMargins(15, v.getTop(), 0, 0);
                    lpFeedback.width = mImageViewWidth;
                    lpFeedback.height = mImageViewHeight;
                    v.setLayoutParams(lpFeedback);
                    v.postInvalidateOnAnimation();
                    lastPosition[0] = 0;
                    lastPosition[1] = v.getTop();
                }
            });
            v.startAnimation(animation);
        } else {
            final TranslateAnimation animation = new TranslateAnimation(0,
                    mScreenWidth - v.getLeft() - v.getWidth() - 15, 0, 0);
            animation.setDuration(400);// 设置动画持续时间
            animation.setRepeatCount(0);// 设置重复次数
            animation.setRepeatMode(Animation.ABSOLUTE);
            animation.setFillAfter(true);
            animation.setAnimationListener(new Animation.AnimationListener() {
                @Override
                public void onAnimationStart(Animation arg0) {
                    // TODO: 2017/3/1
                }

                @Override
                public void onAnimationRepeat(Animation arg0) {
                    // TODO Auto-generated method stub
                }

                @Override
                public void onAnimationEnd(Animation arg0) {
                    v.clearAnimation();
                    RelativeLayout.LayoutParams lpFeedback = new RelativeLayout.LayoutParams(
                            RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
                    lpFeedback.setMargins(mScreenWidth - v.getWidth() - 15, v.getTop(), 0, 0);
                    lpFeedback.width = mImageViewWidth;
                    lpFeedback.height = mImageViewHeight;
                    v.setLayoutParams(lpFeedback);
                    v.postInvalidateOnAnimation();
                    lastPosition[0] = mScreenWidth - v.getWidth();
                    lastPosition[1] = v.getTop();
                }
            });
            v.startAnimation(animation);
        }
    }

    public void remove() {
        if (mImageView != null) {
            mImageView.removeAllViews();
            mImageView.setVisibility(View.GONE);
        }
    }

    private Boolean getChildA(View view) {
        Boolean a = false;
        if (view instanceof ViewGroup) {
            ViewGroup vp = (ViewGroup) view;
            for (int i = 0; i < vp.getChildCount(); i++) {
                View viewchild = vp.getChildAt(i);
                if (viewchild.getTag() != null && String.valueOf(viewchild.getTag()).equals("123456789")) {
                    return true;
                }
                a = a || getChildA(viewchild);
            }
        }
        return a;
    }
}

用法

image.png

直接new一个,然后调用addFloatDragView,有三个参数,第一个是上下文,第二个是RelativeLayout,第三个就是点击监听.

这个用法比较限制,你的根布局必须是RelativeLayout,不然没法用.你也可以去里面吧所有RelativeLayout替换成ConstraintLayout,你会发现这个View直接废掉,婚恋系统源码的移动动画不好使了,而且固定在左上角.这种情况不要在基础上改了,直接自定义都比修改省时间.

下面是我随便写的一个View

/**
 * 滑动悬浮窗
 */
public class TestImageView extends AppCompatImageView {

    private int mScreenWidth;
    private int mScreenHeight;
    private int mLastX;
    private int mLastY;
    private boolean isMove = false;
    private OnClickListener onClickListener;

    private Activity activity;

    public TestImageView(@NonNull @NotNull Context context) {
        super(context);
        init(context);
    }

    public TestImageView(@NonNull @NotNull Context context, @Nullable @org.jetbrains.annotations.Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context);

    }

    public TestImageView(@NonNull @NotNull Context context, @Nullable @org.jetbrains.annotations.Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);

    }

    public void setActivity(Activity activity) {
        this.activity = activity;
    }

    private void init(Context context) {
        mScreenWidth = ScreenSizeUtils.getInstance(context).getScreenWidth();
        mScreenHeight = ScreenSizeUtils.getScreenHeight(context);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        // 当前手指坐标
        int x = (int) event.getRawX();
        int y = (int) event.getRawY();

        switch (action) {
            case MotionEvent.ACTION_DOWN:
                isMove = false;
                break;
            case MotionEvent.ACTION_MOVE:
                isMove = true;
                int deltaX = x - mLastX; // x方向移动量
                int deltaY = y - mLastY; // y方向移动量
                int translationX = (int) (ViewHelper.getTranslationX(this) + deltaX); // x方向平移deltaX
                int translationY = (int) (ViewHelper.getTranslationY(this) + deltaY); // y方向平移deltaY
                ViewHelper.setTranslationX(this, translationX);
                ViewHelper.setTranslationY(this, translationY);


                break;
            case MotionEvent.ACTION_UP:
                /**
                 * 当手指点击后抬起,且未滑动就会触发点击监听,只要滑动1像素就会取消点击监听
                 */
                if (!isMove) {
                    if(onClickListener!=null){
                        onClickListener.onClick(this);
                    }
                }
                setImageViewNearEdge(this);
                break;
        }
        // 更新位置
        mLastX = x;
        mLastY = y;
        return true;
    }

    private void setImageViewNearEdge(final View v) {
        ViewPropertyAnimator viewPropertyAnimator = ViewPropertyAnimator.animate(this);
        //设置动画时间
        viewPropertyAnimator.setDuration(400);
        //当控件移动并且抬起手指时,判断这个控件最后的位置是偏左还是偏右
        if (mLastX < (mScreenWidth / 2)) {
            // 设置位移动画 向左移动控件位置
            viewPropertyAnimator.translationX(-v.getLeft() + 15);
        } else {
            // 设置位移动画 向右移动控件位置
            viewPropertyAnimator.translationX(mScreenWidth - v.getLeft() - v.getWidth() - 15);
        }

        viewPropertyAnimator.start();
    }

    @Override
    public void setOnClickListener(OnClickListener onClickListener) {
        this.onClickListener = onClickListener;
    }
}

对了,如果你要是用这个View你需要导入一个三方库 NineOldAndroids

官方解释: Android library for using the Honeycomb (Android 3.0) animation API on all versions of the platform back to 1.0!Animation prior to Honeycomb was very limited in what it could accomplish so in Android 3.x a new API was written. With only a change in imports, we are able to use a large subset of the new-style animation with exactly the same API.

大概意思就是能在低版本使用一些动画,其实我就是用它封装的API(懒)

麻了,还要处理滑动到婚恋系统源码页面上方的问题.唉!我们程序员不能说不!!! 继续改造

private void setImageViewNearEdge(final View v) {
    Log.e("TAG", "setImageViewNearEdge: " + mLastX + "   " + mLastY);
    ViewPropertyAnimator viewPropertyAnimator = ViewPropertyAnimator.animate(this);
    //设置动画时间
    viewPropertyAnimator.setDuration(400);
    //当控件移动并且抬起手指时,判断这个控件最后的位置是偏左还是偏右
    if (mLastX < (mScreenWidth / 2)) {
        // 设置位移动画 向左移动控件位置
        viewPropertyAnimator.translationX(-v.getLeft() + 15);
    } else {
        // 设置位移动画 向右移动控件位置
        viewPropertyAnimator.translationX(mScreenWidth - v.getLeft() - v.getWidth() - 15);
    }
    /**
     * 最后移动的位置超过定义的一个位置就进行Y轴移动
     * 判断上面
     */
    Log.e("TAG", "setImageViewNearEdge: "+getPaddingTop());
    if (mLastY < getMeasuredHeight() + ImageUtil.dp2px(activity, 25)) {
    //上面加上了状态栏的一个高度
        viewPropertyAnimator.translationY(-getTop() + getMeasuredHeight()/2);
    }
    /**
     * 最后移动的位置超过定义的一个位置就进行Y轴移动
     * 判断下面
     */
    if (mLastY > mScreenHeight - getMeasuredHeight()) {
        viewPropertyAnimator.translationY(mScreenHeight - v.getTop() - v.getHeight() * 2);
    }

    viewPropertyAnimator.start();
}

动画2.gif

其实还是有点小瑕疵,因为我这个判断,判断的是你手指按下的位置,其实我觉得婚恋系统源码应该判断这个view的中心点比较好,不过我太菜了,不知道咋算,如果有大佬知道,可以评论区告诉我一下

声明:本文由云豹科技转发自只是未遇见博客,如有侵权请联系作者删除

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值