Android 音乐播放器悬浮窗

之前的项目中写了个音乐播放器,这里记录下悬浮窗相关的东西。

 

废话不多说,先上个效果图

效果图

1、自定义悬浮框布局

根据效果图可以看到悬浮窗 布局由一张封面图,一个圆形进度条以及4个按钮组成。

布局内容就不写了,很简单。

悬浮窗控件 :FloatLayout继承自FrameLayout,引入布局文件。

初始化 windowManager

mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics dm = new DisplayMetrics();
mWindowManager.getDefaultDisplay().getMetrics(dm);
//获取屏幕的宽度,用于后边计算吸边操作
screenWidth = dm.widthPixels;

然后通过 onInterceptTouchEvent 去处理按下、移动和抬起事件的分发拦截。这个是悬浮窗点击、移动的重点

刚开始打算是用onTouchEvent去处理触摸事件,经过验证发现并不能解决父布局和子View的点击冲突,于是试用了onInterceptTouchEvent 方法,发现这才是解决这里点击冲突正确的姿势。

public boolean onInterceptTouchEvent(MotionEvent ev) {

    //左上角为原点,获取相对屏幕的坐标
    x = (int) ev.getRawX();
    y = (int) ev.getRawY();

    //区分拖动和点击事件 ,判断是否需要拦截
    int action = ev.getAction();
    switch (action) {

        case MotionEvent.ACTION_DOWN:
            //获取相对于父view的坐标点
            mTouchStartX = ev.getX();
            mTouchStartY = ev.getY();
            break;

        case MotionEvent.ACTION_MOVE:
            //处理移动逻辑
            float moveStartX = ev.getX();
            float moveStartY = ev.getY();

            //移动的距离大于3,移动距位置
            if (Math.abs(mTouchStartX - moveStartX) > 3 && Math.abs(mTouchStartY - moveStartY) > 3) {
                mParams.x = (int) (x - mTouchStartX);
                mParams.y = (int) (y - mTouchStartY);

                mWindowManager.updateViewLayout(this, mParams);
                return super.onInterceptTouchEvent(ev);
            }
            break;

        case MotionEvent.ACTION_UP:

            float endX = ev.getRawX();
            //根据最终手指停留的地方,设置吸边效果
            if (endX > screenWidth / 2) {
                mParams.x = screenWidth - getWidth();
            } else {
                mParams.x = 0;
            }
            mWindowManager.updateViewLayout(this, mParams);

            float upX = ev.getX();
            float upY = ev.getY();

            if (Math.abs(upX - mTouchStartX) > 30 && Math.abs(upY - mTouchStartY) > 30){
                return true;
            }
            break;
    }

    return super.onInterceptTouchEvent(ev);
}

接下来就是正常处理按钮的点击事件了。因为设计的是当 菜单按钮显示的时候,点击封面图是跳转其他页面,所以需要写一个计时器 把菜单布局收起来。并且在菜单布局收起来的时候,注意调用吸边操作

/**
 * 吸边操作
 */
private void adscrop() {

    if (isWindowLive){

        if (x > screenWidth / 2){
            mParams.x = screenWidth;
        }else{
            mParams.x = 0;
        }

        mWindowManager.updateViewLayout(this,mParams);
    }
}

提供了一个悬浮窗的管理类,需要给悬浮窗定义一些公共方法

/**
 * 将小悬浮窗的参数传入,用于更新小悬浮窗的位置。
 * @param params
 */
public void setParams(WindowManager.LayoutParams params){
    this.mParams = params;
}

/**
 * 设置最大进度条
 * @param maxLength
 */
public void setMaxProgress(int maxLength){
    mFloatSeekbar.setMax(maxLength);
}

/**
 * 更新进度条
 * @param progress
 */
public void upDataProgress(int progress){
    mFloatSeekbar.setProgress(progress);
}

/**
 *隐藏
 */
public void hide(){
    mView.setVisibility(GONE);
}

/**
 * 显示
 */
public void show(){
    mView.setVisibility(VISIBLE);
}

/**
 * 开始/暂停
 * @param isStart
 */
public void startOrStop(boolean isStart){
    if (isStart){
        isPlaying = true;
        mIvStart.setImageResource(R.mipmap.float_image_stop);
    }else{
        isPlaying = false;
        mIvStart.setImageResource(R.mipmap.float_image_start);
    }
}
/**
 * 设置悬浮窗是否被销毁,如果被销毁,移除计时器
 * @param isLive
 */
public void windowState(boolean isLive){
    isWindowLive = isLive;
    if (isWindowLive){
        handler.removeCallbacks(runnable);
    }
}

2、FloatWindowManager 管理类

/**
 * 创建悬浮窗
 * @param context
 */
public static void creatFloatWindow(Context context){
    mParams = new WindowManager.LayoutParams();
    mWindowManager = getWindowManager(context);
    mFloatLayout = new FloatLayout(context);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        mParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
    } else {
        mParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
    }

    //设置图片格式,透明背景效果
    mParams.format = PixelFormat.RGBA_8888;
    //设置不可聚焦
    mParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
    //设置位置左上方
    mParams.gravity = Gravity.START | Gravity.TOP;

    DisplayMetrics dm = new DisplayMetrics();
    //取得窗口属性
    mWindowManager.getDefaultDisplay().getMetrics(dm);
    //窗口的宽度
    int screenWidth = dm.widthPixels;
    //窗口高度
    int screenHeight = dm.heightPixels;
    //以屏幕左上角为原点,设置x、y初始值,相对于gravity
    mParams.x = screenWidth;
    mParams.y = screenHeight / 2 + screenWidth / 3;

    //设置悬浮窗口长宽数据
    mParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
    mParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
    mFloatLayout.setParams(mParams);
    mWindowManager.addView(mFloatLayout, mParams);
}
/**
 * 移除悬浮窗
 */
public static void removeFloatWindowManager() {

    boolean isAttach = true;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        isAttach = mFloatLayout.isAttachedToWindow();
    }
    if (mHasShown && isAttach && mWindowManager != null)
        mWindowManager.removeView(mFloatLayout);

    mHasShown = false;
    mFloatLayout.windowState(false);
}
/**
 *返回已创建的WindowManager。
 * @param context
 * @return
 */
private static WindowManager getWindowManager(Context context) {
    if (mWindowManager == null){
        mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    }
    return mWindowManager;
}
因为设计的是音乐播放器,,所以播放功能应该放在service,悬浮窗的控制也放在service中,所以需要提供公共方法
public static void hide() {
    if (mHasShown)
        mFloatLayout.hide();
    mHasShown = false;
}

public static void show() {
    if (!mHasShown)
        mFloatLayout.show();
    mHasShown = true;
    mFloatLayout.windowState(true);

}

public static void setMaxProgress(int length){
    if (mHasShown || mFloatLayout != null)
        mFloatLayout.setMaxProgress(length);
}

public static void updataProgress(int progress) {
    if (mHasShown || mFloatLayout != null)
        mFloatLayout.upDataProgress(progress);
}

public static void startMusic(boolean isStart){
    mFloatLayout.startOrStop(isStart);
}

最近借鉴了一下朋友的设计 重新 封装了一个播放器,有兴趣的朋友可以探讨一下

XAudioPlayer: 音频播放器 (gitee.com)

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值