很多应用都有悬浮窗等功能,在手机屏幕上放置一个悬浮窗,用户可以操作该悬浮窗,但是这种悬浮窗并不是widget,如下简单介绍下悬浮窗:
一般屏幕放置view都是基于WindowManager类,然后再配合WindowManager.LayoutParams类设置不同的参数,可以达到不同的效果,如是否可以接受用户输入,是否有焦点等。
WindowManager一般通过Context.getSystemService(Context.WINDOW_SERVICE)的方式获取,关于类的继承关系为
public interface WindowManager extends ViewManager,ViewManager只有三个方法,见下:
/** Interface to let you add and remove child views to an Activity. To get an instance * of this class, call {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}. */ public interface ViewManager { public void addView(View view, ViewGroup.LayoutParams params); public void updateViewLayout(View view, ViewGroup.LayoutParams params); public void removeView(View view); }
LayoutParams类的主要可以设置type和flag,具体参数可以参考源码。
以下主要分析一段具体使用代码:
WindowManager wm = null; WindowManager.LayoutParams wmParams = null; // 获取WindowManager wm = (WindowManager) getApplicationContext().getSystemService("window"); // 设置LayoutParams(全局变量)相关参数 wmParams = new WindowManager.LayoutParams(); wmParams.type = WindowManager.LayoutParams.TYPE_PHONE;// 该类型提供与用户交互,置于所有应用程序上方,但是在状态栏后面 wmParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;// 不接受任何按键事件 wmParams.gravity = Gravity.LEFT | Gravity.TOP; // 调整悬浮窗口至左上角 // 以屏幕左上角为原点,设置x、y初始值 wmParams.x = 0; wmParams.y = 0; // 设置悬浮窗口长宽数据 wmParams.width = WindowManager.LayoutParams.WRAP_CONTENT; wmParams.height = WindowManager.LayoutParams.WRAP_CONTENT; wmParams.format = PixelFormat.RGBA_8888; wm.addView(view, wmParams);
上面这段代码就可以放置一个view在屏幕上,这个view的生命周期不依赖于application本身。如需删除view则调用removeView即可。
如需不断更改view内容,位置等,可以不断调用updateViewLayout。
以下这段主要说明如何让view和用户交互事件:
view.setOnTouchListener(new OnTouchListener() { public boolean onTouch(View v, MotionEvent event) { // 获取相对屏幕的坐标,即以屏幕左上角为原点 x = event.getRawX(); // 25是系统状态栏的高度,也可以通过方法得到准确的值,自己微调就是了 y = event.getRawY() - 25; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: // 获取相对View的坐标,即以此View左上角为原点 mTouchStartX = event.getX(); mTouchStartY = event.getY() + view.getHeight() / 2; firstX = (int) event.getX(); totalMove = 0; firstDown = false; return false; case MotionEvent.ACTION_MOVE: updateViewPosition(); if (firstDown) { totalMove = 0; firstDown = false; } curX = (int) event.getX(); int delatX = curX - firstX; totalMove += delatX; firstX = curX; return false; case MotionEvent.ACTION_UP: updateViewPosition(); mTouchStartX = mTouchStartY = 0; boolean result = false; if (totalMove > 10) { totalMove = 0; // things you shouold do here result = true; } if (totalMove < 0 && Math.abs(totalMove) > 10) { totalMove = 0; // things you shouold do here result = true; } return result; } // 写这个是为了让点击和touch事件都起作用 return v.onTouchEvent(event); } }); view.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Toast.makeText(TopFloatService.this, "ok", Toast.LENGTH_SHORT) .show(); soundpool.play(mSound_0, 1, 1, 0, 0, 1); } }); private void updateViewPosition() { // 更新浮动窗口位置参数 wmParams.x = (int) (x - mTouchStartX); wmParams.y = (int) (y - mTouchStartY); wm.updateViewLayout(view, wmParams); }
这样就可以接受用户点击,触摸事件。上述可能有一些变量没有声明,可以参考如下的声明,也可以自己声明:
View view; SoundPool soundpool = null; private float mTouchStartX; private float mTouchStartY; private float x; private float y; int firstX, curX; private int totalMove = 0; private boolean firstDown = true; int mSound_0 = 0; Intent i; ImageView imageview; String path;
此外还可以不断对view进行变换,比如给view设置一个动画,或者更换布局,比如桌面歌词的显示与控制等都可以通过这种方式实现。
最后还是别忘记添加权限:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />