如何实现悬浮窗,看直播软件开发怎么做

使用阿里云直播sdk demo进行直播软件开发,支持直播,点播 视频直播与悬浮窗小窗口无缝衔接切换(一般拉流格式 rtmp m3u8等)
直播点播提示,监听,各种状态返回以及各种提示
视频播放基础库 支持全屏,快进,手势基础操作
全局视频小窗口 权限判断,高斯模糊背景(可以加深颜色)
飘心效果,还有列表单例播放视频 ,列表全屏视频,视频支持手势,进度,亮度,声音

这里对直播软件开发实现直播与悬浮窗直播无缝切换说一下实现原理:

1 ,直播组件做成单例,通过addview的形式,在悬浮窗的framelayout上增加直播view,切换正常陌生也是在原页面留下的framelayout站位增加这个直播view

/**
 * Description:初始化直播弹窗工具
 * Created by PangHaHa on 18-7-18.
 * Copyright (c) 2018 PangHaHa All rights reserved.
 */
public class LiveUtils {
 
    //布局参数.
    private static WindowManager.LayoutParams params;
    //实例化的WindowManager.
    private static WindowManager windowManager;
    private static int statusBarHeight =-1;
    private static FrameLayout toucherLayout;
    private static ImageView imageViewClose;
 
    private static int count = 0;//点击次数
    private static long firstClick = 0;//第一次点击时间
    private static long secondClick = 0;//第二次点击时间
 
    private static float start_X = 0;
    private static float start_Y = 0;
 
 
    // 记录上次移动的位置
    private static float lastX = 0;
    private static float lastY = 0;
    private static int offset;
    // 是否是移动事件
    private static boolean isMoved = false;
    /**
     * 两次点击时间间隔,单位毫秒
     */
    private static final int totalTime = 1000;
 
    private static boolean isInit = true;
 
 
    public static void initLive(final Context context ){
        try {
 
            windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
            //赋值WindowManager&LayoutParam.
            params = new WindowManager.LayoutParams();
            //设置type.系统提示型窗口,一般都在应用程序窗口之上.
            if (Build.VERSION.SDK_INT >= 26) {//8.0新特性
                params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
            } else {
                params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
            }
            //设置效果为背景透明.
            params.format = PixelFormat.RGBA_8888;
            //设置flags.不可聚焦及不可使用按钮对悬浮窗进行操控.
            params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
            //设置窗口坐标参考系
            params.gravity = Gravity.LEFT | Gravity.TOP;
            //用于检测状态栏高度.
            int resourceId = context.getResources().getIdentifier("status_bar_height",
                    "dimen","android");
            if (resourceId > 0) {
                statusBarHeight = context.getResources().getDimensionPixelSize(resourceId);
            }
            offset = DensityUtil.dp2px(context, 2);//移动偏移量
            //设置原点
            params.x = getScreenWidth(context) - DensityUtil.dp2px(context, 170);
            params.y = getScreenHeight(context) - DensityUtil.dp2px(context, 100+72) ;
            //设置悬浮窗口长宽数据.
            params.width = DensityUtil.dp2px(context, 180);
            params.height = DensityUtil.dp2px(context, 100);
 
            //获取浮动窗口视图所在布局.
            toucherLayout = new FrameLayout(context);
            AliyunVodPlayerView playerView = new AliyunVodPlayerView(context);
            playerView.initVideoView(true);
            playerView.setAutoPlay(true);
            playerView.setKeepScreenOn(true);
            playerView.setTitleBarCanShow(false);
            playerView.setControlBarCanShow(false);
            AliyunLocalSource.AliyunLocalSourceBuilder alsb = new AliyunLocalSource.AliyunLocalSourceBuilder();
            alsb.setSource("http://ivi.bupt.edu.cn/hls/cctv3hd.m3u8");
            AliyunLocalSource localSource = alsb.build();
            playerView.setLiveSource(localSource);
 
            toucherLayout.addView(playerView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.MATCH_PARENT));
 
            imageViewClose = new ImageView(context);
            imageViewClose.setImageResource(R.drawable.icon_close);
            FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
                    DensityUtil.dp2px(context, 16), DensityUtil.dp2px(context, 16));
            layoutParams.gravity = Gravity.TOP | Gravity.RIGHT;
            layoutParams.rightMargin = DensityUtil.dp2px(context, 3);
            layoutParams.topMargin = DensityUtil.dp2px(context, 3);
            imageViewClose.setLayoutParams(layoutParams);
 
            toucherLayout.addView(imageViewClose,layoutParams);
 
 
            //添加toucherlayout
            if(isInit) {
                windowManager.addView(toucherLayout,params);
            } else {
                windowManager.updateViewLayout(toucherLayout,params);
            }
 
            //主动计算出当前View的宽高信息.
            toucherLayout.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
 
            //处理touch
            toucherLayout.setOnTouchListener(new View.OnTouchListener() {
                @Override
                public boolean onTouch(View view, MotionEvent event) {
 
                    switch (event.getAction()) {
                        case MotionEvent.ACTION_DOWN:
                            isMoved = false;
                            // 记录按下位置
                            lastX = event.getRawX();
                            lastY = event.getRawY();
 
                            start_X = event.getRawX();
                            start_Y = event.getRawY();
                            break;
                        case MotionEvent.ACTION_MOVE:
                            isMoved = true;
                            // 记录移动后的位置
                            float moveX = event.getRawX();
                            float moveY = event.getRawY();
                            // 获取当前窗口的布局属性, 添加偏移量, 并更新界面, 实现移动
                            params.x += (int) (moveX - lastX);
                            params.y += (int) (moveY - lastY);
                            if (toucherLayout!=null){
                                windowManager.updateViewLayout(toucherLayout,params);
                            }
                            lastX = moveX;
                            lastY = moveY;
                            break;
                        case MotionEvent.ACTION_UP:
 
                            float fmoveX = event.getRawX();
                            float fmoveY = event.getRawY();
 
                            if (Math.abs(fmoveX-start_X)<offset && Math.abs(fmoveY-start_Y)<offset){
                                isMoved = false;
 
                                Intent intent = new Intent(context,MainActivity.class);
                                context.startActivity(intent);
 
 
                            }else {
                                isMoved = true;
                            }
                            break;
                    }
                        // 如果是移动事件, 则消费掉; 如果不是, 则由其他处理, 比如点击
                    return isMoved;
                }
 
            });
 
