WindowManager (service de gestion de fenêtres)
Introduction à cette section :
Cette section vous présente l'un des services système fournis par Android - WindowManager (service de gestion de fenêtres). C'est la couche inférieure qui affiche View. La couche inférieure de Toast, Activity et Dialog utilise toutes ce WindowManager. Il est mondial ! Le cœur de cette classe n'est rien d'autre que d'appeler les méthodes addView, RemoveView et updateViewLayout. Pour afficher la vue et définir les propriétés associées via l'API WindowManager.LayoutParams !
Dans cette section, discutons de quelques exemples d'application de ce WindowManager en développement réel~
Documentation officielle de l'API : WindowManager
1. Quelques concepts de WindowManager :
1) Introduction à WindowManager
Une API fournie par Android pour interagir avec le gestionnaire de fenêtres ! Nous savons tous que l'interface de l'application est Il est composé d'activités une par une, et Activity est composé de vues. Lorsque l'on souhaite afficher une interface, La première chose qui me vient à l’esprit est : l’activité, n’est-ce pas ? Ou Dialogue et Toast.
Mais dans certains cas, les trois premiers peuvent ne pas répondre à nos besoins. Par exemple, nous avons juste un simple affichage. Utiliser Activity semble un peu redondant, et Dialog nécessite un objet Context, et Toast ne peut pas être cliqué... Pour la situation ci-dessus, nous pouvons utiliser WindowManager pour ajouter View à l'écran. Ou supprimez la vue de l'écran ! C'est une interface qui gère le mécanisme de la fenêtre Android, affichant la couche inférieure de View!
2) Comment obtenir l'instance WindowManager
①Obtenir l'objet WindowManager:
WindowManager wManager = getApplicationContext().getSystemService(Context. WINDOW_ SERVICE);
②Obtenir l'objet WindowManager.LayoutParams, préparez-vous aux opérations suivantes
WindowManager.LayoutParams wmParams=new WindowManager.LayoutParams();
2 Exemple d'utilisation de WindowManager :
Exemple 1 : Obtenez la largeur et la hauteur de l'écran.
Avant Android 4.2, nous pouvons utiliser la méthode suivante pour obtenir la largeur et la hauteur de l'écran :
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; }
La méthode ci-dessus est obsolète après Android 4.2. Nous pouvons utiliser une autre méthode pour obtenir la largeur de l'écran. et la hauteur :
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; }
Ensuite, nous pouvons écrire deux autres méthodes pour obtenir la largeur et la hauteur. Voici la deuxième méthode pour obtenir la largeur et la hauteur de l'écran à titre d'exemple :
public static int getScreenW(Context context) { return getScreenHW2(context)[0]; } public static int getScreenH(Context context) { return getScreenHW2(context)[1]; }
Bien sûr, si vous n'écrivez pas une autre classe d'outils, vous pouvez l'obtenir directement, par exemple :
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(); } }
Exécuter le résultat :
Instance 2 : Définir la fenêtre pour qu'elle s'affiche en plein écran
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); getSupportActionBar().hide();
Résultats en cours d'exécution :
Exemple 3 : Garder l'écran toujours allumé
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); } }
Exemple 4 : Mise en place d'une simple boîte flottante
Exécution des rendus :
Code d'implémentation :
Nous avons d'abord besoin d'un service d'arrière-plan pour attendre nos opérations en arrière-plan, comme terminer le dessin et supprimer la boîte flottante, etc. On définit donc un Service : MyService.java : Nous avons besoin d'une méthode pour créer une boîte flottante 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; }
Ensuite, il nous suffit d'appeler la méthode createWindowView() ci-dessus dans la méthode OnCreate() pour commencer à charger la boîte flottante. Mais nous avons découvert quelque chose : cette chose ne semble pas être éteinte. Bon sang, d'accord, nous devons ensuite analyser la demande !
Quand elle est dans l'interface normale du téléphone portable, c'est-à-dire, sur le bureau, cette chose s'affiche, et lorsque nous lançons d'autres applications, cette boîte flottante devrait Disparaître, lorsque nous lançons l'application et revenons au bureau, la boîte flottante réapparaîtra
Ensuite, nous devons d'abord déterminer si l'application est sur le bureau, nous ajoutons donc le code suivant :
/** * 判断当前界面是否是桌面 */ 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; }
D'accord, nous devons ensuite faire une série de jugements de temps en temps, par exemple : si elle est sur le bureau, si la boîte flottante a été chargée, Sinon, chargez-le ; sinon, s'il est chargé, supprimez la boîte flottante ! Ici, nous utilisons handler~, car cela ne peut pas être fait directement dans le thread enfant ! Mettez à jour l'interface utilisateur, donc, vous savez, nous écrivons donc un gestionnaire pour effectuer les opérations ci-dessus :
//定义一个更新界面的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; } } };
La dernière chose à faire est de réécrire la méthode onStartCommand() de Service, qui consiste à porter des jugements, prendre sortir le contenu dans l'intention Données, déterminez si la boîte flottante doit être ajoutée ou supprimée !
@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); }
D'accord, maintenant, le travail principal est terminé, et puis il y a quelques choses fragmentaires, en utilisant une activité Pour démarrer ce 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; } } }
Ajoutez ensuite des autorisations à AndroidManifest.xml et inscrivez-vous à MainService :
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <uses-permission android:name="android.permission.GET_TASKS" /> <service android:name=".MainService"/>
D'accord, la logique est relativement simple. comprenez~ Jetons-y un coup d'œil vous-même~
3. Extension du document :
À partir du quatrième exemple, vous avez peut-être remarqué : WindowManager.LayoutParams East, c'est une marque, Par exemple, la relation entre le plein écran et l'heure n'est pas répertoriée une par une. Vous pouvez la vérifier sur le site officiel ou sur le lien suivant :
Document officiel : WindowManager.LayoutParams<🎜. >
Service système Android-WindowManager
De plus, si vous êtes intéressé par la boîte flottante mentionnée ci-dessus et souhaitez l'étudier plus en profondeur, vous pouvez visiter Oncle Guo (Guo Le blog de Lin) :L'effet de fenêtre flottante du bureau Android est réalisé, imitant l'effet de fenêtre flottante 360 Mobile Guard
La fenêtre flottante du bureau Android est avancé, l'effet de petite fusée du majordome mobile QQ est réalisé
4. Téléchargez l'exemple de code dans cette section :
Résumé de cette section :
Dans cette section, nous avons étudié le WindowManager dans le service système Android. Les trois premiers exemples peuvent. Il sera davantage utilisé dans le développement réel. Il est recommandé d'écrire le premier exemple en tant que classe d'outils. Après tout, la largeur et la hauteur de l'écran sont différentes. Il y en a beaucoup ~ Quant au cadre flottant, vous pouvez le comprendre si vous le pouvez. Si vous ne le faites pas, ce n'est pas grave ~ Dans le développement réel, on vous demande rarement d'en créer un. Cadre flottant... Bon, d'accord, c'est tout pour cette section, merci~