这里主要用到WindowManager类,通过WindowManager的addView(View view, WindowManager.LayoutParams params)方法可以在窗体中添加view对象,如果希望浮动窗体随手指一块移动,应该修改params参数并通过windowManager对象的updateViewLayout()方法完成更新,并配合service!对于WindowManager.LayoutParams这里不做介绍,不了解的朋友可以网上查~
<!-- 浮动窗体 需申请权限 --> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />/** * * @author zimo2013 * @see http://blog.csdn.net/zimo2013 * */ public class MyService extends Service { private MyWindowManager windowManager; /* * 这里使用线程池,比如访问网络数据的实时更新,主要完成于悬浮框的通信,可根据需要实现,这里并没有实现 */ private ScheduledExecutorService threadPool; @Override public void onCreate() { super.onCreate(); } @Override public IBinder onBind(Intent intent) { return null; } @Override public int onStartCommand(Intent intent, int flags, int startId) { if (windowManager == null) { windowManager = MyWindowManager .getInstance(getApplicationContext()); } if (threadPool == null) { threadPool = Executors.newScheduledThreadPool(1); threadPool.scheduleAtFixedRate(command, 0, 1, TimeUnit.SECONDS); } //开启悬浮框 windowManager.show(); return super.onStartCommand(intent, flags, startId); } private Runnable command = new Runnable() { @Override public void run() { // 在该方法中,定时更新ui,比如访问网络数据的实时更新,主要完成于悬浮框的通信,这里暂不实现 System.out.println("run()"); } }; public void onDestroy() { super.onDestroy(); if (threadPool != null) { threadPool.shutdown(); threadPool = null; } } }/** * 单例设计 * * @author zimo2013 * @see http://blog.csdn.net/zimo2013 * */ public class MyWindowManager { private static MyWindowManager manager; private MyWindowManager() { } private static WindowManager winManager; private static Context context; private LayoutParams params; private LayoutParams paramsContent; /** * 显示的浮动窗体 */ private FloatView floatView; /** * 点击浮动窗体后的详细信息 */ private FloatContentView floatContentView; // 屏幕的尺寸 private static int displayWidth; private static int displayHeight; /** * @param context * ApplicationContext * @return */ public static synchronized MyWindowManager getInstance(Context context) { if (manager == null) { MyWindowManager.context = context; winManager = (WindowManager) context .getSystemService(Context.WINDOW_SERVICE); displayWidth = winManager.getDefaultDisplay().getWidth(); displayHeight = winManager.getDefaultDisplay().getHeight(); manager = new MyWindowManager(); } return manager; } /** * 显示悬浮框 */ public void show() { floatView = getView(); if (floatView.getParent() == null) { winManager.addView(floatView, params); } } /** * 点击悬浮框后的显示的实时详细流量信息 */ public void showContent() { winManager.addView(getContentView(), paramsContent); // 先移除悬浮框 winManager.removeView(floatView); floatView = null; } // 移动悬浮框 public void move(View view, int delatX, int deltaY) { if (view == floatView) { params.x += delatX; params.y += deltaY; // 更新floatView winManager.updateViewLayout(view, params); } } /** * 移除悬浮框 */ public void dismiss() { winManager.removeView(floatContentView); floatContentView = null; } public void back() { winManager.addView(getView(), params); winManager.removeView(floatContentView); floatContentView = null; } /** * 是否实时更新流量数据,主要用于流量悬浮框是否实时更新流量数据 * * @return */ public boolean isUpdate() { if (floatContentView == null) { return false; } return true; } /** * 得到悬浮框view * * @return */ public FloatView getView() { if (floatView == null) { floatView = new FloatView(context); } if (params == null) { params = new LayoutParams(); params.type = LayoutParams.TYPE_PHONE; params.format = PixelFormat.RGBA_8888; params.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL | LayoutParams.FLAG_NOT_FOCUSABLE; params.gravity = Gravity.LEFT | Gravity.TOP; // 须指定宽度高度信息 params.width = floatView.mWidth; params.height = floatView.mHeight; params.x = displayWidth - floatView.mWidth; params.y = displayHeight / 2; } return floatView; } /** * 得到实时流量悬浮框view * * @return */ public View getContentView() { if (floatContentView == null) { floatContentView = new FloatContentView(context); } if (paramsContent == null) { paramsContent = new LayoutParams(); paramsContent.type = LayoutParams.TYPE_PHONE; paramsContent.format = PixelFormat.RGBA_8888; paramsContent.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL | LayoutParams.FLAG_NOT_FOCUSABLE; paramsContent.gravity = Gravity.LEFT | Gravity.TOP; // 须指定宽度高度信息 paramsContent.width = floatContentView.mWidth; paramsContent.height = floatContentView.mHeight; paramsContent.x = (displayWidth - floatContentView.mWidth) / 2; paramsContent.y = (displayHeight - floatContentView.mHeight) / 2; } return floatContentView; } }public class FloatView extends LinearLayout { private TextView tvInfo; private MyWindowManager manager; public int mWidth; public int mHeight; private int preX; private int preY; private int x; private int y; private boolean isMove; public FloatView(Context context) { this(context, null); } public FloatView(Context context, AttributeSet attrs) { super(context, attrs); // 填充布局,并添加至 LayoutInflater.from(context).inflate(R.layout.float_view, this); tvInfo = (TextView) findViewById(R.id.tv_info); //宽高 mWidth = tvInfo.getLayoutParams().width; mHeight = tvInfo.getLayoutParams().height; manager = MyWindowManager.getInstance(context); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: preX = (int) event.getRawX(); preY = (int) event.getRawY(); isMove = false; break; case MotionEvent.ACTION_MOVE: x = (int) event.getRawX(); y = (int) event.getRawY(); manager.move(this, x - preX, y - preY); isMove = true; preX = x; preY = y; break; case MotionEvent.ACTION_UP: if (!isMove) {//是否是移动,主要是区分click //显示悬浮框详细流量信息 manager.showContent(); } break; } return super.onTouchEvent(event); } }public class FloatContentView extends LinearLayout implements OnClickListener { public int mWidth; public int mHeight; private MyWindowManager manager; private PackageManager pkgManager; private LayoutInflater inflater; private TrafficAdapter adapter; private Button btCLose; private Button btBack; private ListView listView; public FloatContentView(Context context) { this(context, null); } public FloatContentView(Context context, AttributeSet attrs) { super(context, attrs); pkgManager = context.getPackageManager(); // 填充布局,并添加至 inflater = LayoutInflater.from(context); inflater.inflate(R.layout.float_content_view, this); manager = MyWindowManager.getInstance(context); ViewGroup.LayoutParams params = findViewById(R.id.content) .getLayoutParams(); mWidth = params.width; mHeight = params.height; btCLose = (Button) findViewById(R.id.bt_close); btBack = (Button) findViewById(R.id.bt_back); listView = (ListView) findViewById(R.id.list); btCLose.setOnClickListener(this); btBack.setOnClickListener(this); adapter = new TrafficAdapter(); listView.setAdapter(adapter); //消息,主要完成流量的实时更新 handler.sendEmptyMessage(0); } // 主要用于实时更新 private Handler handler = new Handler() { public void handleMessage(android.os.Message msg) { // 更新流信息 if (manager.isUpdate()) { adapter.notifyDataSetChanged(); handler.sendEmptyMessageDelayed(0, 2 * 1000); } } }; @Override public void onClick(View v) { if (v == btCLose) { manager.dismiss(); } else if (v == btBack) { manager.back(); } } private class TrafficAdapter extends BaseAdapter { private List<ResolveInfo> infos; TrafficAdapter() { Intent intent = new Intent(); intent.setAction(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_LAUNCHER); //根据意图,查询得到与之对应的list信息 infos = pkgManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); } @Override public int getCount() { return infos.size(); } @Override public Object getItem(int position) { return infos.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = inflater.inflate(R.layout.item, null); convertView.setTag(new ViewHolder(convertView)); } ViewHolder holder = (ViewHolder) convertView.getTag(); ResolveInfo info = infos.get(position); holder.tvPkgName.setText(info.loadLabel(pkgManager).toString()); try { //得到uid int uid = pkgManager.getPackageInfo(info.activityInfo.packageName, 0).applicationInfo.uid; //TrafficStats查询具体流量数据 holder.tvRcvSize.setText(SizeFormaterUtil.getDataSize(TrafficStats .getUidRxBytes(uid))); holder.tvSndSize.setText(SizeFormaterUtil.getDataSize(TrafficStats .getUidTxBytes(uid))); } catch (NameNotFoundException e) { e.printStackTrace(); } return convertView; } } private class ViewHolder { TextView tvPkgName; TextView tvRcvSize; TextView tvSndSize; public ViewHolder(View view) { tvPkgName = (TextView) view.findViewById(R.id.tv_name); tvRcvSize = (TextView) view.findViewById(R.id.tv_rcv); tvSndSize = (TextView) view.findViewById(R.id.tv_snd); } } }源码下载>>