zoukankan      html  css  js  c++  java
  • 在桌面添加可拖动/点击的悬浮窗口

    用过新版本android 360手机助手都人都对 360中只在桌面显示一个小小悬浮窗口羡慕不已吧?

    其实实现这种功能,主要有两步
    1.判断当前显示的是为桌面。

    2.使用windowManager往最顶层添加一个View
    .这个知识点就是为本文主要讲解的内容哦。在本文的讲解中,我们还会讲到下面的知识点:
      a.如果获取到状态栏的高度
      b.悬浮窗口的拖动
      c.悬浮窗口的点击事件


    有开始之前,我们先来看一下效果图:

    接下来我们来看看FloatView的代码:

    View Code
     1 public class FloatView extends ImageView{ 
     2         private float mTouchX; 
     3         private float mTouchY; 
     4         private float x; 
     5         private float y; 
     6         private float mStartX; 
     7         private float mStartY; 
     8         private OnClickListener mClickListener; 
     9   
    10         private WindowManager windowManager = (WindowManager) getContext() 
    11                         .getApplicationContext().getSystemService(Context.WINDOW_SERVICE); 
    12         // 此windowManagerParams变量为获取的全局变量,用以保存悬浮窗口的属性 
    13         private WindowManager.LayoutParams windowManagerParams = ((FloatApplication) getContext() 
    14                         .getApplicationContext()).getWindowParams(); 
    15   
    16         public FloatView(Context context) { 
    17                 super(context); 
    18         } 
    19   
    20         @Override
    21         public boolean onTouchEvent(MotionEvent event) { 
    22                 //获取到状态栏的高度 
    23                 Rect frame =  new  Rect();   
    24                 getWindowVisibleDisplayFrame(frame); 
    25                 int  statusBarHeight = frame.top;  
    26                 System.out.println("statusBarHeight:"+statusBarHeight); 
    27                 // 获取相对屏幕的坐标,即以屏幕左上角为原点 
    28                 x = event.getRawX(); 
    29                 y = event.getRawY() - statusBarHeight; // statusBarHeight是系统状态栏的高度 
    30                 Log.i("tag", "currX" + x + "====currY" + y); 
    31                 switch (event.getAction()) { 
    32                 case MotionEvent.ACTION_DOWN: // 捕获手指触摸按下动作 
    33                         // 获取相对View的坐标,即以此View左上角为原点 
    34                         mTouchX = event.getX(); 
    35                         mTouchY = event.getY(); 
    36                         mStartX = x; 
    37                         mStartY = y; 
    38                         Log.i("tag", "startX" + mTouchX + "====startY"
    39                                         + mTouchY); 
    40                         break; 
    41   
    42                 case MotionEvent.ACTION_MOVE: // 捕获手指触摸移动动作 
    43                         updateViewPosition(); 
    44                         break; 
    45   
    46                 case MotionEvent.ACTION_UP: // 捕获手指触摸离开动作 
    47                         updateViewPosition(); 
    48                         mTouchX = mTouchY = 0; 
    49                         if ((x - mStartX) < 5 && (y - mStartY) < 5) { 
    50                                 if(mClickListener!=null) { 
    51                                         mClickListener.onClick(this); 
    52                                 } 
    53                         } 
    54                         break; 
    55                 } 
    56                 return true; 
    57         } 
    58         @Override
    59         public void setOnClickListener(OnClickListener l) { 
    60                 this.mClickListener = l; 
    61         } 
    62         private void updateViewPosition() { 
    63                 // 更新浮动窗口位置参数 
    64                 windowManagerParams.x = (int) (x - mTouchX); 
    65                 windowManagerParams.y = (int) (y - mTouchY); 
    66                 windowManager.updateViewLayout(this, windowManagerParams); // 刷新显示 
    67         } 
    68 }

    代码解释:
    int  statusBarHeight = frame.top;

    为获取状态栏的高度,为什么在event.getRawY()的时候减去状态栏的高度呢?
        
    因为我们的悬浮窗口不可能显示到状态栏中去,而后getRawY为获取到屏幕原点的距离。当我们屏幕处于全屏模式时,获取到的状态栏高度会变成0 
        (x - mStartX) < 5 && (y - mStartY) < 5
       
    如果我们在触摸过程中,移动距离少于5 ,则视为点击,触发点击的回调。

    另外我们需要自定义一个application:

    View Code
    1 public class FloatApplication extends Application { 
    2         private WindowManager.LayoutParams windowParams = new WindowManager.LayoutParams(); 
    3   
    4         public WindowManager.LayoutParams getWindowParams() { 
    5                 return windowParams; 
    6         } 
    7 }

    代码解释:
      自定义application的目的是为了保存windowParams的值 ,因为我们在拖动悬浮窗口的时候,如果每次都重新new一个layoutParams的话,在update
    的时候会在异常发现。
      windowParams的值也不一定非得在自定义application里面来保存,只要是全局的都行。

    最后我们再来看看Activity中的实现。

    View Code
     1 public class MainActivity extends Activity  implements OnClickListener{ 
     2         private WindowManager windowManager = null; 
     3         private WindowManager.LayoutParams windowManagerParams = null; 
     4         private FloatView floatView = null; 
     5   
     6         @Override
     7     public void onCreate(Bundle savedInstanceState) { 
     8         super.onCreate(savedInstanceState); 
     9         requestWindowFeature(Window.FEATURE_NO_TITLE);//取消标题栏 
    10         getWindow().setFlags(WindowManager.LayoutParams. FLAG_FULLSCREEN , 
    11                       WindowManager.LayoutParams. FLAG_FULLSCREEN);//全屏 
    12         setContentView(R.layout.activity_main); 
    13                createView();  
    14     } 
    15   
    16         @Override
    17         public boolean onCreateOptionsMenu(Menu menu) { 
    18                 getMenuInflater().inflate(R.menu.activity_main, menu); 
    19                 return true; 
    20         } 
    21   
    22         public void onDestroy() { 
    23                 super.onDestroy(); 
    24                 // 在程序退出(Activity销毁)时销毁悬浮窗口 
    25                 windowManager.removeView(floatView); 
    26         } 
    27   
    28         private void createView() { 
    29                 floatView = new FloatView(getApplicationContext()); 
    30                 floatView.setOnClickListener(this); 
    31                 floatView.setImageResource(R.drawable.ic_launcher); // 这里简单的用自带的icon来做演示 
    32                 // 获取WindowManager 
    33                 windowManager = (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE); 
    34                 // 设置LayoutParams(全局变量)相关参数 
    35                 windowManagerParams = ((FloatApplication) getApplication()).getWindowParams(); 
    36   
    37                 windowManagerParams.type = LayoutParams.TYPE_PHONE; // 设置window type 
    38                 windowManagerParams.format = PixelFormat.RGBA_8888; // 设置图片格式,效果为背景透明 
    39                 // 设置Window flag 
    40                 windowManagerParams.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL 
    41                                 | LayoutParams.FLAG_NOT_FOCUSABLE; 
    42                 /* 
    43                  * 注意,flag的值可以为: 
    44                  * LayoutParams.FLAG_NOT_TOUCH_MODAL 不影响后面的事件 
    45                  * LayoutParams.FLAG_NOT_FOCUSABLE  不可聚焦 
    46                  * LayoutParams.FLAG_NOT_TOUCHABLE 不可触摸 
    47                  */
    48                 // 调整悬浮窗口至左上角,便于调整坐标 
    49                 windowManagerParams.gravity = Gravity.LEFT | Gravity.TOP;  
    50                 // 以屏幕左上角为原点,设置x、y初始值 
    51                 windowManagerParams.x = 0; 
    52                 windowManagerParams.y = 0; 
    53                 // 设置悬浮窗口长宽数据 
    54                 windowManagerParams.width = LayoutParams.WRAP_CONTENT; 
    55                 windowManagerParams.height = LayoutParams.WRAP_CONTENT; 
    56                 // 显示myFloatView图像 
    57                 windowManager.addView(floatView, windowManagerParams); 
    58         } 
    59   
    60         public void onClick(View v) { 
    61                 Toast.makeText(this, "Clicked", Toast.LENGTH_SHORT).show(); 
    62         } 
    63 }

    代码解释:    
       在activity中我们主要是添加悬浮窗,并且设置他的位置。另外需要注意flags的应用:

    LayoutParams.FLAG_NOT_TOUCH_MODAL 不影响后面的事件
    LayoutParams.FLAG_NOT_FOCUSABLE  不可聚焦
    LayoutParams.FLAG_NOT_TOUCHABLE 不可触摸

    最后我们在onDestroy()中移除到悬浮窗口。所以,我们测试的时候,记得按Home键来切换到桌面。           
    最后千万记得,在androidManifest.xml中来申明我们需要用到的android.permission.SYSTEM_ALERT_WINDOW权限
    并且记得申明我们自定义的application哦。
    AndroidManifest.xml代码如下:

    View Code
     1 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     2     package="com.krislq.floating"
     3     android:versionCode="1"
     4     android:versionName="1.0" > 
     5   
     6     <uses-sdk 
     7         android:minSdkVersion="8"
     8         android:targetSdkVersion="15" /> 
     9         <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> 
    10     <application 
    11         android:icon="@drawable/ic_launcher"
    12         android:label="@string/app_name"
    13         android:theme="@style/AppTheme" android:name="FloatApplication"> 
    14         <activity 
    15             android:name=".MainActivity"
    16             android:label="@string/title_activity_main" > 
    17             <intent-filter> 
    18                 <action android:name="android.intent.action.MAIN" /> 
    19   
    20                 <category android:name="android.intent.category.LAUNCHER" /> 
    21             </intent-filter> 
    22         </activity> 
    23     </application> 
    24 </manifest>
  • 相关阅读:
    优先队列
    Problem W UVA 662 二十三 Fast Food
    UVA 607 二十二 Scheduling Lectures
    UVA 590 二十一 Always on the run
    UVA 442 二十 Matrix Chain Multiplication
    UVA 437 十九 The Tower of Babylon
    UVA 10254 十八 The Priest Mathematician
    UVA 10453 十七 Make Palindrome
    UVA 10163 十六 Storage Keepers
    UVA 1252 十五 Twenty Questions
  • 原文地址:https://www.cnblogs.com/QLJ1314/p/2796547.html
Copyright © 2011-2022 走看看