WindowManager(창 관리 서비스)
이 섹션 소개:
이 섹션에서는 Android에서 제공하는 시스템 서비스 중 하나인 WindowManager(창 관리 서비스)를 소개합니다. View를 표시하는 하위 레이어입니다. Toast, Activity, Dialog의 하위 레이어는 모두 이 WindowManager를 사용합니다. 그는 글로벌하다! 이 클래스의 핵심은 addView, RemoveView 및 updateViewLayout 메소드를 호출하는 것입니다. WindowManager.LayoutParams API를 통해 View를 표시하고 관련 속성을 설정하려면!
이 섹션에서는 실제 개발에서 이 WindowManager의 몇 가지 응용 예를 논의해 보겠습니다~
공식 API 문서: WindowManager
1. WindowManager의 일부 개념:
1) WindowManager 소개
API 창 관리자와 상호 작용하기 위해 Android에서 제공합니다! 우리 모두는 앱의 인터페이스가 다음과 같다는 것을 알고 있습니다. Activity로 하나씩 구성되어 있으며, Activity는 View로 구성되어 있습니다. 가장 먼저 떠오르는 것은 활동입니다. 그렇죠? 아니면 대화와 토스트.
그러나 어떤 경우에는 처음 세 개가 우리의 요구 사항을 충족하지 못할 수도 있습니다. 예를 들어 간단한 디스플레이만 있을 뿐입니다. Activity를 사용하는 것은 약간 중복되는 것 같고 Dialog에는 Context 개체가 필요하며 Toast를 클릭할 수 없습니다... 위의 상황에서는 WindowManager를 사용하여 화면에 View를 추가할 수 있습니다. 아니면 화면에서 보기를 제거하세요! View의 하단 레이어를 표시하는 Android 창 메커니즘을 관리하는 인터페이스입니다!
2) WindowManager 인스턴스를 가져오는 방법
①WindowManager 개체 가져오기:
WindowManager wManager = getApplicationContext().getSystemService(Context. WINDOW_ SERVICE);
②WindowManager.LayoutParams 개체를 가져와 후속 작업 준비
WindowManager.LayoutParams wmParams=new WindowManager.LayoutParams();
2.WindowManager 사용 예:
예제 1: 화면 너비와 높이 가져오기
Android 4.2 이전에는 다음 방법을 사용하여 화면 너비와 높이를 가져올 수 있었습니다.
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; }
위 방법은 Android 4.2 이후에는 더 이상 사용되지 않습니다. 화면 너비와 높이를 가져옵니다. :
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; }
그런 다음 너비와 높이를 가져오는 두 가지 방법을 더 작성할 수 있습니다. 예를 들어 화면 너비와 높이를 가져오는 두 번째 방법은 다음과 같습니다. 다른 도구 클래스를 작성하지 않고 직접 얻을 수 있습니다. 예:
public static int getScreenW(Context context) { return getScreenHW2(context)[0]; } public static int getScreenH(Context context) { return getScreenHW2(context)[1]; }실행 결과
:
예 2: 창을 전체 화면으로 표시하도록 설정
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();
}
}
:
예 3: 화면을 항상 켜두기
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
getSupportActionBar().hide();
예 4: 간단한 플로팅 박스 구현
렌더링 실행
:
구현 코드:
먼저 그리기 완료, 플로팅 박스 제거 등과 같은 백그라운드 작업을 기다리는 백그라운드 서비스가 필요합니다. 그래서 우리는 서비스를 정의합니다: MyService.java: 플로팅 상자 뷰를 생성하는 메서드가 필요합니다:
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); } }
그런 다음 OnCreate() 메서드에서 위의 createWindowView() 메서드만 호출하면 플로팅 상자 로드가 시작됩니다. 그런데 한 가지 발견했습니다. 이건 끌 수 없는 것 같군요. 젠장, 알았어. 다음에는 수요를 분석해 보자!
이건 휴대폰의 일반 인터페이스에 있을 때만 표시됩니다. 다른 앱을 시작할 때 이 부동 상자는 사라지세요. 앱을 실행하고 데스크톱으로 돌아가면 떠 있는 상자가 다시 나타납니다!
그런 다음 먼저 앱이 데스크톱에 있는지 확인해야 하므로 다음 코드를 추가합니다.
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; }
좋아요, 다음은 A입니다. 데스크탑에 있는지, 플로팅 박스가 로드되었는지, 그렇지 않으면 로드하고, 로드되면 플로팅 상자를 제거하십시오! 여기서는 핸들러~를 사용합니다. 왜냐하면 이는 하위 스레드에서 직접 실행될 수 없기 때문입니다. UI를 업데이트하므로 위 작업을 완료하기 위해 핸들러를 직접 작성합니다.
/** * 判断当前界面是否是桌面 */ 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; }
마지막으로 해야 할 일은 Service의 onStartCommand() 메서드를 다시 작성하는 것입니다. 의도 데이터, 플로팅 박스를 추가해야 할지 제거해야 할지 결정하세요!
//定义一个更新界面的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; } } };
자, 이제 주요 작업이 완료되었습니다. 그 다음에는 Activity를 사용하여 몇 가지 단편적인 작업이 있습니다. 이 서비스를 시작하려면: MainActivity.java:
@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); }
그런 다음 AndroidManifest.xml에 권한을 추가하고 MainService에 등록합니다.
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; } } }
좋아, 논리는 비교적 이해하기 쉽습니다~ 직접 살펴보겠습니다~
3 . 문학 확장:
네 번째 예에서 눈치챘을 것입니다: WindowManager.LayoutParams, 이것은 표시입니다. 예를 들어 전체 화면 ~ 시간 관계가 하나씩 나열되지 않습니다. 공식 홈페이지 또는 다음 링크에서 확인할 수 있습니다.
공식 문서: WindowManager.LayoutParams
또한, 위의 내용에 관심이 있으신 분은 플로팅 프레임에 관심이 있고 더 깊게 연구하고 싶으시면 Guo 삼촌(Guo Lin)의 블로그를 방문하세요:
4 이 섹션의 코드 샘플을 다운로드하세요:
이 섹션 요약:
이 섹션에서는 Android 시스템 서비스의 WindowManager를 연구했습니다. 처음 세 가지 예는 다음과 같습니다. 실제 개발에서는 더 많이 쓰이겠지만, 첫 번째 예제는 툴 클래스로 작성하는 것이 좋습니다. 꽤 많아요~ 플로팅 프레임은 알면 알겠지만, 모르면 괜찮아요~ 실제 개발에서는 만들어 달라는 경우가 거의 없습니다. 플로팅 프레임... 음, 알겠습니다. 이번 섹션은 여기까지입니다. 감사합니다~