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初始值,相对于gravitymParams.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)


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部