02布局原理与XML原理分析
Posted 清风百草
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了02布局原理与XML原理分析相关的知识,希望对你有一定的参考价值。
(1)使用插件化的方案为App换肤
(2)不需要重启App就能够换肤
(3)市场上所有的APP都可以当成自己的皮肤包来用。
(4)无闪烁
(5)便于扩展与维护,入侵性很小。
(6)只需要在Application初始化一次即可使用
(7)喜欢什么样的皮肤包,就可以将它的apk包拿过来就可以了。
【02】布局原理与XML原理分析
文章目录
1.布局原理
(1)因为要实现换肤功能,因此需要了解布局的原理
(2)换肤需要找到UI上的控件,对UI的结构是需要很清楚的。
(3)需要了解布局的层次结构,View的绘制流程。
2.UI是怎么出来的
2.1ActivityThread
(1)performLaunchActivity
- Activity布局的入口是从performLaunchActivity开始的
- 会初始化Activity上下文
- 创建Activity
mInstrumentation.newActivity()
- Application的初始化
- activity getWindow()
- activity.attach Activity绑定的过程对mWindow进行初始化
2.2PhoneWindow
(1)Activity中包含了一个PhoneWindow
2.2.1setContentView实现
@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)
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
mLayoutInflater.inflate(layoutResID, mContentParent);
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed())
cb.onContentChanged();
mContentParentExplicitlySet = true;
2.2.2installDecor()
(1)就是new了一个DecorView出来
protected DecorView generateDecor(int featureId)
// System process doesn't have application context and in that case we need to directly use
// the context we have. Otherwise we want the application context, so we don't cling to the
// activity.
Context context;
if (mUseDecorContext)
Context applicationContext = getContext().getApplicationContext();
if (applicationContext == null)
context = getContext();
else
context = new DecorContext(applicationContext, this);
if (mTheme != -1)
context.setTheme(mTheme);
else
context = getContext();
return new DecorView(context, featureId, this, getAttributes());
2.2.3布局解析
mLayoutInflater.inflate(layoutResID, mContentParent);
2.2.4类之间的关系
(1)DecorView就是一个FrameLayout
public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks
(2)generateLayout()
protected ViewGroup generateLayout(DecorView decor)
// Inflate the window decor.
int layoutResource;
int features = getLocalFeatures();
// System.out.println("Features: 0x" + Integer.toHexString(features));
if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0)
if (mIsFloating)
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogTitleIconsDecorLayout, res, true);
layoutResource = res.resourceId;
else
layoutResource = 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 = 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(
R.attr.dialogCustomTitleDecorLayout, res, true);
layoutResource = res.resourceId;
else
layoutResource = 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(
R.attr.dialogTitleDecorLayout, res, true);
layoutResource = res.resourceId;
else if ((features & (1 << FEATURE_ACTION_BAR)) != 0)
layoutResource = a.getResourceId(
R.styleable.Window_windowActionBarFullscreenDecorLayout,
R.layout.screen_action_bar);
else
layoutResource = R.layout.screen_title;
// System.out.println("Title!");
else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0)
layoutResource = R.layout.screen_simple_overlay_action_mode;
else
// Embedded, so no decoration is needed.
layoutResource = R.layout.screen_simple;
// System.out.println("Simple!");
- 以上是返回窗口装饰相关的代码
- 找到相关的layout文件 screen_simple.xml,系统自带的
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:orientation="vertical">
<ViewStub android:id="@+id/action_mode_bar_stub"
android:inflatedId="@+id/action_mode_bar"
android:layout="@layout/action_mode_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="?attr/actionBarTheme" />
<FrameLayout
android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foregroundInsidePadding="false"
android:foregroundGravity="fill_horizontal|top"
android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>
- 加载以上xml文件,通过addView的形式贴到DecorView上面。
void onResourcesLoaded(LayoutInflater inflater, int layoutResource)
if (mBackdropFrameRenderer != null)
loadBackgroundDrawablesIfNeeded();
mBackdropFrameRenderer.onResourcesLoaded(
this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
getCurrentColor(mNavigationColorViewState));
mDecorCaptionView = createDecorCaptionView(inflater);
final View root = inflater.inflate(layoutResource, null);
if (mDecorCaptionView != null)
if (mDecorCaptionView.getParent() == null)
addView(mDecorCaptionView,
new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
mDecorCaptionView.addView(root,
new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
else
// Put it below the color views.
addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
mContentRoot = (ViewGroup) root;
initializeElevation();
(3)mLayoutInflater.inflate(layoutResID, mContentParent);
- mContentParent初始化是在
com.android.internal.policy.PhoneWindow#installDecor
mContentParent = generateLayout(mDecor);
(4)最终调用android.view.LayoutInflater#inflate(org.xmlpull.v1.XmlPullParser, android.view.ViewGroup, boolean)
- root就是FrameLayout,mContentParent,也就是布局screen_simple.xml中的id为content的控件.
(5)创建Activity的View,即根布局视图
android.view.LayoutInflater#inflate(org.xmlpull.v1.XmlPullParser, android.view.ViewGroup, boolean)
android.view.LayoutInflater#createViewFromTag(android.view.View, java.lang.String, android.content.Context, android.util.AttributeSet, boolean)
(6)关于inflate的第三个参数attachToRoot
- 如果是false父布局的LayoutParams参数填充进布局,就直接从布局中获取参数值。
- 如果是true就父布局的LayoutParams参数就不会填充进去,就需要通过代码的方式填充进去。
- 如果根布局产生的View不是root子布局的情况,就会进入到if条件中。
ViewGroup.LayoutParams params = null;
if (root != null)
if (DEBUG)
System.out.println("Creating params from root: " +
root);
// Create layout params that match root, if supplied
params = root.generateLayoutParams(attrs);
if (!attachToRoot)
// Set the layout params for temp if we are not
// attaching. (If we are, we use addView, below)
temp.setLayoutParams(params);
(7)布局如何创建的
android.view.LayoutInflater#createViewFromTag(android.view.View, java.lang.String, android.content.Context, android.util.AttributeSet, boolean)
- 创建
android.view.LayoutInflater#createView(android.content.Context, java.lang.String, java.lang.String, android.util.AttributeSet)
constructor = clazz.getConstructor(mConstructorSignature);
所有的View在通过LayoutInflater创建时,都是通过带两个参数的构造方法完成的。
即任何一个自定义控件带两个参数的构造方法是绝对不能省略的。
3.自定义LayoutInflater统计View的执行过程
(1)View的生成都是通过反射生成出来的。
(2)我们是否可以自己重写一个LayoutInflater将它的代码全部拷贝进去,就可以统计到所有的View的执行过程了。
(3)在Activity中调用setContentView的时候,都会跑到PhoneWindow的setContentView里面来.
(4)进行布局解析的过程都会执行createViewFromTag()
(5)最终执行createView()
(6)做换肤就需要了解这些点。
4.工厂创建View
(1)android.view.LayoutInflater.Factory#onCreateView
不知道父亲是谁
(2)android.view.LayoutInflater.Factory2#onCreateView
带父控件绑定
(3)android.view.LayoutInflater.FactoryMerger#onCreateView(java.lang.String, android.content.Context, android.util.AttributeSet)
(4)android.view.LayoutInflater.FactoryMerger#onCreateView(android.view.View, java.lang.String, android.content.Context, android.util.AttributeSet)
5.创建View的方案
(1)可以自己写一个LayoutInflater,将其创建的过程改掉
(2)自己写工厂,在工厂里面去抄源码的实现,去完成View创建。
(3)用自己写的工厂去干预到View的加载过程,在换肤的时候,所有的View的资源,都可以通过工厂提前设置进去,去修改控件的值。
6.换肤有两种方案
(1)重写LayoutInflater(这种方式有入侵性)
(2)自己实现Factory
7.代码阅读思维导图
(1)图片可以下载后放大看。
8.打赏鼓励
感谢您的细心阅读,您的鼓励是我写作的不竭动力!!!
8.1微信打赏
8.2支付宝打赏
以上是关于02布局原理与XML原理分析的主要内容,如果未能解决你的问题,请参考以下文章