Android用户首次打开APP的使用教学蒙板效果实现-阿里云开发者社区

开发者社区> 开发与运维> 正文
登录阅读全文

Android用户首次打开APP的使用教学蒙板效果实现

简介:

 转载请注明出处:http://blog.csdn.net/zhaokaiqiang1992

    现在很多安全类的软件,比如360手机助手,百度手机助手等等,都有一个悬浮窗,可以飘浮在桌面上,方便用户使用一些常用的操作。今天这篇文章,就是介绍如何实现桌面悬浮窗效果的。

    首先,看一下效果图。


    悬浮窗一共分为两个部分,一个是平常显示的小窗口,另外一个是点击小窗口显示出来的二级悬浮窗口。

    首先,先看一下这个项目的目录结构。



    最关键的就是红框内的四个类。

    首先,FloatWindowService是一个后台的服务类,主要负责在后台不断的刷新桌面上的小悬浮窗口,否则会导致更换界面之后,悬浮窗口也会随之消失,因此需要不断的刷新。下面是实现代码。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. package com.qust.floatwindow;  
  2.   
  3. import java.util.Timer;  
  4. import java.util.TimerTask;  
  5.   
  6. import android.app.Service;  
  7. import android.content.Context;  
  8. import android.content.Intent;  
  9. import android.os.Handler;  
  10. import android.os.IBinder;  
  11.   
  12. /** 
  13.  * 悬浮窗后台服务 
  14.  *  
  15.  * @author zhaokaiqiang 
  16.  *  
  17.  */  
  18.   
  19. public class FloatWindowService extends Service {  
  20.   
  21.     public static final String LAYOUT_RES_ID = "layoutResId";  
  22.     public static final String ROOT_LAYOUT_ID = "rootLayoutId";  
  23.   
  24.     // 用于在线程中创建/移除/更新悬浮窗  
  25.     private Handler handler = new Handler();  
  26.     private Context context;  
  27.     private Timer timer;  
  28.     // 小窗口布局资源id  
  29.     private int layoutResId;  
  30.     // 布局根布局id  
  31.     private int rootLayoutId;  
  32.   
  33.     @Override  
  34.     public int onStartCommand(Intent intent, int flags, int startId) {  
  35.   
  36.         context = this;  
  37.         layoutResId = intent.getIntExtra(LAYOUT_RES_ID, 0);  
  38.         rootLayoutId = intent.getIntExtra(ROOT_LAYOUT_ID, 0);  
  39.   
  40.         if (layoutResId == 0 || rootLayoutId == 0) {  
  41.             throw new IllegalArgumentException(  
  42.                     "layoutResId or rootLayoutId is illegal");  
  43.         }  
  44.   
  45.         if (timer == null) {  
  46.             timer = new Timer();  
  47.             // 每500毫秒就执行一次刷新任务  
  48.             timer.scheduleAtFixedRate(new RefreshTask(), 0500);  
  49.         }  
  50.         return super.onStartCommand(intent, flags, startId);  
  51.     }  
  52.   
  53.     @Override  
  54.     public void onDestroy() {  
  55.         super.onDestroy();  
  56.         // Service被终止的同时也停止定时器继续运行  
  57.         timer.cancel();  
  58.         timer = null;  
  59.     }  
  60.   
  61.     private class RefreshTask extends TimerTask {  
  62.   
  63.         @Override  
  64.         public void run() {  
  65.             // 当前界面没有悬浮窗显示,则创建悬浮  
  66.             if (!FloatWindowManager.getInstance(context).isWindowShowing()) {  
  67.                 handler.post(new Runnable() {  
  68.                     @Override  
  69.                     public void run() {  
  70.                         FloatWindowManager.getInstance(context)  
  71.                                 .createSmallWindow(context, layoutResId,  
  72.                                         rootLayoutId);  
  73.                     }  
  74.                 });  
  75.             }  
  76.         }  
  77.     }  
  78.   
  79.     @Override  
  80.     public IBinder onBind(Intent intent) {  
  81.         return null;  
  82.     }  
  83.   
  84. }  

    除了后台服务之外,我们还需要两个自定义的布局,分别是FloatWindowSmallView和FloatWindowBigView,这两个自定义的布局,主要负责悬浮窗的前台显示,我们分别看一下代码实现。

    首先是FloatWindowSmallView类的实现。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. package com.qust.floatwindow;  
  2.   
  3. import java.lang.reflect.Field;  
  4.   
  5. import android.annotation.SuppressLint;  
  6. import android.content.Context;  
  7. import android.graphics.PixelFormat;  
  8. import android.view.Gravity;  
  9. import android.view.LayoutInflater;  
  10. import android.view.MotionEvent;  
  11. import android.view.View;  
  12. import android.view.WindowManager;  
  13. import android.widget.LinearLayout;  
  14. import android.widget.TextView;  
  15.   
  16. import com.qust.demo.ScreenUtils;  
  17. import com.qust.floatingwindow.R;  
  18.   
  19. /** 
  20.  * 小悬浮窗,用于初始显示 
  21.  *  
  22.  * @author zhaokaiqiang 
  23.  *  
  24.  */  
  25. public class FloatWindowSmallView extends LinearLayout {  
  26.   
  27.     // 小悬浮窗的宽  
  28.     public int viewWidth;  
  29.     // 小悬浮窗的高  
  30.     public int viewHeight;  
  31.     // 系统状态栏的高度  
  32.     private static int statusBarHeight;  
  33.     // 用于更新小悬浮窗的位置  
  34.     private WindowManager windowManager;  
  35.     // 小悬浮窗的布局参数  
  36.     public WindowManager.LayoutParams smallWindowParams;  
  37.     // 记录当前手指位置在屏幕上的横坐标  
  38.     private float xInScreen;  
  39.     // 记录当前手指位置在屏幕上的纵坐标  
  40.     private float yInScreen;  
  41.     // 记录手指按下时在屏幕上的横坐标,用来判断单击事件  
  42.     private float xDownInScreen;  
  43.     // 记录手指按下时在屏幕上的纵坐标,用来判断单击事件  
  44.     private float yDownInScreen;  
  45.     // 记录手指按下时在小悬浮窗的View上的横坐标  
  46.     private float xInView;  
  47.     // 记录手指按下时在小悬浮窗的View上的纵坐标  
  48.     private float yInView;  
  49.     // 单击接口  
  50.     private OnClickListener listener;  
  51.   
  52.     /** 
  53.      * 构造函数 
  54.      *  
  55.      * @param context 
  56.      *            上下文对象 
  57.      * @param layoutResId 
  58.      *            布局资源id 
  59.      * @param rootLayoutId 
  60.      *            根布局id 
  61.      */  
  62.     public FloatWindowSmallView(Context context, int layoutResId,  
  63.             int rootLayoutId) {  
  64.         super(context);  
  65.         windowManager = (WindowManager) context  
  66.                 .getSystemService(Context.WINDOW_SERVICE);  
  67.         LayoutInflater.from(context).inflate(layoutResId, this);  
  68.         View view = findViewById(rootLayoutId);  
  69.         viewWidth = view.getLayoutParams().width;  
  70.         viewHeight = view.getLayoutParams().height;  
  71.   
  72.         statusBarHeight = getStatusBarHeight();  
  73.   
  74.         TextView percentView = (TextView) findViewById(R.id.percent);  
  75.         percentView.setText("悬浮窗");  
  76.   
  77.         smallWindowParams = new WindowManager.LayoutParams();  
  78.         // 设置显示类型为phone  
  79.         smallWindowParams.type = WindowManager.LayoutParams.TYPE_PHONE;  
  80.         // 显示图片格式  
  81.         smallWindowParams.format = PixelFormat.RGBA_8888;  
  82.         // 设置交互模式  
  83.         smallWindowParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL  
  84.                 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;  
  85.         // 设置对齐方式为左上  
  86.         smallWindowParams.gravity = Gravity.LEFT | Gravity.TOP;  
  87.         smallWindowParams.width = viewWidth;  
  88.         smallWindowParams.height = viewHeight;  
  89.         smallWindowParams.x = ScreenUtils.getScreenWidth(context);  
  90.         smallWindowParams.y = ScreenUtils.getScreenHeight(context) / 2;  
  91.   
  92.     }  
  93.   
  94.     @SuppressLint("ClickableViewAccessibility")  
  95.     @Override  
  96.     public boolean onTouchEvent(MotionEvent event) {  
  97.         switch (event.getAction()) {  
  98.         // 手指按下时记录必要的数据,纵坐标的值都减去状态栏的高度  
  99.         case MotionEvent.ACTION_DOWN:  
  100.             // 获取相对与小悬浮窗的坐标  
  101.             xInView = event.getX();  
  102.             yInView = event.getY();  
  103.             // 按下时的坐标位置,只记录一次  
  104.             xDownInScreen = event.getRawX();  
  105.             yDownInScreen = event.getRawY() - statusBarHeight;  
  106.             break;  
  107.         case MotionEvent.ACTION_MOVE:  
  108.             // 时时的更新当前手指在屏幕上的位置  
  109.             xInScreen = event.getRawX();  
  110.             yInScreen = event.getRawY() - statusBarHeight;  
  111.             // 手指移动的时候更新小悬浮窗的位置  
  112.             updateViewPosition();  
  113.             break;  
  114.         case MotionEvent.ACTION_UP:  
  115.             // 如果手指离开屏幕时,按下坐标与当前坐标相等,则视为触发了单击事件  
  116.             if (xDownInScreen == event.getRawX()  
  117.                     && yDownInScreen == (event.getRawY() - getStatusBarHeight())) {  
  118.   
  119.                 if (listener != null) {  
  120.                     listener.click();  
  121.                 }  
  122.   
  123.             }  
  124.             break;  
  125.         }  
  126.         return true;  
  127.     }  
  128.   
  129.     /** 
  130.      * 设置单击事件的回调接口 
  131.      */  
  132.     public void setOnClickListener(OnClickListener listener) {  
  133.         this.listener = listener;  
  134.     }  
  135.   
  136.     /** 
  137.      * 更新小悬浮窗在屏幕中的位置 
  138.      */  
  139.     private void updateViewPosition() {  
  140.         smallWindowParams.x = (int) (xInScreen - xInView);  
  141.         smallWindowParams.y = (int) (yInScreen - yInView);  
  142.         windowManager.updateViewLayout(this, smallWindowParams);  
  143.     }  
  144.   
  145.     /** 
  146.      * 获取状态栏的高度 
  147.      *  
  148.      * @return 
  149.      */  
  150.     private int getStatusBarHeight() {  
  151.   
  152.         try {  
  153.             Class<?> c = Class.forName("com.android.internal.R$dimen");  
  154.             Object o = c.newInstance();  
  155.             Field field = c.getField("status_bar_height");  
  156.             int x = (Integer) field.get(o);  
  157.             return getResources().getDimensionPixelSize(x);  
  158.         } catch (Exception e) {  
  159.             e.printStackTrace();  
  160.         }  
  161.   
  162.         return 0;  
  163.     }  
  164.   
  165.     /** 
  166.      * 单击接口 
  167.      *  
  168.      * @author zhaokaiqiang 
  169.      *  
  170.      */  
  171.     public interface OnClickListener {  
  172.   
  173.         public void click();  
  174.   
  175.     }  
  176.   
  177. }  

    在这个类里面,主要的工作是实现悬浮窗口在桌面前端的实现,还有就是位置的移动和单击事件的判断以及处理。这里使用的是主要是WindowManager类的一些方法和属性,下一篇会详细说明,这篇只说实现。

    除了小悬浮窗之外,点击之后弹出的二级悬浮窗也是类似的方式添加到桌面上,下面是二级悬浮窗的代码。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. package com.qust.floatwindow;  
  2.   
  3. import android.content.Context;  
  4. import android.graphics.PixelFormat;  
  5. import android.view.Gravity;  
  6. import android.view.LayoutInflater;  
  7. import android.view.View;  
  8. import android.view.WindowManager;  
  9. import android.widget.LinearLayout;  
  10. import android.widget.TextView;  
  11.   
  12. import com.qust.demo.ScreenUtils;  
  13. import com.qust.floatingwindow.R;  
  14.   
  15. public class FloatWindowBigView extends LinearLayout {  
  16.   
  17.     // 记录大悬浮窗的宽  
  18.     public int viewWidth;  
  19.     // 记录大悬浮窗的高  
  20.     public int viewHeight;  
  21.   
  22.     public WindowManager.LayoutParams bigWindowParams;  
  23.   
  24.     private Context context;  
  25.   
  26.     public FloatWindowBigView(Context context) {  
  27.         super(context);  
  28.         this.context = context;  
  29.   
  30.         LayoutInflater.from(context).inflate(R.layout.float_window_big, this);  
  31.   
  32.         View view = findViewById(R.id.big_window_layout);  
  33.         viewWidth = view.getLayoutParams().width;  
  34.         viewHeight = view.getLayoutParams().height;  
  35.   
  36.         bigWindowParams = new WindowManager.LayoutParams();  
  37.         // 设置显示的位置,默认的是屏幕中心  
  38.         bigWindowParams.x = ScreenUtils.getScreenWidth(context) / 2 - viewWidth  
  39.                 / 2;  
  40.         bigWindowParams.y = ScreenUtils.getScreenHeight(context) / 2  
  41.                 - viewHeight / 2;  
  42.         bigWindowParams.type = WindowManager.LayoutParams.TYPE_PHONE;  
  43.         bigWindowParams.format = PixelFormat.RGBA_8888;  
  44.   
  45.         // 设置交互模式  
  46.         bigWindowParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL  
  47.                 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;  
  48.   
  49.         bigWindowParams.gravity = Gravity.LEFT | Gravity.TOP;  
  50.         bigWindowParams.width = viewWidth;  
  51.         bigWindowParams.height = viewHeight;  
  52.   
  53.         initView();  
  54.   
  55.     }  
  56.   
  57.     private void initView() {  
  58.         TextView tv_back = (TextView) findViewById(R.id.tv_back);  
  59.         tv_back.setOnClickListener(new OnClickListener() {  
  60.   
  61.             @Override  
  62.             public void onClick(View v) {  
  63.                 FloatWindowManager.getInstance(context).removeBigWindow();  
  64.             }  
  65.         });  
  66.     }  
  67.   
  68. }  

    这些基本的类建立起来之后,剩下的就是最重要的类FloatWindowManager的实现。这个类实现的就是对悬浮窗的操作。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. package com.qust.floatwindow;  
  2.   
  3. import android.content.Context;  
  4. import android.content.Intent;  
  5. import android.view.WindowManager;  
  6.   
  7. /** 
  8.  * 悬浮窗管理器 
  9.  *  
  10.  * @author zhaokaiqiang 
  11.  *  
  12.  */  
  13. public class FloatWindowManager {  
  14.   
  15.     // 小悬浮窗对象  
  16.     private FloatWindowSmallView smallWindow;  
  17.     // 大悬浮窗对象  
  18.     private FloatWindowBigView bigWindow;  
  19.     // 用于控制在屏幕上添加或移除悬浮窗  
  20.     private WindowManager mWindowManager;  
  21.     // FloatWindowManager的单例  
  22.     private static FloatWindowManager floatWindowManager;  
  23.     // 上下文对象  
  24.     private Context context;  
  25.   
  26.     private FloatWindowManager(Context context) {  
  27.         this.context = context;  
  28.     }  
  29.   
  30.     public static FloatWindowManager getInstance(Context context) {  
  31.   
  32.         if (floatWindowManager == null) {  
  33.             floatWindowManager = new FloatWindowManager(context);  
  34.         }  
  35.         return floatWindowManager;  
  36.     }  
  37.   
  38.     /** 
  39.      * 创建小悬浮窗 
  40.      *  
  41.      * @param context 
  42.      *            必须为应用程序的Context. 
  43.      */  
  44.     public void createSmallWindow(Context context, int layoutResId,  
  45.             int rootLayoutId) {  
  46.         WindowManager windowManager = getWindowManager();  
  47.   
  48.         if (smallWindow == null) {  
  49.             smallWindow = new FloatWindowSmallView(context, layoutResId,  
  50.                     rootLayoutId);  
  51.             windowManager.addView(smallWindow, smallWindow.smallWindowParams);  
  52.         }  
  53.     }  
  54.   
  55.     /** 
  56.      * 将小悬浮窗从屏幕上移除 
  57.      *  
  58.      * @param context 
  59.      */  
  60.     public void removeSmallWindow() {  
  61.         if (smallWindow != null) {  
  62.             WindowManager windowManager = getWindowManager();  
  63.             windowManager.removeView(smallWindow);  
  64.             smallWindow = null;  
  65.         }  
  66.     }  
  67.   
  68.     public void setOnClickListener(FloatWindowSmallView.OnClickListener listener) {  
  69.         if (smallWindow != null) {  
  70.             smallWindow.setOnClickListener(listener);  
  71.         }  
  72.     }  
  73.   
  74.     /** 
  75.      * 创建大悬浮窗 
  76.      *  
  77.      * @param context 
  78.      *            必须为应用程序的Context. 
  79.      */  
  80.     public void createBigWindow(Context context) {  
  81.         WindowManager windowManager = getWindowManager();  
  82.         if (bigWindow == null) {  
  83.             bigWindow = new FloatWindowBigView(context);  
  84.             windowManager.addView(bigWindow, bigWindow.bigWindowParams);  
  85.         }  
  86.     }  
  87.   
  88.     /** 
  89.      * 将大悬浮窗从屏幕上移除 
  90.      *  
  91.      * @param context 
  92.      */  
  93.     public void removeBigWindow() {  
  94.         if (bigWindow != null) {  
  95.             WindowManager windowManager = getWindowManager();  
  96.             windowManager.removeView(bigWindow);  
  97.             bigWindow = null;  
  98.         }  
  99.     }  
  100.   
  101.     public void removeAll() {  
  102.   
  103.         context.stopService(new Intent(context, FloatWindowService.class));  
  104.         removeSmallWindow();  
  105.         removeBigWindow();  
  106.   
  107.     }  
  108.   
  109.     /** 
  110.      * 是否有悬浮窗显示(包括小悬浮窗和大悬浮) 
  111.      *  
  112.      * @return 有悬浮窗显示在桌面上返回true,没有的话返回false 
  113.      */  
  114.     public boolean isWindowShowing() {  
  115.         return smallWindow != null || bigWindow != null;  
  116.     }  
  117.   
  118.     /** 
  119.      * 如果WindowManager还未创建,则创建新的WindowManager返回。否则返回当前已创建的WindowManager 
  120.      *  
  121.      * @param context 
  122.      * @return 
  123.      */  
  124.     private WindowManager getWindowManager() {  
  125.         if (mWindowManager == null) {  
  126.             mWindowManager = (WindowManager) context  
  127.                     .getSystemService(Context.WINDOW_SERVICE);  
  128.         }  
  129.         return mWindowManager;  
  130.     }  
  131.   
  132. }  

    还有个获取屏幕宽高的帮助类。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. package com.qust.demo;  
  2.   
  3. import android.content.Context;  
  4. import android.view.WindowManager;  
  5.   
  6. /** 
  7.  * 屏幕帮助类 
  8.  *  
  9.  * @author zhaokaiqiang 
  10.  *  
  11.  */  
  12. public class ScreenUtils {  
  13.   
  14.     /** 
  15.      * 获取屏幕宽度 
  16.      *  
  17.      * @return 
  18.      */  
  19.     @SuppressWarnings("deprecation")  
  20.     public static int getScreenWidth(Context context) {  
  21.         return ((WindowManager) context  
  22.                 .getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay()  
  23.                 .getWidth();  
  24.     }  
  25.   
  26.     /** 
  27.      * 获取屏幕宽度 
  28.      *  
  29.      * @return 
  30.      */  
  31.     @SuppressWarnings("deprecation")  
  32.     public static int getScreenHeight(Context context) {  
  33.         return ((WindowManager) context  
  34.                 .getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay()  
  35.                 .getHeight();  
  36.     }  
  37.   
  38. }  

    完成这些,我们就可以直接用了。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. package com.qust.demo;  
  2.   
  3. import android.app.Activity;  
  4. import android.content.Context;  
  5. import android.content.Intent;  
  6. import android.os.Bundle;  
  7. import android.view.KeyEvent;  
  8. import android.view.View;  
  9.   
  10. import com.qust.floatingwindow.R;  
  11. import com.qust.floatwindow.FloatWindowManager;  
  12. import com.qust.floatwindow.FloatWindowService;  
  13. import com.qust.floatwindow.FloatWindowSmallView.OnClickListener;  
  14.   
  15. /** 
  16.  * 示例 
  17.  *  
  18.  * @ClassName: com.qust.demo.MainActivity 
  19.  * @Description: 
  20.  * @author zhaokaiqiang 
  21.  * @date 2014-10-23 下午11:30:13 
  22.  *  
  23.  */  
  24. public class MainActivity extends Activity {  
  25.   
  26.     private FloatWindowManager floatWindowManager;  
  27.   
  28.     private Context context;  
  29.   
  30.     @Override  
  31.     protected void onCreate(Bundle savedInstanceState) {  
  32.         super.onCreate(savedInstanceState);  
  33.         setContentView(R.layout.activity_main);  
  34.   
  35.         context = this;  
  36.         floatWindowManager = FloatWindowManager.getInstance(context);  
  37.   
  38.     }  
  39.   
  40.     /** 
  41.      * 显示小窗口 
  42.      *  
  43.      * @param view 
  44.      */  
  45.     public void show(View view) {  
  46.         // 需要传递小悬浮窗布局,以及根布局的id,启动后台服务  
  47.         Intent intent = new Intent(context, FloatWindowService.class);  
  48.         intent.putExtra(FloatWindowService.LAYOUT_RES_ID,  
  49.                 R.layout.float_window_small);  
  50.         intent.putExtra(FloatWindowService.ROOT_LAYOUT_ID,  
  51.                 R.id.small_window_layout);  
  52.         startService(intent);  
  53.     }  
  54.   
  55.     /** 
  56.      * 显示二级悬浮窗 
  57.      *  
  58.      * @param view 
  59.      */  
  60.     public void showBig(View view) {  
  61.   
  62.         // 设置小悬浮窗的单击事件  
  63.         floatWindowManager.setOnClickListener(new OnClickListener() {  
  64.   
  65.             @Override  
  66.             public void click() {  
  67.                 floatWindowManager.createBigWindow(context);  
  68.             }  
  69.         });  
  70.   
  71.     }  
  72.   
  73.     /** 
  74.      * 移除所有的悬浮窗 
  75.      *  
  76.      * @param view 
  77.      */  
  78.     public void remove(View view) {  
  79.         floatWindowManager.removeAll();  
  80.     }  
  81.   
  82.     @Override  
  83.     public boolean onKeyDown(int keyCode, KeyEvent event) {  
  84.   
  85.         // 返回键移除二级悬浮窗  
  86.         if (keyCode == KeyEvent.KEYCODE_BACK  
  87.                 && event.getAction() == KeyEvent.ACTION_DOWN) {  
  88.             floatWindowManager.removeBigWindow();  
  89.             return true;  
  90.         }  
  91.   
  92.         return super.onKeyDown(keyCode, event);  
  93.     }  
  94.   
  95. }  

    项目下载地址: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设置很多参数,下面我们看一下常用的设置

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. // 设置LayoutParams参数  
  2.         LayoutParams params = new WindowManager.LayoutParams();  
  3.         //设置显示的类型,TYPE_PHONE指的是来电话的时候会被覆盖,其他时候会在最前端,显示位置在stateBar下面,其他更多的值请查阅文档  
  4.         params.type = WindowManager.LayoutParams.TYPE_PHONE;  
  5.         //设置显示格式  
  6.         params.format = PixelFormat.RGBA_8888;  
  7.         //设置对齐方式  
  8.         params.gravity = Gravity.LEFT | Gravity.TOP;  
  9.         //设置宽高  
  10.         params.width = ScreenUtils.getScreenWidth(this);  
  11.         params.height = ScreenUtils.getScreenHeight(this);  
  12.         //设置显示的位置  
  13.         params.x;  
  14.         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的一个对象,然后我们就可以获取到当前屏幕的一些参数,比如说宽高。

    下面是我常用的一个工具类。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. package com.qust.teachmask;  
  2.   
  3. import android.content.Context;  
  4. import android.view.WindowManager;  
  5.   
  6. /** 
  7.  * 屏幕帮助类 
  8.  *  
  9.  * @author zhaokaiqiang 
  10.  *  
  11.  */  
  12. public class ScreenUtils {  
  13.   
  14.     /** 
  15.      * 获取屏幕宽度 
  16.      *  
  17.      * @return 
  18.      */  
  19.     @SuppressWarnings("deprecation")  
  20.     public static int getScreenWidth(Context context) {  
  21.         return ((WindowManager) context  
  22.                 .getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay()  
  23.                 .getWidth();  
  24.     }  
  25.   
  26.     /** 
  27.      * 获取屏幕宽度 
  28.      *  
  29.      * @return 
  30.      */  
  31.     @SuppressWarnings("deprecation")  
  32.     public static int getScreenHeight(Context context) {  
  33.         return ((WindowManager) context  
  34.                 .getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay()  
  35.                 .getHeight();  
  36.     }  
  37.   
  38. }  

    知道上面这些之后,我们就可以实现教学模板效果了,首先看效果图。



    下面是代码实现

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. package com.qust.teachmask;  
  2.   
  3. import android.app.Activity;  
  4. import android.graphics.PixelFormat;  
  5. import android.os.Bundle;  
  6. import android.view.Gravity;  
  7. import android.view.View;  
  8. import android.view.View.OnClickListener;  
  9. import android.view.WindowManager;  
  10. import android.view.WindowManager.LayoutParams;  
  11. import android.widget.ImageView;  
  12. import android.widget.ImageView.ScaleType;  
  13.   
  14. public class MainActivity extends Activity {  
  15.   
  16.     private ImageView img;  
  17.   
  18.     private WindowManager windowManager;  
  19.   
  20.     @Override  
  21.     protected void onCreate(Bundle savedInstanceState) {  
  22.         super.onCreate(savedInstanceState);  
  23.         setContentView(R.layout.activity_main);  
  24.   
  25.         windowManager = getWindowManager();  
  26.   
  27.         // 动态初始化图层  
  28.         img = new ImageView(this);  
  29.         img.setLayoutParams(new LayoutParams(  
  30.                 android.view.ViewGroup.LayoutParams.MATCH_PARENT,  
  31.                 android.view.ViewGroup.LayoutParams.MATCH_PARENT));  
  32.         img.setScaleType(ScaleType.FIT_XY);  
  33.         img.setImageResource(R.drawable.guide);  
  34.   
  35.         // 设置LayoutParams参数  
  36.         LayoutParams params = new WindowManager.LayoutParams();  
  37.         // 设置显示的类型,TYPE_PHONE指的是来电话的时候会被覆盖,其他时候会在最前端,显示位置在stateBar下面,其他更多的值请查阅文档  
  38.         params.type = WindowManager.LayoutParams.TYPE_PHONE;  
  39.         // 设置显示格式  
  40.         params.format = PixelFormat.RGBA_8888;  
  41.         // 设置对齐方式  
  42.         params.gravity = Gravity.LEFT | Gravity.TOP;  
  43.         // 设置宽高  
  44.         params.width = ScreenUtils.getScreenWidth(this);  
  45.         params.height = ScreenUtils.getScreenHeight(this);  
  46.   
  47.         // 添加到当前的窗口上  
  48.         windowManager.addView(img, params);  
  49.   
  50.         // 点击图层之后,将图层移除  
  51.         img.setOnClickListener(new OnClickListener() {  
  52.   
  53.             @Override  
  54.             public void onClick(View arg0) {  
  55.                 windowManager.removeView(img);  
  56.             }  
  57.         });  
  58.   
  59.     }  
  60. }  

   github项目地址:https://github.com/ZhaoKaiQiang/TeachMask


版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享: