Android's WindowManager (window management service)

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:

Android desktop floating window effect is realized, imitating the floating window effect of 360 mobile phone guards

The Android desktop floating window is advanced, and the QQ mobile phone housekeeper small rocket effect is realized

Guess you like

Origin blog.csdn.net/leyang0910/article/details/132438209