android实现可自由移动、监听点击事件的悬浮窗
2019/7/7 21:50:08
本文主要是介绍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实现悬浮窗
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持找一找教程网。
这篇关于android实现可自由移动、监听点击事件的悬浮窗的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-01-18android.permission.read_media_video
- 2024-01-18android_getaddrinfo failed eai_nodata
- 2024-01-18androidmo
- 2024-01-15Android下三种离屏渲染技术
- 2024-01-09Android 蓝牙使用
- 2024-01-06Android对接华为AI - 文本识别
- 2023-11-15代码安全之代码混淆及加固(Android)
- 2023-11-10简述Android语音播报TTS
- 2023-11-06Android WiFi工具类
- 2023-07-22Android开发未来的出路