Introduction to this section:
Among the system services provided by Android in this section, WindowManager (window management service) is the bottom layer of View. Toast, Activity, and Dialog are all used in the bottom layer of WindowManager. It is global. ! The core of this class is nothing more than: call addView, removeView, updateViewLayout these methods to display the View and set related properties through the WindowManager.LayoutParams API!
In this section, let's discuss some application examples of this WindowManager in actual development~
Official API documentation: WindowManager
1. Some concepts of WindowManager:
1) Introduction to WindowManager
An API provided by Android for interacting with the window manager! We all know that the interface of App is composed of Acitivty one by one, and Activity is composed of View. When we want to display an interface, the first thing we think of is: Activity, right? Or Dialog and Toast.
But in some cases, the first three may not meet our needs. For example, we are just a simple display Activity, which seems a bit redundant, and Dialog needs Context objects, and Toast cannot be clicked... For the above situations We can use WindowManager to add View to the screen, or remove View from the screen! It is an interface to manage the Android window mechanism, displaying the bottom layer of View!
2) How to get the WindowManager instance
①Get the WindowManager object :
WindowManager wManager = getApplicationContext().getSystemService(Context. WINDOW_ SERVICE);
② Obtain the WindowManager.LayoutParams object to prepare for subsequent operations
WindowManager.LayoutParams wmParams=new WindowManager.LayoutParams();
2. WindowManager usage example:
Example 1: Get the screen width and height
Before Android 4.2, we can use the following method to get the screen width and height:
public static int[] getScreenHW(Context context) { WindowManager manager = (WindowManager)context .getSystemService(Context.WINDOW_SERVICE); Display display = manager.getDefaultDisplay(); int width = display.getWidth(); int height = display.getHeight(); int[] HW = new int[] { width, height }; return HW; }
The above method is outdated after Android 4.2, we can use another method to get the screen width and height:
public static int[] getScreenHW2(Context context) { WindowManager manager = (WindowManager) context. getSystemService(Context.WINDOW_SERVICE); DisplayMetrics dm = new DisplayMetrics(); manager.getDefaultDisplay().getMetrics(dm); int width = dm.widthPixels; int height = dm.heightPixels; int[] HW = new int[] { width, height }; return HW; }
Then we can write two more methods to get the width and height. Here we take the second method to get the screen width and height as an example:
public static int getScreenW(Context context) { return getScreenHW2(context)[0]; } public static int getScreenH(Context context) { return getScreenHW2(context)[1]; }
Of course, if you don't write another tool class, you can get it directly, for example:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); WindowManager wManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE); DisplayMetrics dm = new DisplayMetrics(); wManager.getDefaultDisplay().getMetrics(dm); Toast.makeText(MainActivity.this, "当前手机的屏幕宽高:" + dm.widthPixels + "*" + dm.heightPixels, Toast.LENGTH_SHORT).show(); } }
Running result :
Example 2: Setting the window to display in full screen
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); getSupportActionBar().hide();
Running result :
Example 3: Keep the screen always on
public void setKeepScreenOn(Activity activity,boolean keepScreenOn) { if(keepScreenOn) { activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); }else{ activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } }
Example 4: Implementation of a simple floating frame
Running effect diagram :
Implementation code :
First of all, we need a background Service to wait for our operations in the background, such as completing the drawing and removal of the floating frame, so we define a Service: MyService.java : We need a method to create a floating frame View:
private void createWindowView() { btnView = new Button(getApplicationContext()); btnView.setBackgroundResource(R.mipmap.ic_launcher); windowManager = (WindowManager) getApplicationContext() .getSystemService(Context.WINDOW_SERVICE); params = new WindowManager.Lay outParams( ); // Set Window Type params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; // Set the floating frame to be untouchable params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; accept any event, and does not affect subsequent event responses params.format = PixelFormat.RGBA_8888; // Set the width and height of the floating box params.width = 200; params.height = 200; params.gravity = Gravity.LEFT; params.x = 200; params.y = 000; // Set the touch monitor of the floating frame btnView.setOnTouchListener(new View.OnTouchListener() { // save the variable int lastX of the last position of the floating frame , lastY; int paramX, paramY; @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: lastX = (int) event.getRawX(); lastY = (int ) event.getRawY(); paramX = params.x; paramY = params.y; break; case MotionEvent.ACTION_MOVE: int dx = (int) event.getRawX() - lastX; int dy = (int) event.getRawY() - lastY; params.x = paramX + dx; params.y = paramY + dy; // 更新悬浮窗位置 windowManager.updateViewLayout(btnView, params); break; } return true; } }); windowManager.addView(btnView, params); isAdded = true; }
Then we only need to call the above-mentioned createWindowView( ) method in the OnCreate( ) method to start loading the floating frame, but we found one thing: this thing seems to be impossible to turn off, shit, well, next we will analyze Down demand!
When we are in the normal interface of the mobile phone, that is, the desktop, this thing is displayed, and when we start other apps, the floating box should disappear, and when we launch the app and return to the desktop, the floating box will reappear!
Then we first need to determine whether the App is on the desktop, so we add the following code:
/** * Determine whether the current interface is a desktop */ public boolean isHome(){ if(mActivityManager == null) { mActivityManager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE); } List<RunningTaskInfo> rti = mActivityManager.getRunningTasks(1 ); return homeList.contains(rti.get(0).topActivity.getPackageName()); } /** * Get the application package name of the desktop application * @return return a string list containing all package names */ private List<String> getHomes() { List<String> names = new ArrayList<String>(); PackageManager packageManager = this.getPackageManager(); // properties 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; }
Ok, next we need to make a series of judgments every once in a while, such as: whether it is on the desktop, whether the floating frame has been loaded, otherwise load it; otherwise, if it is loaded, remove the floating frame! Here we use handler~, because the UI cannot be updated directly in the child thread, so, you know, so we write a handler to complete the above operations:
//定义一个更新界面的Handler private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch(msg.what) { case HANDLE_CHECK_ACTIVITY: if(isHome()) { if(!isAdded) { windowManager.addView(btnView, params); isAdded = true; new Thread(new Runnable() { public void run() { for(int i=0;i<10;i++){ try { Thread.sleep(1000); } catch (InterruptedException e) {e.printStackTrace();} Message m = new Message(); m.what=2; mHandler.sendMessage(m); } } }).start();} } else { if(isAdded) { windowManager.removeView(btnView); isAdded = false; } } mHandler.sendEmptyMessageDelayed(HANDLE_CHECK_ACTIVITY, 0); break; } } };
The last thing to do is to rewrite the onStartCommand() method of the Service, which is to make a judgment, take out the data in the Intent, and judge whether it is necessary to add the floating frame or remove the floating frame!
@Override public int onStartCommand(Intent intent, int flags, int startId) { int operation = intent.getIntExtra(OPERATION, OPERATION_SHOW); switch(operation) { case OPERATION_SHOW: mHandler.removeMessages(HANDLE_CHECK_ACTIVITY); mHandler.sendEmptyMessage(HANDLE_CHECK_ACTIVITY); break; case OPERATION_HIDE: mHandler.removeMessages(HANDLE_CHECK_ACTIVITY); break; } return super.onStartCommand(intent, flags, startId); }
Ok, so far, the main work is done, and then there are some bits and pieces. Use an Activity to start the Service: MainActivity.java :
public class MainActivity extends AppCompatActivity implements View.OnClickListener { private Button btn_on; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); bindViews(); } private void bindViews() { btn_on = (Button) findViewById(R.id.btn_on); btn_on.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn_on: Intent mIntent = new Intent(MainActivity.this, MainService.class); mIntent.putExtra(MainService.OPERATION, MainService.OPERATION_SHOW); startService(mIntent); Toast.makeText(MainActivity.this, "The suspension box is open~", Toast.LENGTH_SHORT).show(); break; } } }
Then AndroidManifest.xml adds permissions and registers for MainService:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <uses-permission android:name="android.permission.GET_TASKS" /> <service android:name=".MainService"/>
Well, the logic is relatively easy to understand~ Let’s see for yourself~
3. Literature extension:
From the fourth example, you may have noticed: WindowManager.LayoutParams, this is a mark, such as full screen ~ time relationship will not be listed one by one, you can go to the official website or the following link to view:
Official documentation: WindowManager.LayoutParams
Android system service-WindowManager
In addition, if you are interested in the above floating frame and want to study it more deeply, you can see Uncle Guo (Guo Lin)'s blog: