首页 > 系统 > Android > 正文

android实现可自由移动、监听点击事件的悬浮窗

2019-10-21 21:34:02
字体:
来源:转载
供稿:网友

最近因为项目需要,自己实现了个可以自由移动,并且长按可以跳出一个控制播放的,大的悬浮窗

好,开始吧。首先我们先聊权限,悬浮窗需要在manifest中声明一个权限:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

然后呢,嗯,我们来讲讲关于悬浮窗实现的原理。

在Andriod中,所有的界面元素都要通过windowmanger来实现,像Activity、Fragment等等这些也是在其上实现。因此,我们的悬浮窗自然要通过这个实现。

这个项目中,我们自定义了两个悬浮窗view。我们以其中一个比较简单的为例: 

我们自定义一个管理可以统一管理悬浮窗的类MyWindowManager,负责创建,删除悬浮窗

/** * Created by shiwe on 2017/3/7. * 悬浮窗管理 * 创建,移除 * 单例模式 */public class MyWindowManager { private FloatNormalView normalView; private FloatControlView controlView; private static MyWindowManager instance; private MyWindowManager() { } public static MyWindowManager getInstance() {  if (instance == null)   instance = new MyWindowManager();  return instance; } /**  * 创建小型悬浮窗  */ public void createNormalView(Context context) {  if (normalView == null)   normalView = new FloatNormalView(context); } /**  * 移除悬浮窗  *  * @param context  */ public void removeNormalView(Context context) {  if (normalView != null) {   WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);   windowManager.removeView(normalView);   normalView = null;  } } /**  * 创建小型悬浮窗  */ public void createControlView(Context context) {  if (controlView == null)   controlView = new FloatControlView(context); } /**  * 移除悬浮窗  *  * @param context  */ public void removeControlView(Context context) {  if (controlView != null) {   WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);   windowManager.removeView(controlView);   controlView = null;  } }}

然后看看我们自定义的一个view,其继承自LinearLayout,我们在initLayoutParams初始化这个控件的位置等其他参数;在initEvent方法中定义随手指移动的监听事件以及长按的监听事件。

public class FloatNormalView extends LinearLayout { private Context context = null; private View view = null; private ImageView ivShowControlView = null; private WindowManager.LayoutParams lp = new WindowManager.LayoutParams(); private static WindowManager windowManager; private float mTouchStartX; private float mTouchStartY; private float x; private float y; private boolean initViewPlace = false; private MyWindowManager myWindowManager; private boolean isControlViewShowing = false; public FloatNormalView(Context context) {  super(context);  this.context = context;  myWindowManager = MyWindowManager.getInstance();  LayoutInflater.from(context).inflate(R.layout.float_normal_view, this);  view = findViewById(R.id.ll_float_normal);  ivShowControlView = (ImageView) findViewById(R.id.iv_show_control_view);  windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);  initLayoutParams();  initEvent(); } /**  * 初始化参数  */ private void initLayoutParams() {  //屏幕宽高  int screenWidth = windowManager.getDefaultDisplay().getWidth();  int screenHeight = windowManager.getDefaultDisplay().getHeight();  //总是出现在应用程序窗口之上。  lp.type = WindowManager.LayoutParams.TYPE_PHONE;  // FLAG_NOT_TOUCH_MODAL不阻塞事件传递到后面的窗口  // FLAG_NOT_FOCUSABLE 悬浮窗口较小时,后面的应用图标由不可长按变为可长按,不设置这个flag的话,home页的划屏会有问题  lp.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;  //悬浮窗默认显示的位置  lp.gravity = Gravity.START | Gravity.TOP;  //指定位置  lp.x = screenWidth - view.getLayoutParams().width * 2;  lp.y = screenHeight / 2 + view.getLayoutParams().height * 2;  //悬浮窗的宽高  lp.width = WindowManager.LayoutParams.WRAP_CONTENT;  lp.height = WindowManager.LayoutParams.WRAP_CONTENT;  lp.format = PixelFormat.TRANSPARENT;  windowManager.addView(this, lp); } /**  * 设置悬浮窗监听事件  */ private void initEvent() {  ivShowControlView.setOnLongClickListener(new OnLongClickListener() {   @Override   public boolean onLongClick(View view) {    if (!isControlViewShowing) {     myWindowManager.createControlView(context);     isControlViewShowing = true;    } else {     myWindowManager.removeControlView(context);     isControlViewShowing = false;    }    return true;   }  });  view.setOnTouchListener(new OnTouchListener() {   @Override   public boolean onTouch(View v, MotionEvent event) {    switch (event.getAction()) {     case MotionEvent.ACTION_DOWN:      if (!initViewPlace) {       initViewPlace = true;       //获取初始位置       mTouchStartX += (event.getRawX() - lp.x);       mTouchStartY += (event.getRawY() - lp.y);      } else {       //根据上次手指离开的位置与此次点击的位置进行初始位置微调       mTouchStartX += (event.getRawX() - x);       mTouchStartY += (event.getRawY() - y);      }      break;     case MotionEvent.ACTION_MOVE:      // 获取相对屏幕的坐标,以屏幕左上角为原点      x = event.getRawX();      y = event.getRawY();      updateViewPosition();      break;     case MotionEvent.ACTION_UP:      break;    }    return true;   }  }); } /**  * 更新浮动窗口位置  */ private void updateViewPosition() {  lp.x = (int) (x - mTouchStartX);  lp.y = (int) (y - mTouchStartY);  windowManager.updateViewLayout(this, lp); }

最后,只需要在Activity中调用mywindowManager中调用createxxx方法就可以。

public class MainActivity extends AppCompatActivity { MyWindowManager myWindowManager; @Override protected void onCreate(@Nullable Bundle savedInstanceState) {  super.onCreate(savedInstanceState);  setContentView(R.layout.activity_main);  myWindowManager = MyWindowManager.getInstance();  myWindowManager.createNormalView(this.getApplicationContext()); }}

最后,附上demo项目的下载地址: android实现悬浮窗

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持VEVB武林网。


注:相关教程知识阅读请移步到Android开发频道。
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表