<html>
一、 闲扯
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>
五、总结
-
我的GitHub
-
阅读排行
- Android 拍照、选择图片并裁剪(24618)
- Android UltimateRecyclerView优雅地使用RecyclerView(17051)
- Android PullToRefresh 分析之四、扩展RecyclerView(10935)
- Android RecyclerView加入头部和尾部(9486)
- Android 上传图片到JavaWebserver(8780)
- Java 算法 JSON Bean相互转化及JSON生成实体类(6258)
- Android PullToRefresh 分析之中的一个、初识PullToRefresh(5206)
- Android PullToRefresh 全然解析(4943)
- Android PullToRefresh 分析之五、扩展刷新载入样式(4693)
- Android 打造底部导航控件(4628)
-
文章存档
-
评论排行
- Android 拍照、选择图片并裁剪(37)
- Android PullToRefresh 分析之四、扩展RecyclerView(16)
- Android RecyclerView加入头部和尾部(12)
- Android PullToRefresh 分析之五、扩展刷新载入样式(10)
- Android PullToRefresh 全然解析(9)
- Android UltimateRecyclerView优雅地使用RecyclerView(6)
- Android 关于屏幕的一些事儿(3)
- Android 上传图片到JavaWebserver(3)
- Android 手把手教你开发轮转大图控件(2)
- Android HTML5 audio autoplay无效问题(2)
-
最新评论
-
Android HTML5 audio autoplay无效问题
Vicent_9920回复:
webView.getSettings().setMediaPlaybackRequiresUser...
-
我刚才回复的好像不在了。尴尬,我用这段代码能够实现动态改动比例吗?mGestureCropImage...
-
@xuehuayous:这个看了。要改动的非常多。我想用简洁的方式实现,感觉你这个就非常好,我直接用这个...
-
xuehuayous回复:
@sinat_30822393:事实上就是动态去设置宽高比。建议你去看下https://github....
-
大哥,能不能动态改动图片比例,就是在剪切界面有非常多种比例选项,比如1:1,2:3 。3:4 这种。...
-
Android PullToRefresh 分析之四、扩展RecyclerView
qq_31302233回复:
加入切割线的时候没有偏移。不知道有没有碰到类似情况的
-
Android HTML5 audio autoplay无效问题
qq_35351040回复:
我仅仅能说66666666666
-
Android UltimateRecyclerView优雅地使用RecyclerView
imilyyan回复:
请问下在CoordinatorLayout的布局下,加入了toolbar和UltimateRecyc...
-
Android PullToRefresh 分析之五、扩展刷新载入样式
Jack_lyj回复:
新手请教:LoadingLayoutBase在哪能看到都做了什么啊。急急急,谢谢
-
Android PullToRefresh 分析之五、扩展刷新载入样式
Jack_lyj回复:
新手请教:LoadingLayoutBase在哪能看到都做了什么啊。谢谢
-
发表评论