WindowManager (window management service)


Introduction to this section:

This section brings to you one of the system services provided by Android - WindowManager (window management service), It is the bottom layer that displays View. The bottom layer of Toast, Activity, and Dialog all use this WindowManager. He is global! The core of this class is nothing more than calling the methods addView, removeView, and updateViewLayout. 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 the App is It is composed of Activities one by one, and Activity is composed of Views. When we want to display an interface, The first thing that comes to mind is: Activity, right? Or Dialog and Toast.

But in some cases, the first three may not meet our needs. For example, we just have a simple display Using Activity seems a bit redundant, and Dialog requires a Context object, and Toast cannot be clicked... For the above situation, we can use WindowManager to add View to the screen. Or remove the View from the screen! It is an interface that manages 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);

Get the WindowManager.LayoutParams Object, 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 obtain 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 obsolete after Android 4.2. We can use another method to obtain 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 obtain the width and height. Here is the second method to obtain 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();
    }
}

Run result:

1.png

##Example 2: Set the window to display in full screen

getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
        getSupportActionBar().hide();

Running result:

2.png

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 box

Running renderings

##

Implementation code:

First we need a background Service to wait for our operations in the background, such as completing the drawing and removal of the floating box, etc. So we define a Service: MyService.java: We need a method to create a floating box View:

private void createWindowView() {
    btnView = new Button(getApplicationContext());
    btnView.setBackgroundResource(R.mipmap.ic_launcher);
    windowManager = (WindowManager) getApplicationContext()
            .getSystemService(Context.WINDOW_SERVICE);
    params = new WindowManager.LayoutParams();

    // 设置Window Type
    params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
    // 设置悬浮框不可触摸
    params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
            | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
    // 悬浮窗不可触摸,不接受任何事件,同时不影响后面的事件响应
    params.format = PixelFormat.RGBA_8888;
    // 设置悬浮框的宽高
    params.width = 200;
    params.height = 200;
    params.gravity = Gravity.LEFT;
    params.x = 200;
    params.y = 000;
    // 设置悬浮框的Touch监听
    btnView.setOnTouchListener(new View.OnTouchListener() {
        //保存悬浮框最后位置的变量
        int lastX, 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 createWindowView() method in the OnCreate() method to start loading the floating box. But we discovered something: this thing can’t seem to be turned off. Damn it, okay, next we have to analyze the demand!

When it is in the normal interface of the mobile phone, that is, the desktop, this thing is displayed, and when we launch other Apps, this floating box should Disappear, 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:

/**  
 * 判断当前界面是否是桌面  
 */    
public boolean isHome(){    
    if(mActivityManager == null) {  
        mActivityManager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);    
    }  
    List rti = mActivityManager.getRunningTasks(1);    
    return homeList.contains(rti.get(0).topActivity.getPackageName());    
}  
  
/**  
 * 获得属于桌面的应用的应用包名称  
 * @return 返回包含所有包名的字符串列表  
 */  
private List getHomes() {  
    List names = new ArrayList();    
    PackageManager packageManager = this.getPackageManager();    
    // 属性    
    Intent intent = new Intent(Intent.ACTION_MAIN);    
    intent.addCategory(Intent.CATEGORY_HOME);    
    List resolveInfo = packageManager.queryIntentActivities(intent,    
            PackageManager.MATCH_DEFAULT_ONLY);    
    for(ResolveInfo ri : resolveInfo) {    
        names.add(ri.activityInfo.packageName);    
    }  
    return names;    
}

Okay, 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 box has been loaded, Otherwise, load; otherwise, if loaded, remove the floating box! Here we use handler~, because it cannot be directly executed in the child thread Update the UI, 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 Service, which is to make judgments , take out the content in Intent Data, determine whether the floating box needs to be added or removed!

@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);  
}

Okay, now, the main work is completed, and then there are some fragmentary things, using an Activity To start this 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, "悬浮框已开启~", Toast.LENGTH_SHORT).show();
                break;
        }
    }
}

Then add permissions to AndroidManifest.xml and register for MainService:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.GET_TASKS" />
<service android:name=".MainService"/>

Okay, the logic is relatively easy. I understand~ Let’s take a look at it yourself~


3. Document expansion:

From the fourth example, you may have noticed: WindowManager.LayoutParams East, this is a mark, For example, the relationship between full screen and time is not listed one by one. You can check it on the official website or the following link:

Official document: WindowManager.LayoutParams

Android System Service-WindowManager

In addition, if you are interested in the above-mentioned floating box and want to study more in-depth, you can visit Uncle Guo (Guo Lin)’s blog:

Android desktop floating window effect is realized, imitating the 360 ​​mobile guard floating window effect

Android desktop floating window is advanced, QQ mobile butler small rocket effect is realized


4. Download the code sample in this section:

WindowManagerDemo2.zip


Summary of this section:

In this section we have studied the WindowManager in the Android system service. The first three examples may It will be used more in actual development. It is recommended to write the first example as a tool class. After all, the screen width and height are There are quite a lot~ As for the floating frame, if you can understand it, you can understand it. If you don’t understand it, it’s okay~ In actual development, you are rarely asked to make one. Floating box... Well, okay, that's it for this section, thank you~

4.gif