Android 悬浮窗的创建与移除
2015-08-05 14:11
471 查看
本文主要是介绍悬浮窗的创建与移除,并能通过点击悬浮窗图标回到应用主界面。本文是通过参考网上的文章并通过自己修改完成的。本文主要参考的文章是:http://blog.csdn.net/guolin_blog/article/details/8689140
1.MainActivity.java
2.FloatWindowService.java
3.FloatWindowSmallView.java
4.MyWindowManager.java
5.布局文件1:activity_main.xml
6.布局文件2:float_window.xml
7.配置文件AndroidManifest.xml
添加权限
记得要注册相应的Activity和Service.
1.MainActivity.java
import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; public class MainActivity extends Activity implements OnClickListener { private Button startFloatWindow; private Button stopFloatWindow; private TextView tv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); startFloatWindow = (Button) findViewById(R.id.start_float_window); stopFloatWindow = (Button) findViewById(R.id.stop_float_window); tv= (TextView) findViewById(R.id.show_txt); String str=new StringBuilder() .append("\n") .append("说明:").append("\n") .append("1.点击开启悬浮窗按钮可以看到悬浮窗;").append("\n") .append("2.点击关闭悬浮窗按钮可以移除悬浮窗;").append("\n") .append("3.点击悬浮窗图标可以进入应用主界面.").append("\n") .toString(); tv.setText(str); startFloatWindow.setOnClickListener(this); stopFloatWindow.setOnClickListener(this); } public void onClick(View v) { switch(v.getId()) { case R.id.start_float_window: Intent serviceStart = new Intent(MainActivity.this, FloatWindowService.class); startService(serviceStart ); finish(); break; case R.id.stop_float_window: Intent serviceStop = new Intent(MainActivity.this, FloatWindowService.class); stopService(serviceStop ); finish(); break; } } }
2.FloatWindowService.java
import java.util.ArrayList; import java.util.List; import java.util.Timer; import java.util.TimerTask; import android.app.ActivityManager; import android.app.ActivityManager.RunningTaskInfo; import android.app.Service; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.os.Handler; import android.os.IBinder; public class FloatWindowService extends Service { /** * 用于在线程中创建或移除悬浮窗。 */ private Handler handler = new Handler(); /** * 定时器,定时进行检测当前应该创建还是移除悬浮窗。 */ private Timer timer; @Override public IBinder onBind(Intent intent) { return null; } @Override public int onStartCommand(Intent intent, int flags, int startId) { // 开启定时器,每隔0.5秒刷新一次 if (timer == null) { timer = new Timer(); timer.scheduleAtFixedRate(new RefreshTask(), 0, 500); } return super.onStartCommand(intent, flags, startId); } @Override public void onDestroy() { super.onDestroy(); // Service被终止的同时也停止定时器继续运行 timer.cancel(); timer = null; } class RefreshTask extends TimerTask { @Override public void run() { // 当前界面是桌面,且没有悬浮窗显示,则创建悬浮窗。 if (isHome() && !MyWindowManager.isWindowShowing()) { handler.post(new Runnable() { @Override public void run() { MyWindowManager.createSmallWindow(getApplicationContext()); } }); } // 当前界面不是桌面,且有悬浮窗显示,则移除悬浮窗。 else if (!isHome() && MyWindowManager.isWindowShowing()) { handler.post(new Runnable() { @Override public void run() { MyWindowManager.removeSmallWindow(getApplicationContext()); } }); } } } /** * 判断当前界面是否是桌面 */ @SuppressWarnings("deprecation") private boolean isHome() { ActivityManager mActivityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); List<RunningTaskInfo> rti = mActivityManager.getRunningTasks(1); return getHomes().contains(rti.get(0).topActivity.getPackageName()); } /** * 获得属于桌面的应用的应用包名称 * * @return 返回包含所有包名的字符串列表 */ private List<String> getHomes() { List<String> names = new ArrayList<String>(); PackageManager packageManager = this.getPackageManager(); Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_HOME); List<ResolveInfo> resolveInfo = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); for (ResolveInfo ri : resolveInfo) { names.add(ri.activityInfo.packageName); } return names; } }
3.FloatWindowSmallView.java
import java.lang.reflect.Field; import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.WindowManager; import android.widget.ImageView; import android.widget.LinearLayout; @SuppressLint("ClickableViewAccessibility") public class FloatWindowSmallView extends LinearLayout { /** * 记录小悬浮窗的宽度 */ public static int viewWidth; /** * 记录小悬浮窗的高度 */ public static int viewHeight; /** * 记录系统状态栏的高度 */ private static int statusBarHeight; /** * 用于更新小悬浮窗的位置 */ private WindowManager windowManager; /** * 小悬浮窗的参数 */ private WindowManager.LayoutParams mParams; /** * 记录当前手指位置在屏幕上的横坐标值 */ private float xInScreen; /** * 记录当前手指位置在屏幕上的纵坐标值 */ private float yInScreen; /** * 记录手指按下时在屏幕上的横坐标的值 */ private float xDownInScreen; /** * 记录手指按下时在屏幕上的纵坐标的值 */ private float yDownInScreen; /** * 记录手指按下时在小悬浮窗的View上的横坐标的值 */ private float xInView; /** * 记录手指按下时在小悬浮窗的View上的纵坐标的值 */ private float yInView; private ImageView mfloat; public static int sViewWidth; public static int sViewHeight; public FloatWindowSmallView(Context context) { super(context); windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); LayoutInflater.from(context).inflate(R.layout.float_window, this); View view = findViewById(R.id.float_window_layout); viewWidth = view.getLayoutParams().width; viewHeight = view.getLayoutParams().height; mfloat = (ImageView) findViewById(R.id.iv_btn); mfloat.setBackgroundResource(R.drawable.float_ic); sViewWidth = view.getLayoutParams().width; sViewHeight = view.getLayoutParams().height; } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: // 手指按下时记录必要数据,纵坐标的值都需要减去状态栏高度 xInView = event.getX(); yInView = event.getY(); xDownInScreen = event.getRawX(); yDownInScreen = event.getRawY() - getStatusBarHeight(); xInScreen = event.getRawX(); yInScreen = event.getRawY() - getStatusBarHeight(); break; case MotionEvent.ACTION_MOVE: xInScreen = event.getRawX(); yInScreen = event.getRawY() - getStatusBarHeight(); // 手指移动的时候更新小悬浮窗的位置 updateViewPosition(); break; case MotionEvent.ACTION_UP: // 如果手指离开屏幕时,xDownInScreen和xInScreen相等,且yDownInScreen和yInScreen相等,则视为触发了单击事件。 if (xDownInScreen == xInScreen && yDownInScreen == yInScreen) { Intent intent = new Intent(getContext(),MainActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); getContext().startActivity(intent); } break; default: break; } return true; } /** * 将小悬浮窗的参数传入,用于更新小悬浮窗的位置。 * * @param params * 小悬浮窗的参数 */ public void setParams(WindowManager.LayoutParams params) { mParams = params; } /** * 更新小悬浮窗在屏幕中的位置。 */ private void updateViewPosition() { mParams.x = (int) (xInScreen - xInView); mParams.y = (int) (yInScreen - yInView); windowManager.updateViewLayout(this, mParams); } /** * 用于获取状态栏的高度。 * * @return 返回状态栏高度的像素值。 */ private int getStatusBarHeight() { if (statusBarHeight == 0) { try { Class<?> c = Class.forName("com.android.internal.R$dimen"); Object o = c.newInstance(); Field field = c.getField("status_bar_height"); int x = (Integer) field.get(o); statusBarHeight = getResources().getDimensionPixelSize(x); } catch (Exception e) { e.printStackTrace(); } } return statusBarHeight; } }
4.MyWindowManager.java
import android.app.ActivityManager; import android.content.Context; import android.graphics.PixelFormat; import android.view.Gravity; import android.view.WindowManager; import android.view.WindowManager.LayoutParams; public class MyWindowManager { /** * 小悬浮窗View的实例 */ private static FloatWindowSmallView smallWindow; /** * 小悬浮窗View的参数 */ private static LayoutParams smallWindowParams; /** * 用于控制在屏幕上添加或移除悬浮窗 */ private static WindowManager mWindowManager; /** * 用于获取手机可用内存 */ private static ActivityManager mActivityManager; /** * 创建一个小悬浮窗。初始位置为屏幕的右部中间位置。 * * @param context * 必须为应用程序的Context. */ @SuppressWarnings("deprecation") public static void createSmallWindow(Context context) { WindowManager windowManager = getWindowManager(context); int screenWidth = windowManager.getDefaultDisplay().getWidth(); int screenHeight = windowManager.getDefaultDisplay().getHeight(); if (smallWindow == null) { smallWindow = new FloatWindowSmallView(context); if (smallWindowParams == null) { smallWindowParams = new LayoutParams(); smallWindowParams.type = LayoutParams.TYPE_PHONE; smallWindowParams.format = PixelFormat.RGBA_8888; smallWindowParams.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL | LayoutParams.FLAG_NOT_FOCUSABLE; smallWindowParams.gravity = Gravity.LEFT | Gravity.TOP; smallWindowParams.width = FloatWindowSmallView.viewWidth; smallWindowParams.height = FloatWindowSmallView.viewHeight; smallWindowParams.x = screenWidth; smallWindowParams.y = screenHeight / 2; } smallWindow.setParams(smallWindowParams); windowManager.addView(smallWindow, smallWindowParams); } } /** * 将小悬浮窗从屏幕上移除。 * * @param context * 必须为应用程序的Context. */ public static void removeSmallWindow(Context context) { if (smallWindow != null) { WindowManager windowManager = getWindowManager(context); windowManager.removeView(smallWindow); smallWindow = null; } } /** * 是否有悬浮窗(包括小悬浮窗和大悬浮窗)显示在屏幕上。 * * @return 有悬浮窗显示在桌面上返回true,没有的话返回false。 */ public static boolean isWindowShowing() { return smallWindow != null ; } /** * 如果WindowManager还未创建,则创建一个新的WindowManager返回。否则返回当前已创建的WindowManager。 * * @param context * 必须为应用程序的Context. * @return WindowManager的实例,用于控制在屏幕上添加或移除悬浮窗。 */ private static WindowManager getWindowManager(Context context) { if (mWindowManager == null) { mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); } return mWindowManager; } /** * 如果ActivityManager还未创建,则创建一个新的ActivityManager返回。否则返回当前已创建的ActivityManager。 * * @param context * 可传入应用程序上下文。 * @return ActivityManager的实例,用于获取手机可用内存。 */ @SuppressWarnings("unused") private static ActivityManager getActivityManager(Context context) { if (mActivityManager == null) { mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); } return mActivityManager; } }
5.布局文件1:activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="60dip" android:textStyle="bold" android:textSize="24dip" android:text="悬浮窗功能测试" android:layout_gravity="center"/> <Button android:id="@+id/start_float_window" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="开启悬浮窗" > </Button> <Button android:id="@+id/stop_float_window" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="关闭悬浮窗" > </Button> <TextView android:id="@+id/show_txt" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="20dip" android:text="" /> </LinearLayout>
6.布局文件2:float_window.xml
<?xml version="1.0" encoding="UTF-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:id="@+id/float_window_layout" android:layout_width="wrap_content" android:layout_height="wrap_content"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/iv_btn" android:contentDescription="@null"/> </LinearLayout>
7.配置文件AndroidManifest.xml
添加权限
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <uses-permission android:name="android.permission.GET_TASKS"/>
记得要注册相应的Activity和Service.
相关文章推荐
- Android中的Cursor关闭问题
- Android中Service(服务)详解
- android学习三:json的使用
- Android开发之注解式框架ButterKnife在ADT中的设置
- Android Api Demos登顶之路(二十)Screen Orientation
- Android使用Handler实现线程池的效果,实现照片墙应用
- android获取共享文件信息
- android模拟器访问本地ip
- Android studio gradle debug模式用Release签名
- Android 4.0 事件输入(Event Input)系统
- Android性能调优
- Android getevent/sendevent详解
- Android中添加布局和初始化布局总结
- 如何“任性”使用Android的drawText()
- Facebook工程师是如何改进他们Android客户端的
- Android中批处理drawable-xxx目录中图片资源的那些事儿
- Android Wifi锁之WifiLock
- Android中ListView包含CheckBox时滑动丢失选中状态的解决
- Android进程与内存及内存泄露
- android应用中去掉标题栏的方法