软件编程
位置:首页>> 软件编程>> Android编程>> android实现可自由移动、监听点击事件的悬浮窗

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

作者:石炜贤的工作室  发布时间:2022-04-12 14:15:31 

标签:android,悬浮窗

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

好,开始吧。首先我们先聊权限,悬浮窗需要在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实现悬浮窗

来源:https://blog.csdn.net/code_better/article/details/60886614

0
投稿

猜你喜欢

手机版 软件编程 asp之家 www.aspxhome.com