關於做者java
郭孝星,程序員,吉他手,主要從事Android平臺基礎架構方面的工做,歡迎交流技術方面的問題,能夠去個人Github提issue或者發郵件至guoxiaoxingse@163.com與我交流。android
第一次閱覽本系列文章,請參見導讀,更多文章請參見文章目錄。git
文章目錄程序員
從這篇文章開始,咱們來分析和Window以及WindowManager相關的內容,github
Abstract base class for a top-level window look and behavior policy.架構
Window在Android是一個窗口的概念,平常開發中咱們和它接觸的很少,咱們更多接觸的是View,可是View都是經過Window來呈現的,Window是View的直接管理者。
而WindowManager承擔者管理Window的責任。app
Window在Android中有三種類型:ide
z-index是Android窗口的層級的概念,z-index越大的窗口越居於頂層,函數
z-index對應着WindowManager.LayoutParams裏的type參數,具體說來。工具
應用Window
子Window
系統Window
在WindowManager裏定義了一個LayoutParams內部類,它描述了窗口的參數信息,主要包括:
關於窗口屬性,它控制着窗口的行爲,舉幾個常見的:
關於窗口類型,它對應着窗口的層級,上面咱們也提到過了。
它的構造函數也主要是針對這幾個參數的。
public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
public LayoutParams() {
super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
type = TYPE_APPLICATION;
format = PixelFormat.OPAQUE;
}
public LayoutParams(int _type) {
super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
type = _type;
format = PixelFormat.OPAQUE;
}
public LayoutParams(int _type, int _flags) {
super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
type = _type;
flags = _flags;
format = PixelFormat.OPAQUE;
}
public LayoutParams(int _type, int _flags, int _format) {
super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
type = _type;
flags = _flags;
format = _format;
}
public LayoutParams(int w, int h, int _type, int _flags, int _format) {
super(w, h);
type = _type;
flags = _flags;
format = _format;
}
public LayoutParams(int w, int h, int xpos, int ypos, int _type, int _flags, int _format) {
super(w, h);
x = xpos;
y = ypos;
type = _type;
flags = _flags;
format = _format;
}
}複製代碼
關於窗口模式咱們就比較熟悉了,咱們會在AndroidManifest.xml裏Activity的標籤下設置android:windowSoftInputMode="adjustNothing",來控制輸入鍵盤顯示行爲。
可選的有6個參數,源碼裏也有6個值與之對應:
固然,咱們也能夠經過代碼設置鍵盤模式。
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);複製代碼
Window裏定義了一個Callback接口,Activity實現了Window.Callback接口,將Activity關聯給Window,Window就能夠將一些事件交由Activity處理。
public interface Callback {
//鍵盤事件分發
public boolean dispatchKeyEvent(KeyEvent event);
//觸摸事件分發
public boolean dispatchTouchEvent(MotionEvent event);
//軌跡球事件分發
public boolean dispatchTrackballEvent(MotionEvent event);
//可見性事件分發
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event);
//建立Panel View
public View onCreatePanelView(int featureId);
//建立menu
public boolean onCreatePanelMenu(int featureId, Menu menu);
//畫板準備好時回調
public boolean onPreparePanel(int featureId, View view, Menu menu);
//menu打開時回調
public boolean onMenuOpened(int featureId, Menu menu);
//menu item被選擇時回調
public boolean onMenuItemSelected(int featureId, MenuItem item);
//Window Attributes發生變化時回調
public void onWindowAttributesChanged(WindowManager.LayoutParams attrs);
//Content View發生變化時回調
public void onContentChanged();
//窗口焦點發生變化時回調
public void onWindowFocusChanged(boolean hasFocus);
//Window被添加到WIndowManager時回調
public void onAttachedToWindow();
//Window被從WIndowManager中移除時回調
public void onDetachedFromWindow();
*/
//畫板關閉時回調
public void onPanelClosed(int featureId, Menu menu);
//用戶開始執行搜索操做時回調
public boolean onSearchRequested();
}複製代碼
Window是一個抽象類,它的惟一實現類是PhoneWindow,PhoneWindow裏包含了如下內容:
看到這些,你們有沒有以爲很熟悉,這就是咱們平常開發中常常見到的東西,它在PhoneWindow裏被建立。另外,咱們在Activity裏常常調用的方法,它的實際實現也是
在PhoneWindow裏,咱們分別來看一看。
這是一個咱們很是熟悉的方法,只不過咱們一般是在Activity裏進行調用,可是它的實際實現是在PhoneWindow裏。
public class PhoneWindow extends Window implements MenuBuilder.Callback {
@Override
public void setContentView(int layoutResID) {
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
if (mContentParent == null) {
//1. 若是沒有DecorView則建立它,並將建立好的DecorView賦值給mContentParent
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
//2. 將Activity傳入的佈局文件生成View並添加到mContentParent中
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
//3. 回調Window.Callback裏的onContentChanged()方法,這個Callback也被Activity
//所持有,所以它實際回調的是Activity裏的onContentChanged()方法,通知Activity
//視圖已經發生改變。
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
}複製代碼
這個方法主要作了兩件事情:
建立DecorView是經過installDecor()方法完成的,它的邏輯也很是簡單,就是建立了一個ViewGroup而後返回給了mDecor和mContentParent。
public class PhoneWindow extends Window implements MenuBuilder.Callback {
public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
//生成DecorView
mDecor = generateDecor(-1);
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
...
} else {
...
}
...
}
}
protected ViewGroup generateLayout(DecorView decor) {
//讀取並設置主題顏色、狀態欄顏色等信息
...
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
//設置窗口參數等信息
...
return contentParent;
}
}複製代碼
經過以上這些流程,DecorView已經被建立並初始化完畢,Activity裏的佈局文件也被成功的添加到PhoneWindow的mContentParent(實際上就是DecorView,它是Activity的頂層View)
中,可是這個時候DecorView尚未真正的被WindowManager添加到Window中,它還沒法接受用戶的輸入信息和焦點事件,這個時候就至關於走到了Activity的onCreate()流程,界面還
未展現給用戶。
直到走到Activity的onResume()方法,它會調用Activity的makeVisiable()方法,DecorView才真正的被用戶所看到。
public class Activity extends ContextThemeWrapper implements LayoutInflater.Factory2, Window.Callback, KeyEvent.Callback, OnCreateContextMenuListener, ComponentCallbacks2, Window.OnWindowDismissedCallback, WindowControllerCallback {
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
}複製代碼
一般以上的分析,咱們理解了setContentView的工做原理,另外還有addContentView、clearContentView,正如它們的名字那樣,setContentView是替換View,addContentView是添加View。實現原理相同。
好了,以上即是本篇文章的所有內容,下一篇文章咱們來分析WindowManager的內容,分析Window的添加、移除和更新的流程。
文章末尾給你們提供一個WindowUtils工具類。
import android.animation.ValueAnimator;
import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.view.Surface;
import android.view.Window;
import android.view.WindowManager;
public final class WindowUtils {
/** * Don't let anyone instantiate this class. */
private WindowUtils() {
throw new Error("Do not need instantiate!");
}
/** * 獲取當前窗口的旋轉角度 * * @param activity activity * @return int */
public static int getDisplayRotation(Activity activity) {
switch (activity.getWindowManager().getDefaultDisplay().getRotation()) {
case Surface.ROTATION_0:
return 0;
case Surface.ROTATION_90:
return 90;
case Surface.ROTATION_180:
return 180;
case Surface.ROTATION_270:
return 270;
default:
return 0;
}
}
/** * 當前是不是橫屏 * * @param context context * @return boolean */
public static final boolean isLandscape(Context context) {
return context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
}
/** * 當前是不是豎屏 * * @param context context * @return boolean */
public static final boolean isPortrait(Context context) {
return context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
}
/** * 調整窗口的透明度 1.0f,0.5f 變暗 * @param from from>=0&&from<=1.0f * @param to to>=0&&to<=1.0f * @param context 當前的activity */
public static void dimBackground(final float from, final float to, Activity context) {
final Window window = context.getWindow();
ValueAnimator valueAnimator = ValueAnimator.ofFloat(from, to);
valueAnimator.setDuration(500);
valueAnimator.addUpdateListener(
new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
WindowManager.LayoutParams params
= window.getAttributes();
params.alpha = (Float) animation.getAnimatedValue();
window.setAttributes(params);
}
});
valueAnimator.start();
}
}複製代碼