            //删除
            imageViewClose.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    remove(context);
                }
            });
        }catch (Exception e){
            e.printStackTrace();
        }
 
        isInit = false;
    }
 
    private static void remove(Context context) {
        if(windowManager != null && toucherLayout != null) {
            windowManager.removeView(toucherLayout);
            isInit = true;
        }
    }
 
    /**
     * 获取屏幕宽度(px)
     */
    private static int getScreenWidth(Context context) {
        return context.getResources().getDisplayMetrics().widthPixels;
    }
    /**
     * 获取屏幕高度(px)
     */
    private static int getScreenHeight(Context context){
        return context.getResources().getDisplayMetrics().heightPixels;
    }
 
}

2,对弹窗初始化时权限的判断

 private void showLiveWindow() {
        if (Build.VERSION.SDK_INT >= 23) {
            if (!Settings.canDrawOverlays(getContext())) {
                //没有悬浮窗权限,跳转申请
                Toast.makeText(getApplicationContext(), "请开启悬浮窗权限", Toast.LENGTH_LONG).show();
                //魅族不支持直接打开应用设置
                if (!MEIZU.isMeizuFlymeOS()) {
                    Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));
                    startActivityForResult(intent, 0);
                } else {
                    Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
                    startActivityForResult(intent, 0);
                }
            } else {
                LiveUtils.initLive(MainActivity.this);
                finish();
            }
        } else {
            //6.0以下 只有MUI会修改权限
            if (MIUI.rom()) {
                if (PermissionUtils.hasPermission(getContext())) {
                    LiveUtils.initLive(MainActivity.this);
                    finish();
                } else {
                    MIUI.req(getContext());
                }
            } else {
                LiveUtils.initLive(MainActivity.this);
                finish();
            }
        }
 
    }

下面是判断各种rom机型工具类

 
import android.os.Build;
import android.text.TextUtils;
 
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
 
public class OsUtils {
 
    public static final String ROM_MIUI = "MIUI";
    public static final String ROM_EMUI = "EMUI";
    public static final String ROM_FLYME = "FLYME";
    public static final String ROM_OPPO = "OPPO";
    public static final String ROM_SMARTISAN = "SMARTISAN";
    public static final String ROM_VIVO = "VIVO";
    public static final String ROM_QIKU = "QIKU";
 
    private static final String KEY_VERSION_MIUI = "ro.miui.ui.version.name";
    private static final String KEY_VERSION_EMUI = "ro.build.version.emui";
    private static final String KEY_VERSION_OPPO = "ro.build.version.opporom";
    private static final String KEY_VERSION_SMARTISAN = "ro.smartisan.version";
    private static final String KEY_VERSION_VIVO = "ro.vivo.os.version";
 
    private static String sName;
    private static String sVersion;
 
    public static boolean isEmui() {
        return check(ROM_EMUI);
    }
 
    public static boolean isMiui() {
        return check(ROM_MIUI);
    }
 
    public static boolean isVivo() {
        return check(ROM_VIVO);
    }
 
    public static boolean isOppo() {
        return check(ROM_OPPO);
    }
 
    public static boolean isFlyme() {
        return check(ROM_FLYME);
    }
 
    public static boolean is360() {
        return check(ROM_QIKU) || check("360");
    }
 
    public static boolean isSmartisan() {
        return check(ROM_SMARTISAN);
    }
 
    public static String getName() {
        if (sName == null) {
            check("");
        }
        return sName;
    }
 
    public static String getVersion() {
        if (sVersion == null) {
            check("");
        }
        return sVersion;
    }
 
    public static boolean check(String rom) {
        if (sName != null) {
            return sName.equals(rom);
        }
 
        if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_MIUI))) {
            sName = ROM_MIUI;
        } else if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_EMUI))) {
            sName = ROM_EMUI;
        } else if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_OPPO))) {
            sName = ROM_OPPO;
        } else if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_VIVO))) {
            sName = ROM_VIVO;
        } else if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_SMARTISAN))) {
            sName = ROM_SMARTISAN;
        } else {
            sVersion = Build.DISPLAY;
            if (sVersion.toUpperCase().contains(ROM_FLYME)) {
                sName = ROM_FLYME;
            } else {
                sVersion = Build.UNKNOWN;
                sName = Build.MANUFACTURER.toUpperCase();
            }
        }
        return sName.equals(rom);
    }
 
    public static String getProp(String name) {
        String line = null;
        BufferedReader input = null;
        try {
            Process p = Runtime.getRuntime().exec("getprop " + name);
            input = new BufferedReader(new InputStreamReader(p.getInputStream()), 1024);
            line = input.readLine();
            input.close();
        } catch (IOException ex) {
            return null;
        } finally {
            if (input != null) {
                try {
                    input.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return line;
    }
}
 

以上就是直播软件开发,如何实现直播小窗播放的相关内容。

声明:本文由云豹科技转发自庞哈哈12138博客,如有侵权请联系作者删除

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值