Android中实现直播带货功能

前言:

这两年直播带货非常火,前段时间我们项目需求也提了这个功能,于是查看抖音、快手、淘宝的实现方式,结合咱们自己的项目,直播带货有2种场景:

1.一种是在单一页面进行商品购买.

2.一种是全局悬浮窗,在所有页面都可以去购物.

3.单一页面一个拖拽按钮就可以实现,全局悬浮窗比较麻烦,由于涉及到多个页面的跳转传递数据和回收问题。

4.单个页面的悬浮按钮,可以用一个自定义的可拖拽按钮来实现.

5.实现思路:

5.1 通过重写控件的onTouchEvent方法监听触摸效果.

5.2 通过view的setX和setY方法来实现移动.

5.3 使用属性动画来实现边缘吸附效果.

5.4 左右吸附功能:

ScreenUtils.dip2px(getContext(), 20)是控制左右边距的大小,通过dp转化为px,适配不同分辨率的机型。如果需要靠边吸附,更改为0即可,如果不需要自动吸附功能,也直接注释掉就可以了,使用十分简单方便。

5.5 可拖动的悬浮按钮

/**
 * @作者: njb
 * @时间: 2019/12/10 14:38
 * @描述: 可拖动的悬浮按钮
 */
@SuppressLint("AppCompatCustomView")
public  class DragFloatActionButton extends ImageView {
​
    private int parentHeight;
    private int parentWidth;
    private int lastX;
    private int lastY;
    private boolean isDrag;
​
    public DragFloatActionButton(Context context) {
        super(context);
    }
​
    public DragFloatActionButton(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
​
    public DragFloatActionButton(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
​
​
    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int rawX = (int) event.getRawX();
        int rawY = (int) event.getRawY();
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
                isDrag = false;
                setPressed(true);
                getParent().requestDisallowInterceptTouchEvent(true);
                lastX = rawX;
                lastY = rawY;
                ViewGroup parent;
                if (getParent() != null) {
                    parent = (ViewGroup) getParent();
                    parentHeight = parent.getHeight();
                    parentWidth = parent.getWidth();
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if (parentHeight <= 0 || parentWidth == 0) {
                    isDrag = false;
                    break;
                } else {
                    isDrag = true;
                }
                int dx = rawX - lastX;
                int dy = rawY - lastY;
                //这里修复一些华为手机无法触发点击事件
                int distance = (int) Math.sqrt(dx * dx + dy * dy);
                if (distance == 0) {
                    isDrag = false;
                    break;
                }
                float x = getX() + dx;
                float y = getY() + dy;
                //检测是否到达边缘 左上右下
                x = x < 0 ? 0 : x > parentWidth - getWidth() ? parentWidth - getWidth() : x;
                y = getY() < 0 ? 0 : getY() + getHeight() > parentHeight ? parentHeight - getHeight() : y;
                setX(x);
                setY(y);
                lastX = rawX;
                lastY = rawY;
                Rlog.d("aa", "isDrag=" + isDrag + "getX=" + getX() + ";getY=" + getY() + ";parentWidth=" + parentWidth);
                break;
            case MotionEvent.ACTION_UP:
                if (isDrag) {
                    //恢复按压效果
                    setPressed(false);
                    moveHide(rawX);
                }
                break;
            default:
                break;
        }
        //如果是拖拽则消耗事件,否则正常传递即可。
        return isDrag || super.onTouchEvent(event);
    }
​
    /**
     * 吸附动画
     * @param rawX
     */
    private void moveHide(int rawX) {
        if (rawX >= parentWidth / 2) {
            //靠右吸附
            animate().setInterpolator(new DecelerateInterpolator())
                    .setDuration(500)
                    .xBy(parentWidth - getWidth() - getX() - ScreenUtil.dip2px( 20))
                    .start();
        } else {
            //靠左吸附
            ObjectAnimator oa = ObjectAnimator.ofFloat(this, "x", getX(),
                    ScreenUtil.dip2px(20));
            oa.setInterpolator(new DecelerateInterpolator());
            oa.setDuration(500);
            oa.start();
​
        }
    }
}

6.单页面实现的效果截图如下:

img

点击按钮就可以进入直播购物界面,这里就不写了,很简单的一个跳转事件.后面会给出全局悬浮窗实现方案。

7.全局悬浮窗:

这里全局悬浮窗用的是EasyFloat这个第三方库,由于项目时间太赶,所以用的现成的方案,没有专门用系统的windowmanager, EasyFloat的github地址为: GitHub - princekin-f/EasyFloat: 🔥 EasyFloat:浮窗从未如此简单(Android可拖拽悬浮窗口,支持页面过滤、自定义动画,可设置单页面浮窗、前台浮窗、全局浮窗,浮窗权限按需自动申请...),这里有详细的使用说明和介绍.

7.1 导入方式:implementation 'com.github.princekin-f:EasyFloat:1.2.1'

7.2 如果你要在整个项目全局使用的话就可以直接在application中初始化,

​
**
 * @author: njb
 * @date: 2020/2/28 0028 12:35
 * @desc:
 */
public class App extends Application {
    //全局Context
    private static Context sContext;
​
    @Override
    public void onCreate() {
        super.onCreate();
        sContext = getApplicationContext();
        //初始化悬浮窗
        initEasyFloat();
    }
​
    private void initEasyFloat() {
        EasyFloat.init(this);
    }
​
​
    public static Context getContext() {
        return sContext;
    }
}

img

7.3 请求系统浮窗权限

loadPermissions();

img

/**
 * 请求悬浮窗权限
 */
private void loadPermissions() {
    if (PermissionUtils.checkPermission(this)) {
        String liveId = "100086";
        //初始化悬浮窗
        initFlowWindow(liveId);
    }else {
        final AlertDialog.Builder alert = new AlertDialog.Builder(this);
        alert.setMessage("使用浮窗功能,需要您授权悬浮窗权限。");
        alert.setNegativeButton("取消", ((dialog, which) -> dialog.dismiss()));
        alert.setPositiveButton("去开启", (dialog, which) -> {
            String liveId = "100086";
            initFlowWindow(liveId);
            dialog.dismiss();
        });
        alert.show();
    }
}

7.4 初始化悬浮窗布局,由于我们的需求是从直接跳转到购物页面,所以点击按钮回到直播间,这里只是给出悬浮窗的使用,具体的业务逻辑要根据需求自己去实现,当然回直播间后这个悬浮按钮也随之隐藏,如果需求不需要隐藏也可以设置不隐藏.

/**
 * 初始化悬浮窗布局
 * @param liveId
 */
private void initFlowWindow(String liveId) {
        if (!TextUtils.isEmpty(liveId)) {
            EasyFloat.with(this)
                    // 设置浮窗xml布局文件
                    .setLayout(R.layout.layout_float_window, view -> {
                        // view就是我们传入的浮窗xml布局
                        view.findViewById(R.id.go_to_image).setOnClickListener(view1 -> {
                            //回直播
                           startActivity(new Intent(Mainivity.this,LiveActivity.class))
                            //隐藏悬浮框
                            EasyFloat.hideAppFloat("live");
                            EasyFloat.dismissAppFloat("live");
                        });
                    })
                    // 设置浮窗显示类型,默认只在当前Activity显示,可选一直显示、仅前台显示
                    .setShowPattern(ShowPattern.FOREGROUND)
                    // 设置吸附方式,共15种模式,详情参考SidePattern
                    .setSidePattern(SidePattern.RESULT_HORIZONTAL)
                    // 设置浮窗的标签,用于区分多个浮窗
                    .setTag("live")
                    // 设置浮窗是否可拖拽
                    .setDragEnable(true)
                    // 设置浮窗的对齐方式和坐标偏移量
                    .setGravity(Gravity.END | Gravity.CENTER_VERTICAL, 0, 200)
                    // 设置宽高是否充满父布局,直接在xml设置match_parent属性无效
                    .setMatchParent(false, false)
                    // 设置Activity浮窗的出入动画,可自定义,实现相应接口即可(策略模式),无需动画直接设置为null
                    .setAnimator(new DefaultAnimator())
                    // 设置系统浮窗的出入动画,使用同上
                    .setAppFloatAnimator(new AppFloatDefaultAnimator()).show();
            EasyFloat.showAppFloat("live");
        } else {
            EasyFloat.hide();
            EasyFloat.hideAppFloat("live");
        }
}

7.5 实现的效果如下:

img

7.6 最后感谢刘振峰大佬的开源方案,全局悬浮窗介绍这么简单便捷.后面会尝试使用系统的windowmanager实现全局悬浮框.

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值