转载请注明出处:http://blog.csdn.net/zhaokaiqiang1992
现在很多安全类的软件,比如360手机助手,百度手机助手等等,都有一个悬浮窗,可以飘浮在桌面上,方便用户使用一些常用的操作。今天这篇文章,就是介绍如何实现桌面悬浮窗效果的。
首先,看一下效果图。
悬浮窗一共分为两个部分,一个是平常显示的小窗口,另外一个是点击小窗口显示出来的二级悬浮窗口。
首先,先看一下这个项目的目录结构。
最关键的就是红框内的四个类。
首先,FloatWindowService是一个后台的服务类,主要负责在后台不断的刷新桌面上的小悬浮窗口,否则会导致更换界面之后,悬浮窗口也会随之消失,因此需要不断的刷新。下面是实现代码。
-
package com.qust.floatwindow;
-
-
import java.util.Timer;
-
import java.util.TimerTask;
-
-
import android.app.Service;
-
import android.content.Context;
-
import android.content.Intent;
-
import android.os.Handler;
-
import android.os.IBinder;
-
-
-
-
-
-
-
-
-
public class FloatWindowService extends Service {
-
-
public static final String LAYOUT_RES_ID = "layoutResId";
-
public static final String ROOT_LAYOUT_ID = "rootLayoutId";
-
-
-
private Handler handler = new Handler();
-
private Context context;
-
private Timer timer;
-
-
private int layoutResId;
-
-
private int rootLayoutId;
-
-
@Override
-
public int onStartCommand(Intent intent, int flags, int startId) {
-
-
context = this;
-
layoutResId = intent.getIntExtra(LAYOUT_RES_ID, 0);
-
rootLayoutId = intent.getIntExtra(ROOT_LAYOUT_ID, 0);
-
-
if (layoutResId == 0 || rootLayoutId == 0) {
-
throw new IllegalArgumentException(
-
"layoutResId or rootLayoutId is illegal");
-
}
-
-
if (timer == null) {
-
timer = new Timer();
-
-
timer.scheduleAtFixedRate(new RefreshTask(), 0, 500);
-
}
-
return super.onStartCommand(intent, flags, startId);
-
}
-
-
@Override
-
public void onDestroy() {
-
super.onDestroy();
-
-
timer.cancel();
-
timer = null;
-
}
-
-
private class RefreshTask extends TimerTask {
-
-
@Override
-
public void run() {
-
-
if (!FloatWindowManager.getInstance(context).isWindowShowing()) {
-
handler.post(new Runnable() {
-
@Override
-
public void run() {
-
FloatWindowManager.getInstance(context)
-
.createSmallWindow(context, layoutResId,
-
rootLayoutId);
-
}
-
});
-
}
-
}
-
}
-
-
@Override
-
public IBinder onBind(Intent intent) {
-
return null;
-
}
-
-
}
除了后台服务之外,我们还需要两个自定义的布局,分别是FloatWindowSmallView和FloatWindowBigView,这两个自定义的布局,主要负责悬浮窗的前台显示,我们分别看一下代码实现。
首先是FloatWindowSmallView类的实现。
在这个类里面,主要的工作是实现悬浮窗口在桌面前端的实现,还有就是位置的移动和单击事件的判断以及处理。这里使用的是主要是WindowManager类的一些方法和属性,下一篇会详细说明,这篇只说实现。
除了小悬浮窗之外,点击之后弹出的二级悬浮窗也是类似的方式添加到桌面上,下面是二级悬浮窗的代码。
-
package com.qust.floatwindow;
-
-
import android.content.Context;
-
import android.graphics.PixelFormat;
-
import android.view.Gravity;
-
import android.view.LayoutInflater;
-
import android.view.View;
-
import android.view.WindowManager;
-
import android.widget.LinearLayout;
-
import android.widget.TextView;
-
-
import com.qust.demo.ScreenUtils;
-
import com.qust.floatingwindow.R;
-
-
public class FloatWindowBigView extends LinearLayout {
-
-
-
public int viewWidth;
-
-
public int viewHeight;
-
-
public WindowManager.LayoutParams bigWindowParams;
-
-
private Context context;
-
-
public FloatWindowBigView(Context context) {
-
super(context);
-
this.context = context;
-
-
LayoutInflater.from(context).inflate(R.layout.float_window_big, this);
-
-
View view = findViewById(R.id.big_window_layout);
-
viewWidth = view.getLayoutParams().width;
-
viewHeight = view.getLayoutParams().height;
-
-
bigWindowParams = new WindowManager.LayoutParams();
-
-
bigWindowParams.x = ScreenUtils.getScreenWidth(context) / 2 - viewWidth
-
/ 2;
-
bigWindowParams.y = ScreenUtils.getScreenHeight(context) / 2
-
- viewHeight / 2;
-
bigWindowParams.type = WindowManager.LayoutParams.TYPE_PHONE;
-
bigWindowParams.format = PixelFormat.RGBA_8888;
-
-
-
bigWindowParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
-
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-
-
bigWindowParams.gravity = Gravity.LEFT | Gravity.TOP;
-
bigWindowParams.width = viewWidth;
-
bigWindowParams.height = viewHeight;
-
-
initView();
-
-
}
-
-
private void initView() {
-
TextView tv_back = (TextView) findViewById(R.id.tv_back);
-
tv_back.setOnClickListener(new OnClickListener() {
-
-
@Override
-
public void onClick(View v) {
-
FloatWindowManager.getInstance(context).removeBigWindow();
-
}
-
});
-
}
-
-
}
这些基本的类建立起来之后,剩下的就是最重要的类FloatWindowManager的实现。这个类实现的就是对悬浮窗的操作。
还有个获取屏幕宽高的帮助类。
-
package com.qust.demo;
-
-
import android.content.Context;
-
import android.view.WindowManager;
-
-
-
-
-
-
-
-
public class ScreenUtils {
-
-
-
-
-
-
-
@SuppressWarnings("deprecation")
-
public static int getScreenWidth(Context context) {
-
return ((WindowManager) context
-
.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay()
-
.getWidth();
-
}
-
-
-
-
-
-
-
@SuppressWarnings("deprecation")
-
public static int getScreenHeight(Context context) {
-
return ((WindowManager) context
-
.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay()
-
.getHeight();
-
}
-
-
}
完成这些,我们就可以直接用了。
-
package com.qust.demo;
-
-
import android.app.Activity;
-
import android.content.Context;
-
import android.content.Intent;
-
import android.os.Bundle;
-
import android.view.KeyEvent;
-
import android.view.View;
-
-
import com.qust.floatingwindow.R;
-
import com.qust.floatwindow.FloatWindowManager;
-
import com.qust.floatwindow.FloatWindowService;
-
import com.qust.floatwindow.FloatWindowSmallView.OnClickListener;
-
-
-
-
-
-
-
-
-
-
-
public class MainActivity extends Activity {
-
-
private FloatWindowManager floatWindowManager;
-
-
private Context context;
-
-
@Override
-
protected void onCreate(Bundle savedInstanceState) {
-
super.onCreate(savedInstanceState);
-
setContentView(R.layout.activity_main);
-
-
context = this;
-
floatWindowManager = FloatWindowManager.getInstance(context);
-
-
}
-
-
-
-
-
-
-
public void show(View view) {
-
-
Intent intent = new Intent(context, FloatWindowService.class);
-
intent.putExtra(FloatWindowService.LAYOUT_RES_ID,
-
R.layout.float_window_small);
-
intent.putExtra(FloatWindowService.ROOT_LAYOUT_ID,
-
R.id.small_window_layout);
-
startService(intent);
-
}
-
-
-
-
-
-
-
public void showBig(View view) {
-
-
-
floatWindowManager.setOnClickListener(new OnClickListener() {
-
-
@Override
-
public void click() {
-
floatWindowManager.createBigWindow(context);
-
}
-
});
-
-
}
-
-
-
-
-
-
-
public void remove(View view) {
-
floatWindowManager.removeAll();
-
}
-
-
@Override
-
public boolean onKeyDown(int keyCode, KeyEvent event) {
-
-
-
if (keyCode == KeyEvent.KEYCODE_BACK
-
&& event.getAction() == KeyEvent.ACTION_DOWN) {
-
floatWindowManager.removeBigWindow();
-
return true;
-
}
-
-
return super.onKeyDown(keyCode, event);
-
}
-
-
}
项目下载地址:https://github.com/ZhaoKaiQiang/FloatWindow
在上面文章中,我们介绍了如何实现桌面悬浮窗口,在这个效果的实现过程中,最重要的一个类就是WindowManager,今天这篇文章,将对WindowManager的使用进行介绍,并且实现一个使用WindowManager来实现用户打开APP,显示首次使用教学蒙板的效果。
WindowManager类实现了ViewManager接口,ViewManager接口允许我们在Activity上添加或者是移除view,因此WindowManager也允许我们在Activity上进行View的添加和移除操作。
我们可以通过下面的方法获取一个WindowManager对象
Context.getSystemService(Context.WINDOW_SERVICE)
在Activity之中,我们可以直接通过getWindowManager()获取到一个WindowManager对象。
每一个WindowManager实例都被绑定到一个独有的Display对象上面,如果我们想获取不同Display的WindowManager对象,我们可以通过createDisplayContext(Display)获取到这个Display的Context对象,然后使用上面的方法,也可以获取到一个WindowManager对象。
我们在使用WindowManager类的时候,通常使用下面的几个方法:
windowManager.addView(View,WindowManager.LayoutParam);
windowManager.removeView();
windowManager.getDefaultDisplay();
windowManager.addView()方法用来向当前的窗口上添加View对象,需要接受两个参数,View是要添加到窗口的View对象,而WindowManager.LayoutParam则是添加的窗口的参数,在上一篇添加悬浮窗的操作的时候,需要对LayoutParam设置很多参数,下面我们看一下常用的设置
-
-
LayoutParams params = new WindowManager.LayoutParams();
-
-
params.type = WindowManager.LayoutParams.TYPE_PHONE;
-
-
params.format = PixelFormat.RGBA_8888;
-
-
params.gravity = Gravity.LEFT | Gravity.TOP;
-
-
params.width = ScreenUtils.getScreenWidth(this);
-
params.height = ScreenUtils.getScreenHeight(this);
-
-
params.x;
-
params.y;
设置好LayoutParam之后,我们就可以通过windowManager.addView(View,WindowManager.LayoutParam)将View添加到窗口之上,不过,我们需要申明权限
<uses-permissionandroid:name="android.permission.SYSTEM_ALERT_WINDOW"/>
添加完成之后,我们就可以在窗口上看到我们添加的View对象了。如果我们想将添加的View移除,我们只需要调用windowManager.removeView()即可,参数就是我们前面使用的View对象,使用很简单。除了这个方法,还有个windowManager.removeViewImmediate(),也可以将View移除,但是文档中说,这个方法并不是给一般程序调用的,因此需要小心使用,我们开发的都属于一般程序,建议不要使用这个方法。
除了这两个方法之外,我们最常用的另外一个方法就是windowManager.getDefaultDisplay(),通过这个方法,我们可以获取到当前界面的Display的一个对象,然后我们就可以获取到当前屏幕的一些参数,比如说宽高。
下面是我常用的一个工具类。
-
package com.qust.teachmask;
-
-
import android.content.Context;
-
import android.view.WindowManager;
-
-
-
-
-
-
-
-
public class ScreenUtils {
-
-
-
-
-
-
-
@SuppressWarnings("deprecation")
-
public static int getScreenWidth(Context context) {
-
return ((WindowManager) context
-
.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay()
-
.getWidth();
-
}
-
-
-
-
-
-
-
@SuppressWarnings("deprecation")
-
public static int getScreenHeight(Context context) {
-
return ((WindowManager) context
-
.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay()
-
.getHeight();
-
}
-
-
}
知道上面这些之后,我们就可以实现教学模板效果了,首先看效果图。
下面是代码实现
-
package com.qust.teachmask;
-
-
import android.app.Activity;
-
import android.graphics.PixelFormat;
-
import android.os.Bundle;
-
import android.view.Gravity;
-
import android.view.View;
-
import android.view.View.OnClickListener;
-
import android.view.WindowManager;
-
import android.view.WindowManager.LayoutParams;
-
import android.widget.ImageView;
-
import android.widget.ImageView.ScaleType;
-
-
public class MainActivity extends Activity {
-
-
private ImageView img;
-
-
private WindowManager windowManager;
-
-
@Override
-
protected void onCreate(Bundle savedInstanceState) {
-
super.onCreate(savedInstanceState);
-
setContentView(R.layout.activity_main);
-
-
windowManager = getWindowManager();
-
-
-
img = new ImageView(this);
-
img.setLayoutParams(new LayoutParams(
-
android.view.ViewGroup.LayoutParams.MATCH_PARENT,
-
android.view.ViewGroup.LayoutParams.MATCH_PARENT));
-
img.setScaleType(ScaleType.FIT_XY);
-
img.setImageResource(R.drawable.guide);
-
-
-
LayoutParams params = new WindowManager.LayoutParams();
-
-
params.type = WindowManager.LayoutParams.TYPE_PHONE;
-
-
params.format = PixelFormat.RGBA_8888;
-
-
params.gravity = Gravity.LEFT | Gravity.TOP;
-
-
params.width = ScreenUtils.getScreenWidth(this);
-
params.height = ScreenUtils.getScreenHeight(this);
-
-
-
windowManager.addView(img, params);
-
-
-
img.setOnClickListener(new OnClickListener() {
-
-
@Override
-
public void onClick(View arg0) {
-
windowManager.removeView(img);
-
}
-
});
-
-
}
-
}
github项目地址:https://github.com/ZhaoKaiQiang/TeachMask