WindowManager (perkhidmatan pengurusan tetingkap)
Pengenalan kepada bahagian ini:
Bahagian ini membawakan anda salah satu perkhidmatan sistem yang disediakan oleh Android - WindowManager (perkhidmatan pengurusan tetingkap). Ia adalah lapisan bawah yang memaparkan View Lapisan bawah Toast, Activity dan Dialog semuanya menggunakan WindowManager ini. Dia global! Teras kelas ini tidak lebih daripada memanggil kaedah addView, removeView dan updateViewLayout Untuk memaparkan Lihat dan menetapkan sifat berkaitan melalui WindowManager.LayoutParams API!
Dalam bahagian ini, mari kita bincangkan beberapa contoh aplikasi WindowManager ini dalam pembangunan sebenar~
Dokumentasi API rasmi: WindowManager
1. Beberapa konsep WindowManager:
1) Pengenalan kepada WindowManager
API yang disediakan oleh Android untuk berinteraksi dengan pengurus tetingkap! Kita semua tahu bahawa antara muka App adalah Ia terdiri daripada Aktiviti satu demi satu, dan Aktiviti terdiri daripada Paparan Apabila kita ingin memaparkan antara muka, Perkara pertama yang terlintas di fikiran ialah: Aktiviti, bukan? Atau Dialog dan Roti Bakar.
Tetapi dalam beberapa kes, tiga yang pertama mungkin tidak memenuhi keperluan kita Sebagai contoh, kita hanya mempunyai paparan yang mudah Menggunakan Aktiviti nampaknya agak berlebihan, dan Dialog memerlukan objek Konteks, dan Toast tidak boleh diklik... Untuk situasi di atas, kita boleh menggunakan WindowManager untuk menambah View pada skrin. Atau alih keluar Paparan daripada skrin! Ia ialah antara muka yang menguruskan mekanisme tetingkap Android, memaparkan lapisan bawah View!
2) Bagaimana untuk mendapatkan contoh WindowManager
①Dapatkan objek WindowManager:
WindowManager wManager = getApplicationContext().getSystemService(Context. WINDOW_ SERVICE);
②Dapatkan Objek WindowManager.LayoutParams, sediakan untuk operasi seterusnya
WindowManager.LayoutParams wmParams=new WindowManager.LayoutParams();
contoh penggunaan WindowManager:
Contoh 1: Dapatkan lebar dan ketinggian skrin
Sebelum Android 4.2, kita boleh menggunakan kaedah berikut untuk mendapatkan lebar dan ketinggian skrin:
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; }
Kaedah di atas sudah lapuk selepas Android 4.2. Kita boleh menggunakan kaedah lain untuk mendapatkan lebar skrin dan ketinggian:
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; }
Kemudian kita boleh menulis dua lagi kaedah untuk mendapatkan lebar dan ketinggian Berikut ialah kaedah kedua untuk mendapatkan lebar dan ketinggian skrin sebagai contoh:
public static int getScreenW(Context context) { return getScreenHW2(context)[0]; } public static int getScreenH(Context context) { return getScreenHW2(context)[1]; }
Sudah tentu,. jika anda tidak menulis kelas alat lain, Anda boleh mendapatkannya secara langsung, contohnya:
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(); } }
Hasil jalankan:
Contoh 2: Tetapkan tetingkap untuk dipaparkan dalam skrin penuh
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); getSupportActionBar().hide();
Hasil berjalan:
Contoh 3: Pastikan skrin sentiasa hidup
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); } }
Contoh 4: Pelaksanaan kotak terapung ringkas
Menjalankan pemaparan :
Kod pelaksanaan:
Mula-mula kami memerlukan Perkhidmatan latar belakang untuk menunggu operasi kami di latar belakang, seperti melengkapkan lukisan dan mengalih keluar kotak terapung, dsb. Jadi kami mentakrifkan Perkhidmatan: MyService.java: Kami memerlukan kaedah untuk mencipta kotak terapung Lihat:
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; }
Kemudian kami hanya perlu memanggil kaedah createWindowView() di atas dalam kaedah OnCreate() untuk mula memuatkan kotak terapung. Tetapi kami menemui sesuatu: Perkara ini nampaknya tidak boleh dimatikan, okey, seterusnya kita perlu menganalisis permintaan!
Apabila ia berada dalam antara muka biasa telefon bimbit, iaitu, desktop, perkara ini dipaparkan, dan apabila kami melancarkan Aplikasi lain, kotak terapung ini sepatutnya Hilang, apabila kami melancarkan aplikasi dan kembali ke desktop, kotak terapung akan muncul semula
Kemudian kita perlu menentukan sama ada Apl berada pada desktop, jadi kami menambah kod berikut:
/** * 判断当前界面是否是桌面 */ 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; }!
Baiklah, seterusnya kita perlu membuat beberapa siri pertimbangan sekali-sekala, seperti: sama ada pada desktop, sama ada kotak terapung telah dimuatkan, Jika tidak, muatkannya; sebaliknya, jika ia dimuatkan, keluarkan kotak terapung Di sini kami menggunakan pengendali, kerana ia tidak boleh dilakukan secara langsung dalam benang kanak-kanak Kemas kini UI, jadi, anda tahu, jadi kami menulis pengendali untuk menyelesaikan operasi di atas:
//定义一个更新界面的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; } } };
Perkara terakhir yang perlu dilakukan ialah menulis semula kaedah onStartCommand() Perkhidmatan, iaitu membuat pertimbangan , mengambil keluar kandungan dalam Niat Data, tentukan sama ada kotak terapung perlu ditambah atau dialih keluar!
@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); }
Baiklah, sekarang, kerja utama selesai, dan kemudian terdapat beberapa perkara yang berpecah, menggunakan Aktiviti Untuk memulakan Perkhidmatan ini: 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; } } }
Kemudian tambahkan kebenaran pada AndroidManifest.xml dan daftar untuk MainService:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <uses-permission android:name="android.permission.GET_TASKS" /> <service android:name=".MainService"/>
Baiklah, logiknya agak mudah faham~ Mari kita lihat sendiri~
3 Peluasan dokumen:
Daripada contoh keempat, anda mungkin perasan: WindowManager.LayoutParams East, ini ialah. satu tanda, Contohnya, hubungan antara skrin penuh dan masa tidak disenaraikan satu demi satu Anda boleh menyemaknya di tapak web rasmi atau pautan berikut:
Dokumen rasmi: WindowManager.LayoutParams<🎜. >
Android System Service-WindowManager
Selain itu, jika anda berminat dengan kotak terapung yang disebutkan di atas dan ingin mengkajinya dengan lebih mendalam, anda boleh melawat Uncle Guo (Guo Blog Lin):Kesan tetingkap terapung desktop Android direalisasikan, meniru kesan tetingkap terapung pengawal mudah alih 360
Tetingkap terapung desktop Android ialah lanjutan, kesan roket kecil butler mudah alih QQ direalisasikan
4 Muat turun sampel kod dalam bahagian ini:
Ringkasan bahagian ini:
Dalam bahagian ini kami telah mengkaji WindowManager dalam perkhidmatan sistem Android Tiga contoh pertama mungkin Ia akan digunakan lebih dalam pembangunan sebenar Adalah disyorkan untuk menulis contoh pertama sebagai kelas alat Lagipun, lebar dan ketinggian skrin Terdapat agak banyak~ Bagi bingkai terapung, anda boleh memahaminya jika anda tidak, tidak mengapa~ Dalam perkembangan sebenar, anda jarang diminta untuk membuatnya. Bingkai terapung... Baiklah, itu sahaja untuk bahagian ini, terima kasih~