主要还是用到了WindowManager对桌面悬浮进行管理.
需要一个火箭的悬浮窗 一个发射台悬浮窗 ,判断火箭是否放到了发射台,如果放上了,则使用AsyTask 慢慢将火箭的图片往上移.结束后., 返回原位.
1.打开activity_main.xml
<RelativeLayout 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" tools:context="com.example.windowmanagerdemo1.MainActivity" > <Button android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/btn_start" android:text="start" /> </RelativeLayout>
就一个Button.
2.新建一个小悬浮窗的视图small_view.xml
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <LinearLayout android:layout_width="60dp" android:layout_height="20dp" android:id="@+id/ll_smallView" android:background="#000000" android:orientation="vertical" > <TextView android:layout_width="fill_parent" android:layout_height="fill_parent" android:id="@+id/tv_percent" android:textColor="#ffffff" /> </LinearLayout> <ImageView android:id="@+id/iv_recketImg" android:layout_width="45dp" android:layout_height="90dp" android:src="@drawable/rocket" android:visibility="gone" /> </FrameLayout>
在帧布局中放入一个LinearLayout,这个主要用来显示当前手机内存, 同时放入一个ImageView ,这个用于手指按下时显示出小火箭,平常小悬浮窗显示时则小火箭隐藏,当按住了小悬浮窗,则显示小火箭,隐藏悬浮窗.
3.新建 大悬浮窗视图 big_view.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="200dp" android:layout_height="100dp" android:id="@+id/ll_bigView" android:background="#000000" android:orientation="vertical" > <Button android:id="@+id/btn_close" android:layout_width="100dp" android:layout_height="40dp" android:layout_gravity="center_horizontal" android:text="关闭悬浮窗" android:textColor="#ffffff" /> <Button android:id="@+id/btn_back" android:layout_width="100dp" android:layout_height="40dp" android:layout_gravity="center_horizontal" android:text="返回" android:textColor="#ffffff" /> </LinearLayout>
大悬浮窗 是在点击了小悬浮窗后显示的,里面就放了两个按钮.
4.新建发射台的视图 luncher.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <ImageView android:id="@+id/iv_luncher" android:layout_width="200dp" android:layout_height="88dp" android:src="@drawable/launcher_bg_hold" /> </LinearLayout>
发射台视图只有一个ImageView.
5,新建一个FloatBigView.java 大悬浮窗的实体类
public class FloatBigView extends LinearLayout { //记录大悬浮窗的高度 public static int ViewHeight; //记录大悬浮窗的宽度 public static int ViewWidth; public FloatBigView(final Context context) { super(context); LayoutInflater.from(context).inflate(R.layout.big_view, this); View view=findViewById(R.id.ll_bigView); ViewHeight=view.getLayoutParams().height; ViewWidth=view.getLayoutParams().width; Button btn_close=(Button) findViewById(R.id.btn_close); Button btn_back=(Button) findViewById(R.id.btn_back); btn_close.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { MyWindowManager.removeBigView(context); MyWindowManager.removeSmallWindow(context); Intent intent=new Intent(getContext(), WindowService.class); context.stopService(intent); } }); btn_back.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { MyWindowManager.removeBigView(context); MyWindowManager.createSmallWindow(context); } }); } }
大悬浮窗的实体类 里面就是绑定刚才的大悬浮窗视图,并找到两个按钮,监听一个 关闭 和一个 返回的事件, MyWindowManager将在下面创建
6,小悬浮窗的实体类
public class FloatSmallView extends LinearLayout { public static int ViewHeight; public static int ViewWidth; //系统状态栏的高度 private static int statusHeight; private WindowManager mWindowManager; //小悬浮窗布局 private LinearLayout smallViewLayout; //小火箭 private ImageView rocketImg; //小悬浮窗参数 private WindowManager.LayoutParams mLayoutParams; //记录手指在屏幕按下时的横坐标 private float xDown; //记录手指在屏幕按下时的纵坐标 private float yDown; //记录手指在屏幕移动时的横坐标 private float xMove; //记录手指在屏幕移动时的纵坐标 private float yMove; //记录手指按下时在小悬浮窗的横坐标 private float xInView; //记录手指按下时在小悬浮窗的纵坐标 private float yInView; //记录小火箭的高度 private int rocketHeight; //记录小火箭的宽度 private int rocketWidth; //记录当前手指是否在悬浮窗按下 private boolean isPressed; public FloatSmallView(Context context) { super(context); mWindowManager=(WindowManager) context.getSystemService(Context.WINDOW_SERVICE); LayoutInflater.from(context).inflate(R.layout.small_view,this); smallViewLayout=(LinearLayout) findViewById(R.id.ll_smallView); ViewHeight=smallViewLayout.getLayoutParams().height; ViewWidth=smallViewLayout.getLayoutParams().width; rocketImg=(ImageView) findViewById(R.id.iv_recketImg); rocketHeight=rocketImg.getLayoutParams().height; rocketWidth=rocketImg.getLayoutParams().width; TextView tv_percent=(TextView) findViewById(R.id.tv_percent); tv_percent.setText(MyWindowManager.getUsedPercentValue(context)); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: isPressed = true; // 手指按下时记录必要数据,纵坐标的值都需要减去状态栏高度 xInView = event.getX(); yInView = event.getY(); xDown = event.getRawX(); yDown= event.getRawY() - getStatusBarHeight(); xMove = event.getRawX(); yMove = event.getRawY() - getStatusBarHeight(); break; case MotionEvent.ACTION_MOVE: xMove=event.getRawX(); yMove=event.getRawY()-getStatusBarHeight(); updateViewStatus(); updateViewPosition(); break; case MotionEvent.ACTION_UP: isPressed=false; if(MyWindowManager.isReadyToLuncher()){ luncherRocket(); } else{ updateViewStatus(); if(xDown==xMove&&yDown==yMove){ Log.e("info","进入1"); openBigWindow(); } } break; } return true; } //打开大悬浮窗口,关闭小悬浮窗 private void openBigWindow(){ Log.e("info","进入2"); MyWindowManager.createBigView(getContext()); MyWindowManager.removeSmallWindow(getContext()); } //开始发射小火箭 private void luncherRocket(){ MyWindowManager.removeRocketLuncher(getContext()); new LuncherTask().execute(); } //传入小悬浮窗的参数, 用于更新位置 public void setParams(WindowManager.LayoutParams mParams){ this.mLayoutParams=mParams; } //更新小悬浮窗的位置 private void updateViewPosition(){ mLayoutParams.x=(int) (xMove-xInView); mLayoutParams.y=(int) (yMove-yInView); mWindowManager.updateViewLayout(this, mLayoutParams); //检查小火箭是否到了发射台 MyWindowManager.updateLuncher(); } //更新悬浮窗的显示样式 ,如果是按下状态 则显示为小火箭 否则为普通悬浮窗 private void updateViewStatus(){ if(isPressed&&rocketImg.getVisibility()!=View.VISIBLE){ mLayoutParams.width=rocketWidth; mLayoutParams.height=rocketHeight; mWindowManager.updateViewLayout(this, mLayoutParams); smallViewLayout.setVisibility(View.GONE); rocketImg.setVisibility(View.VISIBLE); MyWindowManager.createLuncher(getContext()); } else if(!isPressed){ mLayoutParams.width=ViewWidth; mLayoutParams.height=ViewHeight; mWindowManager.updateViewLayout(this, mLayoutParams); smallViewLayout.setVisibility(View.VISIBLE); rocketImg.setVisibility(View.GONE); MyWindowManager.removeRocketLuncher(getContext()); } } //开始执行发射小火箭的任务 class LuncherTask extends AsyncTask<Void,Void, Void>{ @Override protected Void doInBackground(Void... arg0) { while(mLayoutParams.y>0){ mLayoutParams.y=mLayoutParams.y-20; publishProgress(); try { Thread.sleep(30); } catch (InterruptedException e) { e.printStackTrace(); } } return null; } @Override protected void onProgressUpdate(Void... values) { mWindowManager.updateViewLayout(FloatSmallView.this, mLayoutParams); } @Override protected void onPostExecute(Void result) { //小火箭升空后 回归悬浮窗状态 updateViewStatus(); mLayoutParams.x=(int) (xDown-xInView); mLayoutParams.y=(int) (yDown-yInView); mWindowManager.updateViewLayout(FloatSmallView.this, mLayoutParams); } } //获取状态栏的高度 private int getStatusBarHeight() { if (statusHeight == 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); statusHeight = getResources().getDimensionPixelSize(x); } catch (Exception e) { e.printStackTrace(); } } return statusHeight; } }
小悬浮窗中方法较多,注释较多
7.创建发射台实体类RocketLuncher.java
//火箭发射台 public class RocketLuncher extends LinearLayout { public static int width; public static int height; private ImageView luncherImg; public RocketLuncher(Context context) { super(context); LayoutInflater.from(context).inflate(R.layout.luncher, this); luncherImg=(ImageView) findViewById(R.id.iv_luncher); width=luncherImg.getLayoutParams().width; height=luncherImg.getLayoutParams().height; } //更新火箭发射台的显示状态 public void updateLuncherState(boolean isReadyToLuncher){ if(isReadyToLuncher){ luncherImg.setImageResource(R.drawable.launcher_bg_fire); } else{ luncherImg.setImageResource(R.drawable.launcher_bg_hold); } } }
8,新建一个MyWindowManager.java用来管理这几个悬浮窗
public class MyWindowManager { //小悬浮窗的实例 private static FloatSmallView mSmallView; //大悬浮窗的实例 private static FloatBigView mBigView; //火箭发射台的实例 private static RocketLuncher rocketLuncher; //小悬浮窗的参数 private static LayoutParams mSmallViewParams; //大悬浮窗的参数 private static LayoutParams mBigViewParams; //火箭发射台的参数 private static LayoutParams rocketLuncherParams; //用于在屏幕上添加或移除窗口 private static WindowManager mWindowManager; //获取手机可用内存 private static ActivityManager mActivityManager; //创建小悬浮窗 public static void createSmallWindow(Context context){ WindowManager windowManager=getWindowManager(context); int screenWidth=windowManager.getDefaultDisplay().getWidth(); int screenHeight=windowManager.getDefaultDisplay().getHeight(); if(mSmallView==null){ mSmallView=new FloatSmallView(context); if(mSmallViewParams==null){ mSmallViewParams=new LayoutParams(); mSmallViewParams.type=LayoutParams.TYPE_SYSTEM_ALERT; mSmallViewParams.format=PixelFormat.RGBA_8888; mSmallViewParams.flags=LayoutParams.FLAG_NOT_TOUCH_MODAL | LayoutParams.FLAG_NOT_FOCUSABLE; mSmallViewParams.gravity=Gravity.LEFT|Gravity.TOP; mSmallViewParams.width=FloatSmallView.ViewWidth; mSmallViewParams.height=FloatSmallView.ViewHeight; mSmallViewParams.x=screenWidth; mSmallViewParams.y=screenHeight/2; } mSmallView.setParams(mSmallViewParams); windowManager.addView(mSmallView, mSmallViewParams); } } //将小悬浮窗从屏幕上移除 public static void removeSmallWindow(Context context){ if(mSmallView!=null){ WindowManager windowManager=getWindowManager(context); windowManager.removeView(mSmallView); mSmallView=null; } } //创建大悬浮窗 public static void createBigView(Context context){ WindowManager windowManager=getWindowManager(context); int screenWidth=windowManager.getDefaultDisplay().getWidth(); int screenHeight=windowManager.getDefaultDisplay().getHeight(); if(mBigView==null){ mBigView=new FloatBigView(context); if(mBigViewParams==null){ mBigViewParams=new LayoutParams(); mBigViewParams.x=screenWidth/2-FloatBigView.ViewWidth/2; mBigViewParams.y=screenHeight/2-FloatBigView.ViewHeight/2; mBigViewParams.type=LayoutParams.TYPE_PHONE; mBigViewParams.format= PixelFormat.RGBA_8888; mBigViewParams.gravity=Gravity.LEFT|Gravity.TOP; mBigViewParams.width=FloatBigView.ViewWidth; mBigViewParams.height=FloatBigView.ViewHeight; } windowManager.addView(mBigView, mBigViewParams); } } //将大悬浮窗移除 public static void removeBigView(Context context){ if(mBigView!=null){ WindowManager windowManager=getWindowManager(context); windowManager.removeView(mBigView); mBigView=null; } } //创建一个火箭发射台 public static void createLuncher(Context context){ WindowManager windowManager=getWindowManager(context); int screenWidht=windowManager.getDefaultDisplay().getWidth(); int screenHeight=windowManager.getDefaultDisplay().getHeight(); if(rocketLuncher==null){ rocketLuncher=new RocketLuncher(context); if(rocketLuncherParams==null){ rocketLuncherParams=new LayoutParams(); rocketLuncherParams.x=screenWidht/2-RocketLuncher.width/2; rocketLuncherParams.y=screenHeight-RocketLuncher.height; rocketLuncherParams.type=LayoutParams.TYPE_PHONE; rocketLuncherParams.format=PixelFormat.RGBA_8888; rocketLuncherParams.gravity=Gravity.LEFT|Gravity.TOP; rocketLuncherParams.width=RocketLuncher.width; rocketLuncherParams.height=RocketLuncher.height; } windowManager.addView(rocketLuncher, rocketLuncherParams); } } //移除火箭发射台 public static void removeRocketLuncher(Context context){ if(rocketLuncher!=null){ WindowManager wm=getWindowManager(context); wm.removeView(rocketLuncher); rocketLuncher=null; } } //更新火箭发射台的显示状态 public static void updateLuncher(){ if(rocketLuncher!=null){ rocketLuncher.updateLuncherState(isReadyToLuncher()); } } //更新小悬浮窗TextView的内存占比 public static void updateUserPercent(Context context){ if(mSmallView!=null){ TextView percent=(TextView) mSmallView.findViewById(R.id.tv_percent); percent.setText(getUsedPercentValue(context)); } } //是否有悬浮窗 public static boolean isWindowShow(){ return mSmallView!=null||mBigView!=null; } /** * 计算已使用内存的百分比,并返回。 * * @param context * 可传入应用程序上下文。 * @return 已使用内存的百分比,以字符串形式返回。 */ public static String getUsedPercentValue(Context context) { String dir = "/proc/meminfo"; try { FileReader fr = new FileReader(dir); BufferedReader br = new BufferedReader(fr, 2048); String memoryLine = br.readLine(); String subMemoryLine = memoryLine.substring(memoryLine .indexOf("MemTotal:")); br.close(); long totalMemorySize = Integer.parseInt(subMemoryLine.replaceAll( "\D+", "")); long availableSize = getAvailableMemory(context) / 1024; int percent = (int) ((totalMemorySize - availableSize) / (float) totalMemorySize * 100); return percent + "%"; } catch (IOException e) { e.printStackTrace(); } return "悬浮窗"; } /** * 获取当前可用内存,返回数据以字节为单位。 * * @param context * 可传入应用程序上下文。 * @return 当前可用内存。 */ private static long getAvailableMemory(Context context) { ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo(); getActivityManager(context).getMemoryInfo(mi); return mi.availMem; } //判断小火箭是否准备好发射 public static boolean isReadyToLuncher(){ if ((mSmallViewParams.x > rocketLuncherParams.x && mSmallViewParams.x + mSmallViewParams.width < rocketLuncherParams.x + rocketLuncherParams.width) && (mSmallViewParams.y + mSmallViewParams.height > rocketLuncherParams.y)) { return true; } return false; } //得到WindowManager的单例 private static WindowManager getWindowManager(Context context){ if(mWindowManager==null){ mWindowManager=(WindowManager) context.getSystemService(Context.WINDOW_SERVICE); } return mWindowManager; } //得到ActivityManager的单例 private static ActivityManager getActivityManager(Context context){ if(mActivityManager==null){ mActivityManager=(ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); } return mActivityManager; } }
9.悬浮窗的服务WindowService.java,用来时刻检查当前视图是否该显示悬浮窗还是隐藏.
public class WindowService extends Service { //在线程中创建或移除悬浮窗 private Handler handler=new Handler(); //定时器 检查当前是应该创建还是移除悬浮窗 private Timer mTimer; @Override public int onStartCommand(Intent intent, int flags, int startId) { if(mTimer==null){ mTimer=new Timer(); mTimer.scheduleAtFixedRate(new RefreshTaks(), 0, 500); } return super.onStartCommand(intent, flags, startId); } @Override public void onDestroy() { super.onDestroy(); mTimer.cancel(); mTimer=null; } class RefreshTaks extends TimerTask{ @Override public void run() { //如果当前是桌面且没有窗口显示,则创建小悬浮窗 if(isHome()&&!MyWindowManager.isWindowShow()){ handler.post(new Runnable() { @Override public void run() { MyWindowManager.createSmallWindow(getApplicationContext()); } }); } //如果不是桌面,且有悬浮窗显示,则移除悬浮窗 else if(!isHome()&&MyWindowManager.isWindowShow()){ handler.post(new Runnable() { @Override public void run() { MyWindowManager.removeBigView(getApplicationContext()); MyWindowManager.removeSmallWindow(getApplicationContext()); } }); } //当前是桌面 且有悬浮窗显示 则更新内存 else if(isHome()&&MyWindowManager.isWindowShow()){ handler.post(new Runnable() { @Override public void run() { MyWindowManager.updateUserPercent(getApplicationContext()); } }); } } } /** * 判断当前界面是否是桌面 */ 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; } @Override public IBinder onBind(Intent arg0) { return null; } }
10.打开MainActivity.java
public class MainActivity extends ActionBarActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button btn_start=(Button) findViewById(R.id.btn_start); btn_start.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Intent intent=new Intent(MainActivity.this, WindowService.class); MainActivity.this.startService(intent); MainActivity.this.finish(); } }); } }
开启服务.