一、 闲扯
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) { ... ... }那么我给LayoutInflater类设置一个动态代理,然后监视到要运行inflate方法的时候把xml里面的数据更改一下就能够了。
可是LayoutInflater是直接继承自Object,那么基于接口的动态代理肯定是没戏了,那么仅仅有通过基于子类的动态代理,于是把Java Web框架中经常使用的CGLib搞了过来:
final LayoutInflater inflater = LayoutInflater.from(this); Enhancer.create(LayoutInflater.class, new MethodInterceptor() { @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy arg3) throws Throwable { Log.i("MainActivity", "LayoutInflaterde运行的方法名称:" + method.getName()); return method.invoke(inflater, args); } });
二、Hello World
1. 创建一个类直接或间接继承Activity
public class MainActivity extends Activity { }
2. 书写布局文件
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" /> </RelativeLayout>
3. 在Acticity的onCreate()方法中设置布局
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); }
4. 在Manifest清单文件里注冊该Activity
<activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>执行效果例如以下:
三、setContentView 分析
public void setContentView(@LayoutRes int layoutResID) { getWindow().setContentView(layoutResID); initWindowDecorActionBar(); }2. 这里是调用的getWindow()返回对象的setContentView()方法,那么getWindow()返回的是什么呢?
public Window getWindow() { return mWindow; }
final void attach(Context context, ActivityThread aThread, Instrumentation instr, IBinder token, int ident, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, NonConfigurationInstances lastNonConfigurationInstances, Configuration config, String referrer, IVoiceInteractor voiceInteractor) { ... ... mWindow = new PhoneWindow(this); ... ... }4. PhoneWindow 源代码中 setContentView
@Override public void setContentView(int layoutResID) { if (mContentParent == null) { installDecor(); } else { mContentParent.removeAllViews(); } mLayoutInflater.inflate(layoutResID, mContentParent); final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { cb.onContentChanged(); } }
四、installDecor 分析
// This is the top-level view of the window, containing the window decor. private DecorView mDecor; // This is the view in which the window contents are placed. It is either // mDecor itself, or a child of mDecor where the contents go. private ViewGroup mContentParent;通过凝视能够看出, mDecor即Activity的根View,mContentView就是我们放置Activity布局的父View,而且它是mDecor或者mDecor的子View。
2. installDecor() 分析
private void installDecor() { if (mDecor == null) { // 初始化 mDecor mDecor = generateDecor(); mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); mDecor.setIsRootNamespace(true); } if (mContentParent == null) { // 初始化 mContentView mContentParent = generateLayout(mDecor); // Set up decor part of UI to ignore fitsSystemWindows if appropriate. mDecor.makeOptionalFitsSystemWindows(); mTitleView = (TextView)findViewById(com.android.internal.R.id.title); if (mTitleView != null) { if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) { // 设置 mTitleView显示内容 ... ... } else { // 设置 mTitleView显示内容 ... ... } } else { mActionBar = (ActionBarView) findViewById(com.android.internal.R.id.action_bar); if (mActionBar != null) { // 设置 ActionBar ... ... } } } }
(1)、通过generateDecor()初始化mDecor;(2)
、通过generateLayout初始化mContentView。protected DecorView generateDecor() { return new DecorView(getContext(), -1); }DecorView是PhoneWindow的一个继承自FrameLayout的内部类,这里创建了一个DecorView并赋值给了mDecor。
4. generateLayout 分析
protected ViewGroup generateLayout(DecorView decor) { TypedArray a = getWindowStyle(); // 设置 当前Activity配置的主题theme // 窗体是否是浮动的 mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, false); int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR) & (~getForcedWindowFlags()); if (mIsFloating) { setLayout(WRAP_CONTENT, WRAP_CONTENT); setFlags(0, flagsToUpdate); } else { setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate); } // 窗体是否有标题栏 if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) { requestFeature(FEATURE_NO_TITLE); } else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) { // Don't allow an action bar if there is no title. requestFeature(FEATURE_ACTION_BAR); } // 窗体中ActionBar是否在布局空间内 if (a.getBoolean(R.styleable.Window_windowActionBarOverlay, false)) { requestFeature(FEATURE_ACTION_BAR_OVERLAY); } if (a.getBoolean(R.styleable.Window_windowActionModeOverlay, false)) { requestFeature(FEATURE_ACTION_MODE_OVERLAY); } if (a.getBoolean(R.styleable.Window_windowSwipeToDismiss, false)) { requestFeature(FEATURE_SWIPE_TO_DISMISS); } // 窗体是否全屏显示 if (a.getBoolean(R.styleable.Window_windowFullscreen, false)) { setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags())); } ... ... //窗体状态栏颜色配置 if (!mForcedStatusBarColor) { mStatusBarColor = a.getColor(R.styleable.Window_statusBarColor, 0xFF000000); } //窗体导航栏颜色配置 if (!mForcedNavigationBarColor) { mNavigationBarColor = a.getColor(R.styleable.Window_navigationBarColor, 0xFF000000); } if (mAlwaysReadCloseOnTouchAttr || getContext().getApplicationInfo().targetSdkVersion >= android.os.Build.VERSION_CODES.HONEYCOMB) { if (a.getBoolean( R.styleable.Window_windowCloseOnTouchOutside,false)) { setCloseOnTouchOutsideIfNotSet(true); } } WindowManager.LayoutParams params = getAttributes(); //输入法配置 if (!hasSoftInputMode()) { params.softInputMode = a.getInt( R.styleable.Window_windowSoftInputMode, params.softInputMode); } if (a.getBoolean(R.styleable.Window_backgroundDimEnabled, mIsFloating)) { /* All dialogs should have the window dimmed */ if ((getForcedWindowFlags()&WindowManager.LayoutParams.FLAG_DIM_BEHIND) == 0) { params.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND; } if (!haveDimAmount()) { params.dimAmount = a.getFloat( android.R.styleable.Window_backgroundDimAmount, 0.5f); } } //设置当前Activity的出现动画效果 if (params.windowAnimations == 0) { params.windowAnimations = a.getResourceId( R.styleable.Window_windowAnimationStyle, 0); } // The rest are only done if this window is not embedded; otherwise, // the values are inherited from our container. if (getContainer() == null) { if (mBackgroundDrawable == null) { if (mBackgroundResource == 0) { mBackgroundResource = a.getResourceId( R.styleable.Window_windowBackground, 0); } if (mFrameResource == 0) { mFrameResource = a.getResourceId(R.styleable.Window_windowFrame, 0); } mBackgroundFallbackResource = a.getResourceId( R.styleable.Window_windowBackgroundFallback, 0); if (false) { System.out.println("Background: " + Integer.toHexString(mBackgroundResource) + " Frame: " + Integer.toHexString(mFrameResource)); } } mElevation = a.getDimension(R.styleable.Window_windowElevation, 0); mClipToOutline = a.getBoolean(R.styleable.Window_windowClipToOutline, false); mTextColor = a.getColor(R.styleable.Window_textColor, Color.TRANSPARENT); } } // 为窗体加入 decor根布局 // Inflate the window decor. int layoutResource; int features = getLocalFeatures(); if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) { if (mIsFloating) { TypedValue res = new TypedValue(); getContext().getTheme().resolveAttribute( com.android.internal.R.attr.dialogTitleIconsDecorLayout, res, true); layoutResource = res.resourceId; } else { layoutResource = com.android.internal.R.layout.screen_title_icons; } // XXX Remove this once action bar supports these features. removeFeature(FEATURE_ACTION_BAR); // System.out.println("Title Icons!"); } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0 && (features & (1 << FEATURE_ACTION_BAR)) == 0) { // Special case for a window with only a progress bar (and title). // XXX Need to have a no-title version of embedded windows. layoutResource = com.android.internal.R.layout.screen_progress; // System.out.println("Progress!"); } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) { // Special case for a window with a custom title. // If the window is floating, we need a dialog layout if (mIsFloating) { TypedValue res = new TypedValue(); getContext().getTheme().resolveAttribute( com.android.internal.R.attr.dialogCustomTitleDecorLayout, res, true); layoutResource = res.resourceId; } else { layoutResource = com.android.internal.R.layout.screen_custom_title; } // XXX Remove this once action bar supports these features. removeFeature(FEATURE_ACTION_BAR); } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) { // If no other features and not embedded, only need a title. // If the window is floating, we need a dialog layout if (mIsFloating) { TypedValue res = new TypedValue(); getContext().getTheme().resolveAttribute( com.android.internal.R.attr.dialogTitleDecorLayout, res, true); layoutResource = res.resourceId; } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) { if ((features & (1 << FEATURE_ACTION_BAR_OVERLAY)) != 0) { layoutResource = com.android.internal.R.layout.screen_action_bar_overlay; } else { layoutResource = com.android.internal.R.layout.screen_action_bar; } } else { layoutResource = com.android.internal.R.layout.screen_title; } // System.out.println("Title!"); } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) { layoutResource = com.android.internal.R.layout.screen_simple_overlay_action_mode; } else { layoutResource = com.android.internal.R.layout.screen_simple; } mDecor.startChanging(); // 通过布局填充器LayoutInflater将layoutResource填充为布局。载入到decor上 View in = mLayoutInflater.inflate(layoutResource, null); decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); // 对contentParent进行赋值 ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); if (contentParent == null) { throw new RuntimeException("Window couldn't find content container view"); } if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) { ProgressBar progress = getCircularProgressBar(false); if (progress != null) { progress.setIndeterminate(true); } } // Remaining setup -- of background and title -- that only applies // to top-level windows. // 设置窗体的背景标题等 if (getContainer() == null) { Drawable drawable = mBackgroundDrawable; if (mBackgroundResource != 0) { drawable = getContext().getResources().getDrawable(mBackgroundResource); } mDecor.setWindowBackground(drawable); drawable = null; if (mFrameResource != 0) { drawable = getContext().getResources().getDrawable(mFrameResource); } mDecor.setWindowFrame(drawable); if (mTitleColor == 0) { mTitleColor = mTextColor; } if (mTitle != null) { setTitle(mTitle); } setTitleColor(mTitleColor); } mDecor.finishChanging(); return contentParent; }
依据代码中的凝视大家能够清楚看到,主要是读取Activity的theme/feature配置。设置窗体,然后依据窗体的属性来选择相应的窗体修饰并填充为View载入到mDecor中,并对 contentParent赋值。
依据配置选择layoutResource布局com.android.internal.R.layout.xxx,比方:com.android.internal.R.layout.screen_action_bar;
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:fitsSystemWindows="true"> <com.android.internal.widget.ActionBarContainer android:id="@+id/action_bar_container" android:layout_width="match_parent" android:layout_height="wrap_content" style="?android:attr/actionBarStyle"> <com.android.internal.widget.ActionBarView android:id="@+id/action_bar" android:layout_width="match_parent" android:layout_height="wrap_content" style="?android:attr/actionBarStyle" /> <com.android.internal.widget.ActionBarContextView android:id="@+id/action_context_bar" android:layout_width="match_parent" android:layout_height="wrap_content" android:visibility="gone" style="?android:attr/actionModeStyle" /> </com.android.internal.widget.ActionBarContainer> <FrameLayout android:id="@android:id/content" android:layout_width="match_parent" android:layout_height="0dip" android:layout_weight="1" android:foregroundGravity="fill_horizontal|top" android:foreground="?布局主要分为了ActionBar和FrameLayout的布局。是不是有点熟悉这个FrameLayout的id为content。对头,我们在上面分析的mContentParent就是这个FrameLayout。例如以下图所看到的:android:attr/windowContentOverlay" /> <LinearLayout android:id="@+id/lower_action_context_bar" android:layout_width="match_parent" android:layout_height="wrap_content" style="?
android:attr/actionBarStyle" android:visibility="gone" /> </LinearLayout>
发表评论